From b84bdb5936d8a72970757a5b0dfe870e16cd26a2 Mon Sep 17 00:00:00 2001 From: Sergey Kudasov Date: Wed, 21 Feb 2024 16:52:00 +0100 Subject: [PATCH 01/24] build load package differently (#12117) * build load package differently * unpack back to the integration-tests dir * Handle new load go project for tests that use it --------- Co-authored-by: Tate --- .github/actions/build-test-image/action.yml | 2 +- .github/workflows/automation-benchmark-tests.yml | 2 +- .github/workflows/automation-load-tests.yml | 4 ++-- .github/workflows/on-demand-vrfv2-performance-test.yml | 2 +- .github/workflows/on-demand-vrfv2plus-performance-test.yml | 2 +- integration-tests/scripts/buildTests | 7 ++++++- 6 files changed, 12 insertions(+), 7 deletions(-) diff --git a/.github/actions/build-test-image/action.yml b/.github/actions/build-test-image/action.yml index 0c48c43b772..d2ebeaa39b9 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 reorg smoke soak benchmark load/automationv2_1 + default: chaos migration reorg smoke soak benchmark load 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/workflows/automation-benchmark-tests.yml b/.github/workflows/automation-benchmark-tests.yml index 70725511883..3b6bf6384fa 100644 --- a/.github/workflows/automation-benchmark-tests.yml +++ b/.github/workflows/automation-benchmark-tests.yml @@ -64,7 +64,7 @@ jobs: QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ACCOUNT_NUMBER: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} - suites: benchmark load/automationv2_1 chaos reorg + suites: benchmark load chaos reorg - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 env: diff --git a/.github/workflows/automation-load-tests.yml b/.github/workflows/automation-load-tests.yml index ad68e470a4d..7c2b737da88 100644 --- a/.github/workflows/automation-load-tests.yml +++ b/.github/workflows/automation-load-tests.yml @@ -80,7 +80,7 @@ jobs: QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ACCOUNT_NUMBER: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} - suites: benchmark load/automationv2_1 chaos reorg + suites: benchmark load chaos reorg - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 env: @@ -94,7 +94,7 @@ jobs: PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} with: - test_command_to_run: cd integration-tests && go test -timeout 1h -v -run TestLogTrigger ./load/automationv2_1 -count=1 + test_command_to_run: cd integration-tests/load && go test -timeout 1h -v -run TestLogTrigger ./automationv2_1 -count=1 test_download_vendor_packages_command: make gomod cl_repo: ${{ env.CHAINLINK_IMAGE }} cl_image_tag: ${{ env.CHAINLINK_VERSION }} diff --git a/.github/workflows/on-demand-vrfv2-performance-test.yml b/.github/workflows/on-demand-vrfv2-performance-test.yml index 5887d8ec9a2..1e82d8ab02c 100644 --- a/.github/workflows/on-demand-vrfv2-performance-test.yml +++ b/.github/workflows/on-demand-vrfv2-performance-test.yml @@ -69,7 +69,7 @@ jobs: - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 with: - test_command_to_run: cd ./integration-tests && go test -v -count=1 -timeout 24h -run TestVRFV2Performance/vrfv2_performance_test ./load/vrfv2 + test_command_to_run: cd ./integration-tests/load && go test -v -count=1 -timeout 24h -run TestVRFV2Performance/vrfv2_performance_test ./vrfv2 test_download_vendor_packages_command: cd ./integration-tests && go mod download cl_repo: ${{ env.CHAINLINK_IMAGE }} cl_image_tag: ${{ env.CHAINLINK_VERSION }} diff --git a/.github/workflows/on-demand-vrfv2plus-performance-test.yml b/.github/workflows/on-demand-vrfv2plus-performance-test.yml index 36a75704895..2c2e11b6cb3 100644 --- a/.github/workflows/on-demand-vrfv2plus-performance-test.yml +++ b/.github/workflows/on-demand-vrfv2plus-performance-test.yml @@ -70,7 +70,7 @@ jobs: - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 with: - test_command_to_run: cd ./integration-tests && go test -v -count=1 -timeout 24h -run TestVRFV2PlusPerformance/vrfv2plus_performance_test ./load/vrfv2plus + test_command_to_run: cd ./integration-tests/load && go test -v -count=1 -timeout 24h -run TestVRFV2PlusPerformance/vrfv2plus_performance_test ./vrfv2plus test_download_vendor_packages_command: cd ./integration-tests && go mod download cl_repo: ${{ env.CHAINLINK_IMAGE }} cl_image_tag: ${{ env.CHAINLINK_VERSION }} diff --git a/integration-tests/scripts/buildTests b/integration-tests/scripts/buildTests index 749bb545110..27378bdf43f 100755 --- a/integration-tests/scripts/buildTests +++ b/integration-tests/scripts/buildTests @@ -21,6 +21,11 @@ OIFS=$IFS IFS=' ' for x in $tosplit do - go test -c -tags embed ./"${x}" + if [ "$x" = "load" ]; then + echo "Changing directory and executing go test -c ./... for 'load' package" + cd "load" && go test -c -tags embed -o .. ./... + else + go test -c -tags embed ./"${x}" + fi done IFS=$OIFS From 216efed063abb3d779f6c941f2e139c539c77a0e Mon Sep 17 00:00:00 2001 From: Bolek <1416262+bolekk@users.noreply.github.com> Date: Wed, 21 Feb 2024 08:47:39 -0800 Subject: [PATCH 02/24] [KS-55] Engine and Registry improvements (#12115) 1. Move registry operations into a separate goroutine launched at Start() and supporting re-tries, to avoid race conditions on node init. 2. Move most of the hardcoded workflow into Engine constructor, for clarity. 3. Add missing call to consensus.RegisterToWorkflow(). 4. Add logger to Registry. --- core/capabilities/registry.go | 13 +- core/capabilities/registry_test.go | 7 +- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 +- core/services/chainlink/application.go | 2 +- core/services/job/spawner_test.go | 2 +- core/services/workflows/engine.go | 214 +++++++++++++++---------- core/services/workflows/engine_test.go | 28 ++-- go.mod | 2 +- go.sum | 4 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +- 12 files changed, 174 insertions(+), 110 deletions(-) diff --git a/core/capabilities/registry.go b/core/capabilities/registry.go index 02c08ae8dc3..4865116196e 100644 --- a/core/capabilities/registry.go +++ b/core/capabilities/registry.go @@ -6,13 +6,15 @@ import ( "sync" "github.com/smartcontractkit/chainlink-common/pkg/capabilities" + "github.com/smartcontractkit/chainlink/v2/core/logger" ) // Registry is a struct for the registry of capabilities. // Registry is safe for concurrent use. type Registry struct { - m map[string]capabilities.BaseCapability - mu sync.RWMutex + m map[string]capabilities.BaseCapability + mu sync.RWMutex + lggr logger.Logger } // Get gets a capability from the registry. @@ -20,6 +22,7 @@ func (r *Registry) Get(_ context.Context, id string) (capabilities.BaseCapabilit r.mu.RLock() defer r.mu.RUnlock() + r.lggr.Debugw("get capability", "id", id) c, ok := r.m[id] if !ok { return nil, fmt.Errorf("capability not found with id %s", id) @@ -142,13 +145,15 @@ func (r *Registry) Add(ctx context.Context, c capabilities.BaseCapability) error } r.m[id] = c + r.lggr.Infow("capability added", "id", id, "type", info.CapabilityType, "description", info.Description, "version", info.Version) return nil } // NewRegistry returns a new Registry. -func NewRegistry() *Registry { +func NewRegistry(lggr logger.Logger) *Registry { return &Registry{ - m: map[string]capabilities.BaseCapability{}, + m: map[string]capabilities.BaseCapability{}, + lggr: lggr.Named("CapabilityRegistry"), } } diff --git a/core/capabilities/registry_test.go b/core/capabilities/registry_test.go index 8af91d133a2..3f8ca397495 100644 --- a/core/capabilities/registry_test.go +++ b/core/capabilities/registry_test.go @@ -12,6 +12,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/capabilities/triggers" coreCapabilities "github.com/smartcontractkit/chainlink/v2/core/capabilities" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/logger" ) type mockCapability struct { @@ -33,7 +34,7 @@ func (m *mockCapability) UnregisterFromWorkflow(ctx context.Context, request cap func TestRegistry(t *testing.T) { ctx := testutils.Context(t) - r := coreCapabilities.NewRegistry() + r := coreCapabilities.NewRegistry(logger.TestLogger(t)) id := "capability-1" ci, err := capabilities.NewCapabilityInfo( @@ -61,7 +62,7 @@ func TestRegistry(t *testing.T) { func TestRegistry_NoDuplicateIDs(t *testing.T) { ctx := testutils.Context(t) - r := coreCapabilities.NewRegistry() + r := coreCapabilities.NewRegistry(logger.TestLogger(t)) id := "capability-1" ci, err := capabilities.NewCapabilityInfo( @@ -172,7 +173,7 @@ func TestRegistry_ChecksExecutionAPIByType(t *testing.T) { } ctx := testutils.Context(t) - reg := coreCapabilities.NewRegistry() + reg := coreCapabilities.NewRegistry(logger.TestLogger(t)) for _, tc := range tcs { t.Run(tc.name, func(t *testing.T) { id, err := tc.newCapability(ctx, reg) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index b4d6860719d..9cf5111cf52 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -20,7 +20,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.20240219152510-85226a0fbdc1 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c 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-20240215150045-fe2ba71b2f0a diff --git a/core/scripts/go.sum b/core/scripts/go.sum index f52f3cac869..b6432cbe3fe 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1173,8 +1173,8 @@ github.com/smartcontractkit/chain-selectors v1.0.10 h1:t9kJeE6B6G+hKD0GYR4kGJSCq github.com/smartcontractkit/chain-selectors v1.0.10/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= 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.20240219152510-85226a0fbdc1 h1:MNYkjakmoKxg7L1nmfAVeFOdONaLT7E62URBpmcTh84= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240219152510-85226a0fbdc1/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c h1:ielGD+tVCB+irZ+nDt5VDTYJauJI88tirkLLaHWLaTs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 h1:I326nw5GwHQHsLKHwtu5Sb9EBLylC8CfUd7BFAS0jtg= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8/go.mod h1:a65NtrK4xZb01mf0dDNghPkN2wXgcqFQ55ADthVBgMc= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240214203158-47dae5de1336 h1:j00D0/EqE9HRu+63v7KwUOe4ZxLc4AN5SOJFiinkkH0= diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index de2fe789396..eebb70534c0 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -185,7 +185,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) { keyStore := opts.KeyStore restrictedHTTPClient := opts.RestrictedHTTPClient unrestrictedHTTPClient := opts.UnrestrictedHTTPClient - registry := capabilities.NewRegistry() + registry := capabilities.NewRegistry(globalLogger) // LOOPs can be created as options, in the case of LOOP relayers, or // as OCR2 job implementations, in the case of Median today. diff --git a/core/services/job/spawner_test.go b/core/services/job/spawner_test.go index 2139b4a02e2..b23be49458e 100644 --- a/core/services/job/spawner_test.go +++ b/core/services/job/spawner_test.go @@ -305,7 +305,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, capabilities.NewRegistry()) + keyStore.OCR2(), keyStore.DKGSign(), keyStore.DKGEncrypt(), ethKeyStore, testRelayGetter, mailMon, capabilities.NewRegistry(lggr)) 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/workflows/engine.go b/core/services/workflows/engine.go index 1f34b58105d..3260702e66b 100644 --- a/core/services/workflows/engine.go +++ b/core/services/workflows/engine.go @@ -3,6 +3,9 @@ package workflows import ( "context" "fmt" + "time" + + "github.com/shopspring/decimal" "github.com/smartcontractkit/chainlink-common/pkg/capabilities" "github.com/smartcontractkit/chainlink-common/pkg/services" @@ -12,49 +15,91 @@ import ( ) const ( - mockedWorkflowID = "ef7c8168-f4d1-422f-a4b2-8ce0a1075f0a" - mockedTriggerID = "bd727a82-5cac-4071-be62-0152dd9adb0f" + mockedWorkflowID = "aaaaaaaa-f4d1-422f-a4b2-8ce0a1075f0a" + mockedExecutionID = "bbbbbbbb-f4d1-422f-a4b2-8ce0a1075f0a" + mockedTriggerID = "cccccccc-5cac-4071-be62-0152dd9adb0f" ) type Engine struct { services.StateMachine - logger logger.Logger - registry types.CapabilitiesRegistry - trigger capabilities.TriggerCapability - consensus capabilities.ConsensusCapability - target capabilities.TargetCapability - callbackCh chan capabilities.CapabilityResponse - cancel func() + logger logger.Logger + registry types.CapabilitiesRegistry + triggerType string + triggerConfig *values.Map + trigger capabilities.TriggerCapability + consensusType string + consensusConfig *values.Map + consensus capabilities.ConsensusCapability + targetType string + targetConfig *values.Map + target capabilities.TargetCapability + callbackCh chan capabilities.CapabilityResponse + cancel func() } func (e *Engine) Start(ctx context.Context) error { return e.StartOnce("Engine", func() error { - err := e.registerTrigger(ctx) - if err != nil { - return err - } - // create a new context, since the one passed in via Start is short-lived. ctx, cancel := context.WithCancel(context.Background()) e.cancel = cancel - go e.loop(ctx) + go e.init(ctx) + go e.triggerHandlerLoop(ctx) return nil }) } -func (e *Engine) registerTrigger(ctx context.Context) error { - triggerConf, err := values.NewMap( - map[string]any{ - "feedlist": []any{ - // ETHUSD, LINKUSD, USDBTC - 123, 456, 789, - }, +func (e *Engine) init(ctx context.Context) { + retrySec := 5 + ticker := time.NewTicker(time.Duration(retrySec) * time.Second) + defer ticker.Stop() + var err error +LOOP: + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + e.trigger, err = e.registry.GetTrigger(ctx, e.triggerType) + if err != nil { + e.logger.Errorf("failed to get trigger capability: %s, retrying in %d seconds", err, retrySec) + break + } + e.consensus, err = e.registry.GetConsensus(ctx, e.consensusType) + if err != nil { + e.logger.Errorf("failed to get consensus capability: %s, retrying in %d seconds", err, retrySec) + break + } + e.target, err = e.registry.GetTarget(ctx, e.targetType) + if err != nil { + e.logger.Errorf("failed to get target capability: %s, retrying in %d seconds", err, retrySec) + break + } + break LOOP + } + } + + // we have all needed capabilities, now we can register for trigger events + err = e.registerTrigger(ctx) + if err != nil { + e.logger.Errorf("failed to register trigger: %s", err) + } + + // also register for consensus + reg := capabilities.RegisterToWorkflowRequest{ + Metadata: capabilities.RegistrationMetadata{ + WorkflowID: mockedWorkflowID, }, - ) + Config: e.consensusConfig, + } + err = e.consensus.RegisterToWorkflow(ctx, reg) if err != nil { - return err + e.logger.Errorf("failed to register consensus: %s", err) } + e.logger.Info("engine initialized") +} + +func (e *Engine) registerTrigger(ctx context.Context) error { triggerInputs, err := values.NewMap( map[string]any{ "triggerId": mockedTriggerID, @@ -68,7 +113,7 @@ func (e *Engine) registerTrigger(ctx context.Context) error { Metadata: capabilities.RequestMetadata{ WorkflowID: mockedWorkflowID, }, - Config: triggerConf, + Config: e.triggerConfig, Inputs: triggerInputs, } err = e.trigger.RegisterTrigger(ctx, e.callbackCh, triggerRegRequest) @@ -78,7 +123,7 @@ func (e *Engine) registerTrigger(ctx context.Context) error { return nil } -func (e *Engine) loop(ctx context.Context) { +func (e *Engine) triggerHandlerLoop(ctx context.Context) { for { select { case <-ctx.Done(): @@ -92,8 +137,9 @@ func (e *Engine) loop(ctx context.Context) { } } -func (e *Engine) handleExecution(ctx context.Context, resp capabilities.CapabilityResponse) error { - results, err := e.handleConsensus(ctx, resp) +func (e *Engine) handleExecution(ctx context.Context, event capabilities.CapabilityResponse) error { + e.logger.Debugw("executing on a trigger event", "event", event) + results, err := e.handleConsensus(ctx, event) if err != nil { return err } @@ -103,27 +149,14 @@ func (e *Engine) handleExecution(ctx context.Context, resp capabilities.Capabili } func (e *Engine) handleTarget(ctx context.Context, resp *values.List) (*values.List, error) { - report, err := resp.Unwrap() - if err != nil { - return nil, err - } + inputs := map[string]values.Value{ "report": resp, } - config, err := values.NewMap(map[string]any{ - "address": "0xaabbcc", - "method": "updateFeedValues(report bytes, role uint8)", - "params": []any{ - report, 1, - }, - }) - if err != nil { - return nil, err - } tr := capabilities.CapabilityRequest{ Inputs: &values.Map{Underlying: inputs}, - Config: config, + Config: e.targetConfig, Metadata: capabilities.RequestMetadata{ WorkflowID: mockedWorkflowID, }, @@ -132,39 +165,17 @@ func (e *Engine) handleTarget(ctx context.Context, resp *values.List) (*values.L } func (e *Engine) handleConsensus(ctx context.Context, resp capabilities.CapabilityResponse) (*values.List, error) { + e.logger.Debugw("running consensus", "resp", resp) inputs := map[string]values.Value{ "observations": resp.Value, } - config, err := values.NewMap(map[string]any{ - "aggregation_method": "data_feeds_2_0", - "aggregation_config": map[string]any{ - // ETHUSD - "123": map[string]any{ - "deviation": "0.005", - "heartbeat": "24h", - }, - // LINKUSD - "456": map[string]any{ - "deviation": "0.001", - "heartbeat": "24h", - }, - // BTCUSD - "789": map[string]any{ - "deviation": "0.002", - "heartbeat": "6h", - }, - }, - "encoder": "EVM", - }) - if err != nil { - return nil, nil - } cr := capabilities.CapabilityRequest{ Metadata: capabilities.RequestMetadata{ - WorkflowID: mockedWorkflowID, + WorkflowID: mockedWorkflowID, + WorkflowExecutionID: mockedExecutionID, }, Inputs: &values.Map{Underlying: inputs}, - Config: config, + Config: e.consensusConfig, } return capabilities.ExecuteSync(ctx, e.consensus, cr) } @@ -191,26 +202,65 @@ func (e *Engine) Close() error { }) } -func NewEngine(lggr logger.Logger, registry types.CapabilitiesRegistry) (*Engine, error) { - ctx := context.Background() - trigger, err := registry.GetTrigger(ctx, "on_mercury_report") +func NewEngine(lggr logger.Logger, registry types.CapabilitiesRegistry) (engine *Engine, err error) { + engine = &Engine{ + logger: lggr.Named("WorkflowEngine"), + registry: registry, + callbackCh: make(chan capabilities.CapabilityResponse), + } + + // Trigger + engine.triggerType = "on_mercury_report" + engine.triggerConfig, err = values.NewMap( + map[string]any{ + "feedlist": []any{ + 123, 456, 789, // ETHUSD, LINKUSD, USDBTC + }, + }, + ) if err != nil { return nil, err } - consensus, err := registry.GetConsensus(ctx, "off-chain-reporting") + + // Consensus + engine.consensusType = "offchain_reporting" + engine.consensusConfig, err = values.NewMap(map[string]any{ + "aggregation_method": "data_feeds_2_0", + "aggregation_config": map[string]any{ + // ETHUSD + "0x1111111111111111111100000000000000000000000000000000000000000000": map[string]any{ + "deviation": decimal.NewFromFloat(0.003), + "heartbeat": 24, + }, + // LINKUSD + "0x2222222222222222222200000000000000000000000000000000000000000000": map[string]any{ + "deviation": decimal.NewFromFloat(0.001), + "heartbeat": 24, + }, + // BTCUSD + "0x3333333333333333333300000000000000000000000000000000000000000000": map[string]any{ + "deviation": decimal.NewFromFloat(0.002), + "heartbeat": 6, + }, + }, + "encoder": "EVM", + "encoder_config": map[string]any{ + "abi": "mercury_reports bytes[]", + }, + }) if err != nil { return nil, err } - target, err := registry.GetTarget(ctx, "write_polygon_mainnet") + + // Target + engine.targetType = "write_polygon-testnet-mumbai" + engine.targetConfig, err = values.NewMap(map[string]any{ + "address": "0xaabbcc", + "params": []any{"$(report)"}, + "abi": "receive(report bytes)", + }) if err != nil { return nil, err } - return &Engine{ - logger: lggr, - registry: registry, - trigger: trigger, - consensus: consensus, - target: target, - callbackCh: make(chan capabilities.CapabilityResponse), - }, nil + return } diff --git a/core/services/workflows/engine_test.go b/core/services/workflows/engine_test.go index 603f5eee3b1..339792fd06d 100644 --- a/core/services/workflows/engine_test.go +++ b/core/services/workflows/engine_test.go @@ -41,15 +41,23 @@ func (m *mockCapability) Execute(ctx context.Context, ch chan<- capabilities.Cap return nil } +func (m *mockCapability) RegisterToWorkflow(ctx context.Context, request capabilities.RegisterToWorkflowRequest) error { + return nil +} + +func (m *mockCapability) UnregisterFromWorkflow(ctx context.Context, request capabilities.UnregisterFromWorkflowRequest) error { + return nil +} + type mockTriggerCapability struct { capabilities.CapabilityInfo - ch chan<- capabilities.CapabilityResponse + triggerEvent capabilities.CapabilityResponse } var _ capabilities.TriggerCapability = (*mockTriggerCapability)(nil) func (m *mockTriggerCapability) RegisterTrigger(ctx context.Context, ch chan<- capabilities.CapabilityResponse, req capabilities.CapabilityRequest) error { - m.ch = ch + ch <- m.triggerEvent return nil } @@ -59,7 +67,7 @@ func (m *mockTriggerCapability) UnregisterTrigger(ctx context.Context, req capab func TestEngineWithHardcodedWorkflow(t *testing.T) { ctx := context.Background() - reg := coreCap.NewRegistry() + reg := coreCap.NewRegistry(logger.TestLogger(t)) trigger := &mockTriggerCapability{ CapabilityInfo: capabilities.MustNewCapabilityInfo( @@ -73,7 +81,7 @@ func TestEngineWithHardcodedWorkflow(t *testing.T) { consensus := newMockCapability( capabilities.MustNewCapabilityInfo( - "off-chain-reporting", + "offchain_reporting", capabilities.CapabilityTypeConsensus, "an ocr3 consensus capability", "v3.0.0", @@ -88,7 +96,7 @@ func TestEngineWithHardcodedWorkflow(t *testing.T) { target := newMockCapability( capabilities.MustNewCapabilityInfo( - "write_polygon_mainnet", + "write_polygon-testnet-mumbai", capabilities.CapabilityTypeTarget, "a write capability targeting polygon mainnet", "v1.0.0", @@ -107,10 +115,6 @@ func TestEngineWithHardcodedWorkflow(t *testing.T) { eng, err := NewEngine(lggr, reg) require.NoError(t, err) - err = eng.Start(ctx) - require.NoError(t, err) - defer eng.Close() - resp, err := values.NewMap(map[string]any{ "123": decimal.NewFromFloat(1.00), "456": decimal.NewFromFloat(1.25), @@ -120,6 +124,10 @@ func TestEngineWithHardcodedWorkflow(t *testing.T) { cr := capabilities.CapabilityResponse{ Value: resp, } - trigger.ch <- cr + trigger.triggerEvent = cr + + err = eng.Start(ctx) + require.NoError(t, err) + defer eng.Close() assert.Equal(t, cr, <-target.response) } diff --git a/go.mod b/go.mod index be354048dcd..c0ef2b819de 100644 --- a/go.mod +++ b/go.mod @@ -69,7 +69,7 @@ require ( github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chain-selectors v1.0.10 github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20240219152510-85226a0fbdc1 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240214203158-47dae5de1336 github.com/smartcontractkit/chainlink-feeds v0.0.0-20240119021347-3c541a78cdb8 diff --git a/go.sum b/go.sum index 1f2da19fe3b..88ea058b06e 100644 --- a/go.sum +++ b/go.sum @@ -1168,8 +1168,8 @@ github.com/smartcontractkit/chain-selectors v1.0.10 h1:t9kJeE6B6G+hKD0GYR4kGJSCq github.com/smartcontractkit/chain-selectors v1.0.10/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= 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.20240219152510-85226a0fbdc1 h1:MNYkjakmoKxg7L1nmfAVeFOdONaLT7E62URBpmcTh84= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240219152510-85226a0fbdc1/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c h1:ielGD+tVCB+irZ+nDt5VDTYJauJI88tirkLLaHWLaTs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 h1:I326nw5GwHQHsLKHwtu5Sb9EBLylC8CfUd7BFAS0jtg= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8/go.mod h1:a65NtrK4xZb01mf0dDNghPkN2wXgcqFQ55ADthVBgMc= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240214203158-47dae5de1336 h1:j00D0/EqE9HRu+63v7KwUOe4ZxLc4AN5SOJFiinkkH0= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 38954c5b178..ae2ac344c9b 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -23,7 +23,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.20240219152510-85226a0fbdc1 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c github.com/smartcontractkit/chainlink-testing-framework v1.23.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 77c335604cd..fe81cc3ac68 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1507,8 +1507,8 @@ github.com/smartcontractkit/chain-selectors v1.0.10 h1:t9kJeE6B6G+hKD0GYR4kGJSCq github.com/smartcontractkit/chain-selectors v1.0.10/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= 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.20240219152510-85226a0fbdc1 h1:MNYkjakmoKxg7L1nmfAVeFOdONaLT7E62URBpmcTh84= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240219152510-85226a0fbdc1/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c h1:ielGD+tVCB+irZ+nDt5VDTYJauJI88tirkLLaHWLaTs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 h1:I326nw5GwHQHsLKHwtu5Sb9EBLylC8CfUd7BFAS0jtg= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8/go.mod h1:a65NtrK4xZb01mf0dDNghPkN2wXgcqFQ55ADthVBgMc= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240214203158-47dae5de1336 h1:j00D0/EqE9HRu+63v7KwUOe4ZxLc4AN5SOJFiinkkH0= From 423529c1ac7fe2cf46c8423ebf6d76d9563c1dd6 Mon Sep 17 00:00:00 2001 From: Tate Date: Wed, 21 Feb 2024 12:22:13 -0700 Subject: [PATCH 03/24] [TT-704] Add go mod tidy to build/lint section and remove from others (#12120) * Add go mod tidy to build/lint section and remove from others * bump chainlink-github-actions versions * Remove tidy check from test matrix * fix go import issue in lint --- .../actions/build-chainlink-image/action.yml | 4 +- .github/actions/build-test-image/action.yml | 10 ++-- .github/actions/version-file-bump/action.yml | 2 +- .../workflows/automation-benchmark-tests.yml | 2 +- .github/workflows/automation-load-tests.yml | 2 +- .../workflows/automation-nightly-tests.yml | 2 +- .../workflows/automation-ondemand-tests.yml | 6 +-- .github/workflows/build-publish-pr.yml | 2 +- .github/workflows/ci-core.yml | 2 +- .../workflows/client-compatibility-tests.yml | 4 +- .github/workflows/integration-chaos-tests.yml | 6 +-- .github/workflows/integration-tests.yml | 49 ++++++++++++------- .github/workflows/live-testnet-tests.yml | 46 ++++++++--------- .github/workflows/on-demand-ocr-soak-test.yml | 2 +- .../on-demand-vrfv2-eth2-clients-test.yml | 2 +- .../on-demand-vrfv2-performance-test.yml | 2 +- .../on-demand-vrfv2plus-eth2-clients-test.yml | 2 +- .../on-demand-vrfv2plus-performance-test.yml | 2 +- integration-tests/load/go.mod | 16 +++--- integration-tests/load/go.sum | 4 +- .../load/zcluster/cluster_entrypoint_test.go | 6 ++- 21 files changed, 93 insertions(+), 80 deletions(-) diff --git a/.github/actions/build-chainlink-image/action.yml b/.github/actions/build-chainlink-image/action.yml index 644c109ab24..7a2f8a04b71 100644 --- a/.github/actions/build-chainlink-image/action.yml +++ b/.github/actions/build-chainlink-image/action.yml @@ -25,7 +25,7 @@ runs: steps: - name: Check if image exists id: check-image - uses: smartcontractkit/chainlink-github-actions/docker/image-exists@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/docker/image-exists@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: repository: chainlink tag: ${{ inputs.git_commit_sha }}${{ inputs.tag_suffix }} @@ -33,7 +33,7 @@ runs: AWS_ROLE_TO_ASSUME: ${{ inputs.AWS_ROLE_TO_ASSUME }} - name: Build Image if: steps.check-image.outputs.exists == 'false' - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/build-image@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/build-image@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: cl_repo: smartcontractkit/chainlink cl_ref: ${{ inputs.git_commit_sha }} diff --git a/.github/actions/build-test-image/action.yml b/.github/actions/build-test-image/action.yml index d2ebeaa39b9..7fe002a82b2 100644 --- a/.github/actions/build-test-image/action.yml +++ b/.github/actions/build-test-image/action.yml @@ -34,7 +34,7 @@ runs: # Base Test Image Logic - name: Get CTF Version id: version - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/mod-version@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/mod-version@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: go-project-path: ./integration-tests module-name: github.com/smartcontractkit/chainlink-testing-framework @@ -71,7 +71,7 @@ runs: - name: Check if test base image exists if: steps.version.outputs.is_semantic == 'false' id: check-base-image - uses: smartcontractkit/chainlink-github-actions/docker/image-exists@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/docker/image-exists@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: repository: ${{ inputs.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ inputs.QA_AWS_REGION }}.amazonaws.com/test-base-image tag: ${{ steps.long_sha.outputs.long_sha }} @@ -79,7 +79,7 @@ runs: AWS_ROLE_TO_ASSUME: ${{ inputs.QA_AWS_ROLE_TO_ASSUME }} - name: Build Base Image if: steps.version.outputs.is_semantic == 'false' && steps.check-base-image.outputs.exists == 'false' - uses: smartcontractkit/chainlink-github-actions/docker/build-push@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/docker/build-push@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 env: BASE_IMAGE_NAME: ${{ inputs.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ inputs.QA_AWS_REGION }}.amazonaws.com/test-base-image:${{ steps.long_sha.outputs.long_sha }} with: @@ -92,7 +92,7 @@ runs: # Test Runner Logic - name: Check if image exists id: check-image - uses: smartcontractkit/chainlink-github-actions/docker/image-exists@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/docker/image-exists@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: repository: ${{ inputs.repository }} tag: ${{ inputs.tag }} @@ -100,7 +100,7 @@ runs: AWS_ROLE_TO_ASSUME: ${{ inputs.QA_AWS_ROLE_TO_ASSUME }} - name: Build and Publish Test Runner if: steps.check-image.outputs.exists == 'false' - uses: smartcontractkit/chainlink-github-actions/docker/build-push@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/docker/build-push@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: tags: | ${{ inputs.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ inputs.QA_AWS_REGION }}.amazonaws.com/${{ inputs.repository }}:${{ inputs.tag }} diff --git a/.github/actions/version-file-bump/action.yml b/.github/actions/version-file-bump/action.yml index 241b0f5a78c..2c9b95a6898 100644 --- a/.github/actions/version-file-bump/action.yml +++ b/.github/actions/version-file-bump/action.yml @@ -31,7 +31,7 @@ runs: current_version=$(head -n1 ./VERSION) echo "current_version=${current_version}" | tee -a "$GITHUB_OUTPUT" - name: Compare semantic versions - uses: smartcontractkit/chainlink-github-actions/semver-compare@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/semver-compare@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 id: compare with: version1: ${{ steps.get-current-version.outputs.current_version }} diff --git a/.github/workflows/automation-benchmark-tests.yml b/.github/workflows/automation-benchmark-tests.yml index 3b6bf6384fa..60d995c75cd 100644 --- a/.github/workflows/automation-benchmark-tests.yml +++ b/.github/workflows/automation-benchmark-tests.yml @@ -66,7 +66,7 @@ jobs: QA_AWS_ACCOUNT_NUMBER: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} suites: benchmark load chaos reorg - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 env: DETACH_RUNNER: true TEST_SUITE: benchmark diff --git a/.github/workflows/automation-load-tests.yml b/.github/workflows/automation-load-tests.yml index 7c2b737da88..0b22a6e2c82 100644 --- a/.github/workflows/automation-load-tests.yml +++ b/.github/workflows/automation-load-tests.yml @@ -82,7 +82,7 @@ jobs: QA_AWS_ACCOUNT_NUMBER: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} suites: benchmark load chaos reorg - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 env: RR_CPU: 4000m RR_MEM: 4Gi diff --git a/.github/workflows/automation-nightly-tests.yml b/.github/workflows/automation-nightly-tests.yml index 5fb8a8f87d1..6df574e58d0 100644 --- a/.github/workflows/automation-nightly-tests.yml +++ b/.github/workflows/automation-nightly-tests.yml @@ -82,7 +82,7 @@ jobs: upgradeImage: ${{ env.CHAINLINK_IMAGE }} upgradeVersion: ${{ github.sha }} - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 env: TEST_SUITE: ${{ matrix.tests.suite }} with: diff --git a/.github/workflows/automation-ondemand-tests.yml b/.github/workflows/automation-ondemand-tests.yml index a996399b93d..ed528d2490c 100644 --- a/.github/workflows/automation-ondemand-tests.yml +++ b/.github/workflows/automation-ondemand-tests.yml @@ -60,7 +60,7 @@ jobs: - name: Check if image exists if: inputs.chainlinkImage == '' id: check-image - uses: smartcontractkit/chainlink-github-actions/docker/image-exists@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/docker/image-exists@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: repository: chainlink tag: ${{ github.sha }}${{ matrix.image.tag-suffix }} @@ -68,7 +68,7 @@ jobs: AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - name: Build Image if: steps.check-image.outputs.exists == 'false' && inputs.chainlinkImage == '' - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/build-image@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/build-image@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: cl_repo: smartcontractkit/chainlink cl_ref: ${{ github.sha }} @@ -225,7 +225,7 @@ jobs: 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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 env: TEST_SUITE: ${{ matrix.tests.suite }} with: diff --git a/.github/workflows/build-publish-pr.yml b/.github/workflows/build-publish-pr.yml index a093f86d3de..b7b06e149e2 100644 --- a/.github/workflows/build-publish-pr.yml +++ b/.github/workflows/build-publish-pr.yml @@ -32,7 +32,7 @@ jobs: - name: Check if image exists id: check-image - uses: smartcontractkit/chainlink-github-actions/docker/image-exists@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/docker/image-exists@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: repository: ${{ env.ECR_IMAGE_NAME}} tag: sha-${{ env.GIT_SHORT_SHA }} diff --git a/.github/workflows/ci-core.yml b/.github/workflows/ci-core.yml index 78d655d4cd8..01938679054 100644 --- a/.github/workflows/ci-core.yml +++ b/.github/workflows/ci-core.yml @@ -98,7 +98,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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/go/go-test-results-parsing@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: results-file: ./output.txt output-file: ./output-short.txt diff --git a/.github/workflows/client-compatibility-tests.yml b/.github/workflows/client-compatibility-tests.yml index 4d895cbfc5b..3dbcbe52cdc 100644 --- a/.github/workflows/client-compatibility-tests.yml +++ b/.github/workflows/client-compatibility-tests.yml @@ -69,7 +69,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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/build-tests@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_download_vendor_packages_command: cd ./integration-tests && go mod download token: ${{ secrets.GITHUB_TOKEN }} @@ -224,7 +224,7 @@ jobs: 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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_command_to_run: ./tests -test.timeout ${{ matrix.timeout }} -test.run ${{ matrix.test }} binary_name: tests diff --git a/.github/workflows/integration-chaos-tests.yml b/.github/workflows/integration-chaos-tests.yml index 8f9e9b030ec..364b2ac12bb 100644 --- a/.github/workflows/integration-chaos-tests.yml +++ b/.github/workflows/integration-chaos-tests.yml @@ -29,7 +29,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Check if image exists id: check-image - uses: smartcontractkit/chainlink-github-actions/docker/image-exists@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/docker/image-exists@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: repository: chainlink tag: ${{ github.sha }} @@ -37,7 +37,7 @@ jobs: AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - name: Build Image if: steps.check-image.outputs.exists == 'false' - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/build-image@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/build-image@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: cl_repo: smartcontractkit/chainlink cl_ref: ${{ github.sha }} @@ -129,7 +129,7 @@ jobs: 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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_command_to_run: cd integration-tests && go test -timeout 1h -count=1 -json -test.parallel 11 ./chaos 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: cd ./integration-tests && go mod download diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index f8beac21a8c..45401b3cd63 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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/mod-version@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: go-project-path: ./integration-tests module-name: github.com/smartcontractkit/chainlink-testing-framework @@ -85,19 +85,28 @@ jobs: build-lint-integration-tests: name: Build and Lint integration-tests runs-on: ubuntu20.04-16cores-64GB + strategy: + matrix: + project: + - name: integration-tests + path: ./integration-tests + cache-id: e2e + - name: load + path: ./integration-tests/load + cache-id: load steps: - name: Checkout the repo uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Setup Go - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/setup-go@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/setup-go@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: - test_download_vendor_packages_command: cd ./integration-tests && go mod download - go_mod_path: ./integration-tests/go.mod - cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} + test_download_vendor_packages_command: cd ${{ matrix.project.path }} && go mod download + go_mod_path: ${{ matrix.project.path }}/go.mod + cache_key_id: core-${{ matrix.project.cache-id }}-${{ env.MOD_CACHE_VERSION }} cache_restore_only: "true" - name: Build Go run: | - cd ./integration-tests + cd ${{ matrix.project.path }} go build ./... go test -run=^# ./... - name: Lint Go @@ -110,7 +119,7 @@ jobs: # only-new-issues is only applicable to PRs, otherwise it is always set to false only-new-issues: false # disabled for PRs due to unreliability args: --out-format colored-line-number,checkstyle:golangci-lint-report.xml - working-directory: ./integration-tests + working-directory: ${{ matrix.project.path }} build-chainlink: environment: integration @@ -302,7 +311,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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 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 @@ -318,9 +327,10 @@ jobs: QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: "" + should_tidy: "false" - name: Print failed test summary if: always() - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 eth-smoke-tests-matrix-log-poller: if: ${{ !(contains(join(github.event.pull_request.labels.*.name, ' '), 'skip-smoke-tests') || github.event_name == 'workflow_dispatch') }} @@ -388,7 +398,7 @@ jobs: ## 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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 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 @@ -404,6 +414,7 @@ jobs: QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: "" + should_tidy: "false" eth-smoke-tests-matrix: if: ${{ !contains(join(github.event.pull_request.labels.*.name, ' '), 'skip-smoke-tests') }} @@ -569,7 +580,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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 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 @@ -586,10 +597,11 @@ jobs: QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: "" + should_tidy: "false" # 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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/setup-run-tests-environment@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_download_vendor_packages_command: cd ./integration-tests && go mod download go_mod_path: ./integration-tests/go.mod @@ -598,6 +610,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 }} + should_tidy: "false" - name: Show Otel-Collector Logs if: steps.check-label.outputs.trace == 'true' && matrix.product.name == 'ocr2' && matrix.product.tag_suffix == '-plugins' run: | @@ -614,7 +627,7 @@ jobs: 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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_directory: ./integration-tests/smoke/ @@ -685,7 +698,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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/setup-go@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_download_vendor_packages_command: | cd ./integration-tests @@ -741,7 +754,7 @@ jobs: upgradeImage: ${{ env.UPGRADE_IMAGE }} upgradeVersion: ${{ env.UPGRADE_VERSION }} - name: Run Migration Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 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 @@ -854,7 +867,7 @@ jobs: steps: - name: Check if image exists id: check-image - uses: smartcontractkit/chainlink-github-actions/docker/image-exists@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/docker/image-exists@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: repository: chainlink-solana-tests tag: ${{ needs.get_solana_sha.outputs.sha }} @@ -995,7 +1008,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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/setup-run-tests-environment@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: go_mod_path: ./integration-tests/go.mod cache_restore_only: true @@ -1039,7 +1052,7 @@ jobs: echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV - name: Run Tests if: needs.changes.outputs.src == 'true' || github.event_name == 'workflow_dispatch' - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 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 1d273788eee..9cb980aca6c 100644 --- a/.github/workflows/live-testnet-tests.yml +++ b/.github/workflows/live-testnet-tests.yml @@ -91,7 +91,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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/build-tests@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_download_vendor_packages_command: cd ./integration-tests && go mod download token: ${{ secrets.GITHUB_TOKEN }} @@ -248,7 +248,7 @@ jobs: with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -266,7 +266,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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_directory: "./" @@ -320,7 +320,7 @@ jobs: with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -338,7 +338,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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_directory: "./" @@ -392,7 +392,7 @@ jobs: with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -410,7 +410,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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_directory: "./" @@ -464,7 +464,7 @@ jobs: with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -482,7 +482,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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_directory: "./" @@ -532,7 +532,7 @@ jobs: with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -550,7 +550,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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_directory: "./" @@ -604,7 +604,7 @@ jobs: with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -622,7 +622,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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_directory: "./" @@ -676,7 +676,7 @@ jobs: with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -694,7 +694,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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_directory: "./" @@ -748,7 +748,7 @@ jobs: with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -766,7 +766,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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_directory: "./" @@ -816,7 +816,7 @@ jobs: with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -834,7 +834,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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_directory: "./" @@ -885,7 +885,7 @@ jobs: with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -903,7 +903,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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_directory: "./" @@ -953,7 +953,7 @@ jobs: with: name: tests - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests-binary@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_command_to_run: ./tests -test.timeout 30m -test.count=1 -test.parallel=1 -test.run ${{ matrix.test }} binary_name: tests @@ -971,6 +971,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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/show-test-summary@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_directory: "./" diff --git a/.github/workflows/on-demand-ocr-soak-test.yml b/.github/workflows/on-demand-ocr-soak-test.yml index 81f38ba0293..b44a3fb2d92 100644 --- a/.github/workflows/on-demand-ocr-soak-test.yml +++ b/.github/workflows/on-demand-ocr-soak-test.yml @@ -72,7 +72,7 @@ jobs: QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ACCOUNT_NUMBER: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 env: DETACH_RUNNER: true TEST_SUITE: soak diff --git a/.github/workflows/on-demand-vrfv2-eth2-clients-test.yml b/.github/workflows/on-demand-vrfv2-eth2-clients-test.yml index 865f59b5179..24db1e6ffcf 100644 --- a/.github/workflows/on-demand-vrfv2-eth2-clients-test.yml +++ b/.github/workflows/on-demand-vrfv2-eth2-clients-test.yml @@ -46,7 +46,7 @@ jobs: 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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 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 diff --git a/.github/workflows/on-demand-vrfv2-performance-test.yml b/.github/workflows/on-demand-vrfv2-performance-test.yml index 1e82d8ab02c..1e8ab9e9b71 100644 --- a/.github/workflows/on-demand-vrfv2-performance-test.yml +++ b/.github/workflows/on-demand-vrfv2-performance-test.yml @@ -67,7 +67,7 @@ jobs: 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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_command_to_run: cd ./integration-tests/load && go test -v -count=1 -timeout 24h -run TestVRFV2Performance/vrfv2_performance_test ./vrfv2 test_download_vendor_packages_command: cd ./integration-tests && go mod download diff --git a/.github/workflows/on-demand-vrfv2plus-eth2-clients-test.yml b/.github/workflows/on-demand-vrfv2plus-eth2-clients-test.yml index 228f0cdc5ff..01777fba646 100644 --- a/.github/workflows/on-demand-vrfv2plus-eth2-clients-test.yml +++ b/.github/workflows/on-demand-vrfv2plus-eth2-clients-test.yml @@ -46,7 +46,7 @@ jobs: 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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 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 diff --git a/.github/workflows/on-demand-vrfv2plus-performance-test.yml b/.github/workflows/on-demand-vrfv2plus-performance-test.yml index 2c2e11b6cb3..23484f963e3 100644 --- a/.github/workflows/on-demand-vrfv2plus-performance-test.yml +++ b/.github/workflows/on-demand-vrfv2plus-performance-test.yml @@ -68,7 +68,7 @@ jobs: 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@92e0f299a87522c2a37bfc4686c4d8a96dc9d28b # v2.3.5 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 with: test_command_to_run: cd ./integration-tests/load && go test -v -count=1 -timeout 24h -run TestVRFV2PlusPerformance/vrfv2plus_performance_test ./vrfv2plus test_download_vendor_packages_command: cd ./integration-tests && go mod download diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index 15753c05866..174ea743c23 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -15,7 +15,7 @@ require ( github.com/rs/zerolog v1.30.0 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.20240219152510-85226a0fbdc1 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c github.com/smartcontractkit/chainlink-testing-framework v1.23.6 github.com/smartcontractkit/chainlink/integration-tests v0.0.0-20240214231432-4ad5eb95178c github.com/smartcontractkit/chainlink/v2 v2.9.0-beta0.0.20240216210048-da02459ddad8 @@ -29,14 +29,6 @@ require ( // avoids ambigious imports of indirect dependencies exclude github.com/hashicorp/consul v1.2.1 -replace ( - 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 -) - require ( contrib.go.opencensus.io/exporter/stackdriver v0.13.5 // indirect cosmossdk.io/api v0.3.1 // indirect @@ -496,4 +488,10 @@ replace ( // 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 + + 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/load/go.sum b/integration-tests/load/go.sum index 1aacb313ce5..1aaac86bd00 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1490,8 +1490,8 @@ github.com/smartcontractkit/chain-selectors v1.0.10 h1:t9kJeE6B6G+hKD0GYR4kGJSCq github.com/smartcontractkit/chain-selectors v1.0.10/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= 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.20240219152510-85226a0fbdc1 h1:MNYkjakmoKxg7L1nmfAVeFOdONaLT7E62URBpmcTh84= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20240219152510-85226a0fbdc1/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c h1:ielGD+tVCB+irZ+nDt5VDTYJauJI88tirkLLaHWLaTs= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 h1:I326nw5GwHQHsLKHwtu5Sb9EBLylC8CfUd7BFAS0jtg= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8/go.mod h1:a65NtrK4xZb01mf0dDNghPkN2wXgcqFQ55ADthVBgMc= github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240214203158-47dae5de1336 h1:j00D0/EqE9HRu+63v7KwUOe4ZxLc4AN5SOJFiinkkH0= diff --git a/integration-tests/load/zcluster/cluster_entrypoint_test.go b/integration-tests/load/zcluster/cluster_entrypoint_test.go index 03133071b36..e5e8496af8b 100644 --- a/integration-tests/load/zcluster/cluster_entrypoint_test.go +++ b/integration-tests/load/zcluster/cluster_entrypoint_test.go @@ -1,10 +1,12 @@ package zcluster import ( - tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" + "testing" + "github.com/smartcontractkit/wasp" "github.com/stretchr/testify/require" - "testing" + + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) func TestClusterEntrypoint(t *testing.T) { From 14c416c608e32d3bf95e636883afd62fbf795246 Mon Sep 17 00:00:00 2001 From: Anirudh Warrier <12178754+anirudhwarrier@users.noreply.github.com> Date: Wed, 21 Feb 2024 23:55:04 +0400 Subject: [PATCH 04/24] Chore/auto fix load test build (#12125) * fix automation actions - build test image * load go mod tidy --- .github/workflows/automation-benchmark-tests.yml | 2 +- .github/workflows/automation-load-tests.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/automation-benchmark-tests.yml b/.github/workflows/automation-benchmark-tests.yml index 60d995c75cd..c81e46d9930 100644 --- a/.github/workflows/automation-benchmark-tests.yml +++ b/.github/workflows/automation-benchmark-tests.yml @@ -64,7 +64,7 @@ jobs: QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ACCOUNT_NUMBER: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} - suites: benchmark load chaos reorg + suites: benchmark chaos reorg load - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 env: diff --git a/.github/workflows/automation-load-tests.yml b/.github/workflows/automation-load-tests.yml index 0b22a6e2c82..bbdd59f9f5c 100644 --- a/.github/workflows/automation-load-tests.yml +++ b/.github/workflows/automation-load-tests.yml @@ -80,7 +80,7 @@ jobs: QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ACCOUNT_NUMBER: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} - suites: benchmark load chaos reorg + suites: benchmark chaos reorg load - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@c67a09566412d153ff7640d99f96b43aa03abc04 # v2.3.6 env: From 2a53c4fe3208def2477ff49c6f6f26f407fec453 Mon Sep 17 00:00:00 2001 From: FelixFan1992 Date: Wed, 21 Feb 2024 15:40:53 -0500 Subject: [PATCH 05/24] provide integration test support for registry 2.2 (#12071) * provide integration test support for registry 2.2 * fixed some code smells * fix linter issues * linter * linter again * update * versions * add v2.2 * wait for module deployment * fix linter issues and add comments * fix sonarqube * update * linter * fix * tab vs spaces * fix * exclude a file in sonarqube duplication tests * update * update wrappers * update * fix * fix * update for reorg protection bool * update read only address and clean up tests * update parallel tests * small fix --------- Co-authored-by: Tate --- .../native_solc_compile_all_automation | 7 +- .../arbitrum_module/arbitrum_module.go | 313 ++++++++++++++ .../chain_module_base/chain_module_base.go | 313 ++++++++++++++ .../i_chain_module/i_chain_module.go | 294 +++++++++++++ .../optimism_module/optimism_module.go | 313 ++++++++++++++ .../generated/scroll_module/scroll_module.go | 313 ++++++++++++++ ...rapper-dependency-versions-do-not-edit.txt | 5 + core/gethwrappers/go_generate.go | 5 + .../actions/automation_ocr_helpers.go | 23 +- .../actions/automation_ocr_helpers_local.go | 22 +- .../actions/automationv2/actions.go | 136 +++--- integration-tests/benchmark/keeper_test.go | 6 + .../chaos/automation_chaos_test.go | 3 +- .../contracts/contract_deployer.go | 196 ++++++++- .../ethereum/KeeperRegistryVersions.go | 1 + .../contracts/ethereum_keeper_contracts.go | 215 +++++++++- .../reorg/automation_reorg_test.go | 17 +- integration-tests/smoke/automation_test.go | 406 +++++++++--------- .../smoke/automation_test.go_test_list.json | 46 +- .../testsetups/keeper_benchmark.go | 22 +- .../universal/log_poller/helpers.go | 3 +- sonar-project.properties | 3 +- 22 files changed, 2324 insertions(+), 338 deletions(-) create mode 100644 core/gethwrappers/generated/arbitrum_module/arbitrum_module.go create mode 100644 core/gethwrappers/generated/chain_module_base/chain_module_base.go create mode 100644 core/gethwrappers/generated/i_chain_module/i_chain_module.go create mode 100644 core/gethwrappers/generated/optimism_module/optimism_module.go create mode 100644 core/gethwrappers/generated/scroll_module/scroll_module.go diff --git a/contracts/scripts/native_solc_compile_all_automation b/contracts/scripts/native_solc_compile_all_automation index 6194d3cb057..c508079813e 100755 --- a/contracts/scripts/native_solc_compile_all_automation +++ b/contracts/scripts/native_solc_compile_all_automation @@ -91,4 +91,9 @@ 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 \ No newline at end of file +compileContract automation/dev/interfaces/v2_2/IAutomationRegistryMaster.sol +compileContract automation/dev/chains/ArbitrumModule.sol +compileContract automation/dev/chains/ChainModuleBase.sol +compileContract automation/dev/chains/OptimismModule.sol +compileContract automation/dev/chains/ScrollModule.sol +compileContract automation/dev/interfaces/v2_2/IChainModule.sol \ No newline at end of file diff --git a/core/gethwrappers/generated/arbitrum_module/arbitrum_module.go b/core/gethwrappers/generated/arbitrum_module/arbitrum_module.go new file mode 100644 index 00000000000..abcfd3d4bc9 --- /dev/null +++ b/core/gethwrappers/generated/arbitrum_module/arbitrum_module.go @@ -0,0 +1,313 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package arbitrum_module + +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 ArbitrumModuleMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"n\",\"type\":\"uint256\"}],\"name\":\"blockHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"blockNumber\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentL1Fee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"chainModuleFixedOverhead\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"chainModulePerByteOverhead\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"dataSize\",\"type\":\"uint256\"}],\"name\":\"getMaxL1Fee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b50610426806100206000396000f3fe608060405234801561001057600080fd5b50600436106100675760003560e01c806357e871e71161005057806357e871e71461009a57806385df51fd146100a2578063de9ee35e146100b557600080fd5b8063125441401461006c57806318b8f61314610092575b600080fd5b61007f61007a36600461033e565b6100cb565b6040519081526020015b60405180910390f35b61007f610163565b61007f6101da565b61007f6100b036600461033e565b610228565b60408051614e2081526014602082015201610089565b600080606c73ffffffffffffffffffffffffffffffffffffffff166341b247a86040518163ffffffff1660e01b815260040160c060405180830381865afa15801561011a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061013e9190610357565b50505050915050828161015191906103d0565b61015c9060106103d0565b9392505050565b6000606c73ffffffffffffffffffffffffffffffffffffffff1663c6f7de0e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156101b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101d591906103ed565b905090565b6000606473ffffffffffffffffffffffffffffffffffffffff1663a3b1b31d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156101b1573d6000803e3d6000fd5b600080606473ffffffffffffffffffffffffffffffffffffffff1663a3b1b31d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610277573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061029b91906103ed565b905080831015806102b657506101006102b48483610406565b115b156102c45750600092915050565b6040517f2b407a8200000000000000000000000000000000000000000000000000000000815260048101849052606490632b407a8290602401602060405180830381865afa15801561031a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061015c91906103ed565b60006020828403121561035057600080fd5b5035919050565b60008060008060008060c0878903121561037057600080fd5b865195506020870151945060408701519350606087015192506080870151915060a087015190509295509295509295565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820281158282048414176103e7576103e76103a1565b92915050565b6000602082840312156103ff57600080fd5b5051919050565b818103818111156103e7576103e76103a156fea164736f6c6343000813000a", +} + +var ArbitrumModuleABI = ArbitrumModuleMetaData.ABI + +var ArbitrumModuleBin = ArbitrumModuleMetaData.Bin + +func DeployArbitrumModule(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *ArbitrumModule, error) { + parsed, err := ArbitrumModuleMetaData.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(ArbitrumModuleBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &ArbitrumModule{address: address, abi: *parsed, ArbitrumModuleCaller: ArbitrumModuleCaller{contract: contract}, ArbitrumModuleTransactor: ArbitrumModuleTransactor{contract: contract}, ArbitrumModuleFilterer: ArbitrumModuleFilterer{contract: contract}}, nil +} + +type ArbitrumModule struct { + address common.Address + abi abi.ABI + ArbitrumModuleCaller + ArbitrumModuleTransactor + ArbitrumModuleFilterer +} + +type ArbitrumModuleCaller struct { + contract *bind.BoundContract +} + +type ArbitrumModuleTransactor struct { + contract *bind.BoundContract +} + +type ArbitrumModuleFilterer struct { + contract *bind.BoundContract +} + +type ArbitrumModuleSession struct { + Contract *ArbitrumModule + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type ArbitrumModuleCallerSession struct { + Contract *ArbitrumModuleCaller + CallOpts bind.CallOpts +} + +type ArbitrumModuleTransactorSession struct { + Contract *ArbitrumModuleTransactor + TransactOpts bind.TransactOpts +} + +type ArbitrumModuleRaw struct { + Contract *ArbitrumModule +} + +type ArbitrumModuleCallerRaw struct { + Contract *ArbitrumModuleCaller +} + +type ArbitrumModuleTransactorRaw struct { + Contract *ArbitrumModuleTransactor +} + +func NewArbitrumModule(address common.Address, backend bind.ContractBackend) (*ArbitrumModule, error) { + abi, err := abi.JSON(strings.NewReader(ArbitrumModuleABI)) + if err != nil { + return nil, err + } + contract, err := bindArbitrumModule(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &ArbitrumModule{address: address, abi: abi, ArbitrumModuleCaller: ArbitrumModuleCaller{contract: contract}, ArbitrumModuleTransactor: ArbitrumModuleTransactor{contract: contract}, ArbitrumModuleFilterer: ArbitrumModuleFilterer{contract: contract}}, nil +} + +func NewArbitrumModuleCaller(address common.Address, caller bind.ContractCaller) (*ArbitrumModuleCaller, error) { + contract, err := bindArbitrumModule(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &ArbitrumModuleCaller{contract: contract}, nil +} + +func NewArbitrumModuleTransactor(address common.Address, transactor bind.ContractTransactor) (*ArbitrumModuleTransactor, error) { + contract, err := bindArbitrumModule(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &ArbitrumModuleTransactor{contract: contract}, nil +} + +func NewArbitrumModuleFilterer(address common.Address, filterer bind.ContractFilterer) (*ArbitrumModuleFilterer, error) { + contract, err := bindArbitrumModule(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &ArbitrumModuleFilterer{contract: contract}, nil +} + +func bindArbitrumModule(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := ArbitrumModuleMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_ArbitrumModule *ArbitrumModuleRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _ArbitrumModule.Contract.ArbitrumModuleCaller.contract.Call(opts, result, method, params...) +} + +func (_ArbitrumModule *ArbitrumModuleRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ArbitrumModule.Contract.ArbitrumModuleTransactor.contract.Transfer(opts) +} + +func (_ArbitrumModule *ArbitrumModuleRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ArbitrumModule.Contract.ArbitrumModuleTransactor.contract.Transact(opts, method, params...) +} + +func (_ArbitrumModule *ArbitrumModuleCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _ArbitrumModule.Contract.contract.Call(opts, result, method, params...) +} + +func (_ArbitrumModule *ArbitrumModuleTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ArbitrumModule.Contract.contract.Transfer(opts) +} + +func (_ArbitrumModule *ArbitrumModuleTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ArbitrumModule.Contract.contract.Transact(opts, method, params...) +} + +func (_ArbitrumModule *ArbitrumModuleCaller) BlockHash(opts *bind.CallOpts, n *big.Int) ([32]byte, error) { + var out []interface{} + err := _ArbitrumModule.contract.Call(opts, &out, "blockHash", n) + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +func (_ArbitrumModule *ArbitrumModuleSession) BlockHash(n *big.Int) ([32]byte, error) { + return _ArbitrumModule.Contract.BlockHash(&_ArbitrumModule.CallOpts, n) +} + +func (_ArbitrumModule *ArbitrumModuleCallerSession) BlockHash(n *big.Int) ([32]byte, error) { + return _ArbitrumModule.Contract.BlockHash(&_ArbitrumModule.CallOpts, n) +} + +func (_ArbitrumModule *ArbitrumModuleCaller) BlockNumber(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _ArbitrumModule.contract.Call(opts, &out, "blockNumber") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_ArbitrumModule *ArbitrumModuleSession) BlockNumber() (*big.Int, error) { + return _ArbitrumModule.Contract.BlockNumber(&_ArbitrumModule.CallOpts) +} + +func (_ArbitrumModule *ArbitrumModuleCallerSession) BlockNumber() (*big.Int, error) { + return _ArbitrumModule.Contract.BlockNumber(&_ArbitrumModule.CallOpts) +} + +func (_ArbitrumModule *ArbitrumModuleCaller) GetCurrentL1Fee(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _ArbitrumModule.contract.Call(opts, &out, "getCurrentL1Fee") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_ArbitrumModule *ArbitrumModuleSession) GetCurrentL1Fee() (*big.Int, error) { + return _ArbitrumModule.Contract.GetCurrentL1Fee(&_ArbitrumModule.CallOpts) +} + +func (_ArbitrumModule *ArbitrumModuleCallerSession) GetCurrentL1Fee() (*big.Int, error) { + return _ArbitrumModule.Contract.GetCurrentL1Fee(&_ArbitrumModule.CallOpts) +} + +func (_ArbitrumModule *ArbitrumModuleCaller) GetGasOverhead(opts *bind.CallOpts) (GetGasOverhead, + + error) { + var out []interface{} + err := _ArbitrumModule.contract.Call(opts, &out, "getGasOverhead") + + outstruct := new(GetGasOverhead) + if err != nil { + return *outstruct, err + } + + outstruct.ChainModuleFixedOverhead = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + outstruct.ChainModulePerByteOverhead = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int) + + return *outstruct, err + +} + +func (_ArbitrumModule *ArbitrumModuleSession) GetGasOverhead() (GetGasOverhead, + + error) { + return _ArbitrumModule.Contract.GetGasOverhead(&_ArbitrumModule.CallOpts) +} + +func (_ArbitrumModule *ArbitrumModuleCallerSession) GetGasOverhead() (GetGasOverhead, + + error) { + return _ArbitrumModule.Contract.GetGasOverhead(&_ArbitrumModule.CallOpts) +} + +func (_ArbitrumModule *ArbitrumModuleCaller) GetMaxL1Fee(opts *bind.CallOpts, dataSize *big.Int) (*big.Int, error) { + var out []interface{} + err := _ArbitrumModule.contract.Call(opts, &out, "getMaxL1Fee", dataSize) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_ArbitrumModule *ArbitrumModuleSession) GetMaxL1Fee(dataSize *big.Int) (*big.Int, error) { + return _ArbitrumModule.Contract.GetMaxL1Fee(&_ArbitrumModule.CallOpts, dataSize) +} + +func (_ArbitrumModule *ArbitrumModuleCallerSession) GetMaxL1Fee(dataSize *big.Int) (*big.Int, error) { + return _ArbitrumModule.Contract.GetMaxL1Fee(&_ArbitrumModule.CallOpts, dataSize) +} + +type GetGasOverhead struct { + ChainModuleFixedOverhead *big.Int + ChainModulePerByteOverhead *big.Int +} + +func (_ArbitrumModule *ArbitrumModule) Address() common.Address { + return _ArbitrumModule.address +} + +type ArbitrumModuleInterface interface { + BlockHash(opts *bind.CallOpts, n *big.Int) ([32]byte, error) + + BlockNumber(opts *bind.CallOpts) (*big.Int, error) + + GetCurrentL1Fee(opts *bind.CallOpts) (*big.Int, error) + + GetGasOverhead(opts *bind.CallOpts) (GetGasOverhead, + + error) + + GetMaxL1Fee(opts *bind.CallOpts, dataSize *big.Int) (*big.Int, error) + + Address() common.Address +} diff --git a/core/gethwrappers/generated/chain_module_base/chain_module_base.go b/core/gethwrappers/generated/chain_module_base/chain_module_base.go new file mode 100644 index 00000000000..4c830566ef5 --- /dev/null +++ b/core/gethwrappers/generated/chain_module_base/chain_module_base.go @@ -0,0 +1,313 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package chain_module_base + +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 ChainModuleBaseMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"n\",\"type\":\"uint256\"}],\"name\":\"blockHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"blockNumber\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentL1Fee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"chainModuleFixedOverhead\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"chainModulePerByteOverhead\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"getMaxL1Fee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b5061015a806100206000396000f3fe608060405234801561001057600080fd5b50600436106100675760003560e01c806357e871e71161005057806357e871e71461009a57806385df51fd146100a0578063de9ee35e146100b357600080fd5b8063125441401461006c57806318b8f61314610093575b600080fd5b61008061007a3660046100f4565b50600090565b6040519081526020015b60405180910390f35b6000610080565b43610080565b6100806100ae3660046100f4565b6100c7565b60408051600080825260208201520161008a565b600043821015806100e257506101006100e0834361010d565b115b156100ef57506000919050565b504090565b60006020828403121561010657600080fd5b5035919050565b81810381811115610147577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b9291505056fea164736f6c6343000813000a", +} + +var ChainModuleBaseABI = ChainModuleBaseMetaData.ABI + +var ChainModuleBaseBin = ChainModuleBaseMetaData.Bin + +func DeployChainModuleBase(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *ChainModuleBase, error) { + parsed, err := ChainModuleBaseMetaData.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(ChainModuleBaseBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &ChainModuleBase{address: address, abi: *parsed, ChainModuleBaseCaller: ChainModuleBaseCaller{contract: contract}, ChainModuleBaseTransactor: ChainModuleBaseTransactor{contract: contract}, ChainModuleBaseFilterer: ChainModuleBaseFilterer{contract: contract}}, nil +} + +type ChainModuleBase struct { + address common.Address + abi abi.ABI + ChainModuleBaseCaller + ChainModuleBaseTransactor + ChainModuleBaseFilterer +} + +type ChainModuleBaseCaller struct { + contract *bind.BoundContract +} + +type ChainModuleBaseTransactor struct { + contract *bind.BoundContract +} + +type ChainModuleBaseFilterer struct { + contract *bind.BoundContract +} + +type ChainModuleBaseSession struct { + Contract *ChainModuleBase + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type ChainModuleBaseCallerSession struct { + Contract *ChainModuleBaseCaller + CallOpts bind.CallOpts +} + +type ChainModuleBaseTransactorSession struct { + Contract *ChainModuleBaseTransactor + TransactOpts bind.TransactOpts +} + +type ChainModuleBaseRaw struct { + Contract *ChainModuleBase +} + +type ChainModuleBaseCallerRaw struct { + Contract *ChainModuleBaseCaller +} + +type ChainModuleBaseTransactorRaw struct { + Contract *ChainModuleBaseTransactor +} + +func NewChainModuleBase(address common.Address, backend bind.ContractBackend) (*ChainModuleBase, error) { + abi, err := abi.JSON(strings.NewReader(ChainModuleBaseABI)) + if err != nil { + return nil, err + } + contract, err := bindChainModuleBase(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &ChainModuleBase{address: address, abi: abi, ChainModuleBaseCaller: ChainModuleBaseCaller{contract: contract}, ChainModuleBaseTransactor: ChainModuleBaseTransactor{contract: contract}, ChainModuleBaseFilterer: ChainModuleBaseFilterer{contract: contract}}, nil +} + +func NewChainModuleBaseCaller(address common.Address, caller bind.ContractCaller) (*ChainModuleBaseCaller, error) { + contract, err := bindChainModuleBase(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &ChainModuleBaseCaller{contract: contract}, nil +} + +func NewChainModuleBaseTransactor(address common.Address, transactor bind.ContractTransactor) (*ChainModuleBaseTransactor, error) { + contract, err := bindChainModuleBase(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &ChainModuleBaseTransactor{contract: contract}, nil +} + +func NewChainModuleBaseFilterer(address common.Address, filterer bind.ContractFilterer) (*ChainModuleBaseFilterer, error) { + contract, err := bindChainModuleBase(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &ChainModuleBaseFilterer{contract: contract}, nil +} + +func bindChainModuleBase(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := ChainModuleBaseMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_ChainModuleBase *ChainModuleBaseRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _ChainModuleBase.Contract.ChainModuleBaseCaller.contract.Call(opts, result, method, params...) +} + +func (_ChainModuleBase *ChainModuleBaseRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ChainModuleBase.Contract.ChainModuleBaseTransactor.contract.Transfer(opts) +} + +func (_ChainModuleBase *ChainModuleBaseRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ChainModuleBase.Contract.ChainModuleBaseTransactor.contract.Transact(opts, method, params...) +} + +func (_ChainModuleBase *ChainModuleBaseCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _ChainModuleBase.Contract.contract.Call(opts, result, method, params...) +} + +func (_ChainModuleBase *ChainModuleBaseTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ChainModuleBase.Contract.contract.Transfer(opts) +} + +func (_ChainModuleBase *ChainModuleBaseTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ChainModuleBase.Contract.contract.Transact(opts, method, params...) +} + +func (_ChainModuleBase *ChainModuleBaseCaller) BlockHash(opts *bind.CallOpts, n *big.Int) ([32]byte, error) { + var out []interface{} + err := _ChainModuleBase.contract.Call(opts, &out, "blockHash", n) + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +func (_ChainModuleBase *ChainModuleBaseSession) BlockHash(n *big.Int) ([32]byte, error) { + return _ChainModuleBase.Contract.BlockHash(&_ChainModuleBase.CallOpts, n) +} + +func (_ChainModuleBase *ChainModuleBaseCallerSession) BlockHash(n *big.Int) ([32]byte, error) { + return _ChainModuleBase.Contract.BlockHash(&_ChainModuleBase.CallOpts, n) +} + +func (_ChainModuleBase *ChainModuleBaseCaller) BlockNumber(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _ChainModuleBase.contract.Call(opts, &out, "blockNumber") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_ChainModuleBase *ChainModuleBaseSession) BlockNumber() (*big.Int, error) { + return _ChainModuleBase.Contract.BlockNumber(&_ChainModuleBase.CallOpts) +} + +func (_ChainModuleBase *ChainModuleBaseCallerSession) BlockNumber() (*big.Int, error) { + return _ChainModuleBase.Contract.BlockNumber(&_ChainModuleBase.CallOpts) +} + +func (_ChainModuleBase *ChainModuleBaseCaller) GetCurrentL1Fee(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _ChainModuleBase.contract.Call(opts, &out, "getCurrentL1Fee") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_ChainModuleBase *ChainModuleBaseSession) GetCurrentL1Fee() (*big.Int, error) { + return _ChainModuleBase.Contract.GetCurrentL1Fee(&_ChainModuleBase.CallOpts) +} + +func (_ChainModuleBase *ChainModuleBaseCallerSession) GetCurrentL1Fee() (*big.Int, error) { + return _ChainModuleBase.Contract.GetCurrentL1Fee(&_ChainModuleBase.CallOpts) +} + +func (_ChainModuleBase *ChainModuleBaseCaller) GetGasOverhead(opts *bind.CallOpts) (GetGasOverhead, + + error) { + var out []interface{} + err := _ChainModuleBase.contract.Call(opts, &out, "getGasOverhead") + + outstruct := new(GetGasOverhead) + if err != nil { + return *outstruct, err + } + + outstruct.ChainModuleFixedOverhead = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + outstruct.ChainModulePerByteOverhead = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int) + + return *outstruct, err + +} + +func (_ChainModuleBase *ChainModuleBaseSession) GetGasOverhead() (GetGasOverhead, + + error) { + return _ChainModuleBase.Contract.GetGasOverhead(&_ChainModuleBase.CallOpts) +} + +func (_ChainModuleBase *ChainModuleBaseCallerSession) GetGasOverhead() (GetGasOverhead, + + error) { + return _ChainModuleBase.Contract.GetGasOverhead(&_ChainModuleBase.CallOpts) +} + +func (_ChainModuleBase *ChainModuleBaseCaller) GetMaxL1Fee(opts *bind.CallOpts, arg0 *big.Int) (*big.Int, error) { + var out []interface{} + err := _ChainModuleBase.contract.Call(opts, &out, "getMaxL1Fee", arg0) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_ChainModuleBase *ChainModuleBaseSession) GetMaxL1Fee(arg0 *big.Int) (*big.Int, error) { + return _ChainModuleBase.Contract.GetMaxL1Fee(&_ChainModuleBase.CallOpts, arg0) +} + +func (_ChainModuleBase *ChainModuleBaseCallerSession) GetMaxL1Fee(arg0 *big.Int) (*big.Int, error) { + return _ChainModuleBase.Contract.GetMaxL1Fee(&_ChainModuleBase.CallOpts, arg0) +} + +type GetGasOverhead struct { + ChainModuleFixedOverhead *big.Int + ChainModulePerByteOverhead *big.Int +} + +func (_ChainModuleBase *ChainModuleBase) Address() common.Address { + return _ChainModuleBase.address +} + +type ChainModuleBaseInterface interface { + BlockHash(opts *bind.CallOpts, n *big.Int) ([32]byte, error) + + BlockNumber(opts *bind.CallOpts) (*big.Int, error) + + GetCurrentL1Fee(opts *bind.CallOpts) (*big.Int, error) + + GetGasOverhead(opts *bind.CallOpts) (GetGasOverhead, + + error) + + GetMaxL1Fee(opts *bind.CallOpts, arg0 *big.Int) (*big.Int, error) + + Address() common.Address +} diff --git a/core/gethwrappers/generated/i_chain_module/i_chain_module.go b/core/gethwrappers/generated/i_chain_module/i_chain_module.go new file mode 100644 index 00000000000..23cec8fb9c8 --- /dev/null +++ b/core/gethwrappers/generated/i_chain_module/i_chain_module.go @@ -0,0 +1,294 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package i_chain_module + +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 IChainModuleMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"blockHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"blockNumber\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentL1Fee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"chainModuleFixedOverhead\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"chainModulePerByteOverhead\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"dataSize\",\"type\":\"uint256\"}],\"name\":\"getMaxL1Fee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", +} + +var IChainModuleABI = IChainModuleMetaData.ABI + +type IChainModule struct { + address common.Address + abi abi.ABI + IChainModuleCaller + IChainModuleTransactor + IChainModuleFilterer +} + +type IChainModuleCaller struct { + contract *bind.BoundContract +} + +type IChainModuleTransactor struct { + contract *bind.BoundContract +} + +type IChainModuleFilterer struct { + contract *bind.BoundContract +} + +type IChainModuleSession struct { + Contract *IChainModule + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type IChainModuleCallerSession struct { + Contract *IChainModuleCaller + CallOpts bind.CallOpts +} + +type IChainModuleTransactorSession struct { + Contract *IChainModuleTransactor + TransactOpts bind.TransactOpts +} + +type IChainModuleRaw struct { + Contract *IChainModule +} + +type IChainModuleCallerRaw struct { + Contract *IChainModuleCaller +} + +type IChainModuleTransactorRaw struct { + Contract *IChainModuleTransactor +} + +func NewIChainModule(address common.Address, backend bind.ContractBackend) (*IChainModule, error) { + abi, err := abi.JSON(strings.NewReader(IChainModuleABI)) + if err != nil { + return nil, err + } + contract, err := bindIChainModule(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &IChainModule{address: address, abi: abi, IChainModuleCaller: IChainModuleCaller{contract: contract}, IChainModuleTransactor: IChainModuleTransactor{contract: contract}, IChainModuleFilterer: IChainModuleFilterer{contract: contract}}, nil +} + +func NewIChainModuleCaller(address common.Address, caller bind.ContractCaller) (*IChainModuleCaller, error) { + contract, err := bindIChainModule(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &IChainModuleCaller{contract: contract}, nil +} + +func NewIChainModuleTransactor(address common.Address, transactor bind.ContractTransactor) (*IChainModuleTransactor, error) { + contract, err := bindIChainModule(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &IChainModuleTransactor{contract: contract}, nil +} + +func NewIChainModuleFilterer(address common.Address, filterer bind.ContractFilterer) (*IChainModuleFilterer, error) { + contract, err := bindIChainModule(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &IChainModuleFilterer{contract: contract}, nil +} + +func bindIChainModule(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := IChainModuleMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_IChainModule *IChainModuleRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _IChainModule.Contract.IChainModuleCaller.contract.Call(opts, result, method, params...) +} + +func (_IChainModule *IChainModuleRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _IChainModule.Contract.IChainModuleTransactor.contract.Transfer(opts) +} + +func (_IChainModule *IChainModuleRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _IChainModule.Contract.IChainModuleTransactor.contract.Transact(opts, method, params...) +} + +func (_IChainModule *IChainModuleCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _IChainModule.Contract.contract.Call(opts, result, method, params...) +} + +func (_IChainModule *IChainModuleTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _IChainModule.Contract.contract.Transfer(opts) +} + +func (_IChainModule *IChainModuleTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _IChainModule.Contract.contract.Transact(opts, method, params...) +} + +func (_IChainModule *IChainModuleCaller) BlockHash(opts *bind.CallOpts, arg0 *big.Int) ([32]byte, error) { + var out []interface{} + err := _IChainModule.contract.Call(opts, &out, "blockHash", arg0) + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +func (_IChainModule *IChainModuleSession) BlockHash(arg0 *big.Int) ([32]byte, error) { + return _IChainModule.Contract.BlockHash(&_IChainModule.CallOpts, arg0) +} + +func (_IChainModule *IChainModuleCallerSession) BlockHash(arg0 *big.Int) ([32]byte, error) { + return _IChainModule.Contract.BlockHash(&_IChainModule.CallOpts, arg0) +} + +func (_IChainModule *IChainModuleCaller) BlockNumber(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _IChainModule.contract.Call(opts, &out, "blockNumber") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_IChainModule *IChainModuleSession) BlockNumber() (*big.Int, error) { + return _IChainModule.Contract.BlockNumber(&_IChainModule.CallOpts) +} + +func (_IChainModule *IChainModuleCallerSession) BlockNumber() (*big.Int, error) { + return _IChainModule.Contract.BlockNumber(&_IChainModule.CallOpts) +} + +func (_IChainModule *IChainModuleCaller) GetCurrentL1Fee(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _IChainModule.contract.Call(opts, &out, "getCurrentL1Fee") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_IChainModule *IChainModuleSession) GetCurrentL1Fee() (*big.Int, error) { + return _IChainModule.Contract.GetCurrentL1Fee(&_IChainModule.CallOpts) +} + +func (_IChainModule *IChainModuleCallerSession) GetCurrentL1Fee() (*big.Int, error) { + return _IChainModule.Contract.GetCurrentL1Fee(&_IChainModule.CallOpts) +} + +func (_IChainModule *IChainModuleCaller) GetGasOverhead(opts *bind.CallOpts) (GetGasOverhead, + + error) { + var out []interface{} + err := _IChainModule.contract.Call(opts, &out, "getGasOverhead") + + outstruct := new(GetGasOverhead) + if err != nil { + return *outstruct, err + } + + outstruct.ChainModuleFixedOverhead = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + outstruct.ChainModulePerByteOverhead = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int) + + return *outstruct, err + +} + +func (_IChainModule *IChainModuleSession) GetGasOverhead() (GetGasOverhead, + + error) { + return _IChainModule.Contract.GetGasOverhead(&_IChainModule.CallOpts) +} + +func (_IChainModule *IChainModuleCallerSession) GetGasOverhead() (GetGasOverhead, + + error) { + return _IChainModule.Contract.GetGasOverhead(&_IChainModule.CallOpts) +} + +func (_IChainModule *IChainModuleCaller) GetMaxL1Fee(opts *bind.CallOpts, dataSize *big.Int) (*big.Int, error) { + var out []interface{} + err := _IChainModule.contract.Call(opts, &out, "getMaxL1Fee", dataSize) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_IChainModule *IChainModuleSession) GetMaxL1Fee(dataSize *big.Int) (*big.Int, error) { + return _IChainModule.Contract.GetMaxL1Fee(&_IChainModule.CallOpts, dataSize) +} + +func (_IChainModule *IChainModuleCallerSession) GetMaxL1Fee(dataSize *big.Int) (*big.Int, error) { + return _IChainModule.Contract.GetMaxL1Fee(&_IChainModule.CallOpts, dataSize) +} + +type GetGasOverhead struct { + ChainModuleFixedOverhead *big.Int + ChainModulePerByteOverhead *big.Int +} + +func (_IChainModule *IChainModule) Address() common.Address { + return _IChainModule.address +} + +type IChainModuleInterface interface { + BlockHash(opts *bind.CallOpts, arg0 *big.Int) ([32]byte, error) + + BlockNumber(opts *bind.CallOpts) (*big.Int, error) + + GetCurrentL1Fee(opts *bind.CallOpts) (*big.Int, error) + + GetGasOverhead(opts *bind.CallOpts) (GetGasOverhead, + + error) + + GetMaxL1Fee(opts *bind.CallOpts, dataSize *big.Int) (*big.Int, error) + + Address() common.Address +} diff --git a/core/gethwrappers/generated/optimism_module/optimism_module.go b/core/gethwrappers/generated/optimism_module/optimism_module.go new file mode 100644 index 00000000000..c6015b1e32b --- /dev/null +++ b/core/gethwrappers/generated/optimism_module/optimism_module.go @@ -0,0 +1,313 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package optimism_module + +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 OptimismModuleMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"n\",\"type\":\"uint256\"}],\"name\":\"blockHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"blockNumber\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentL1Fee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"chainModuleFixedOverhead\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"chainModulePerByteOverhead\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"dataSize\",\"type\":\"uint256\"}],\"name\":\"getMaxL1Fee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b506104a3806100206000396000f3fe608060405234801561001057600080fd5b50600436106100675760003560e01c806357e871e71161005057806357e871e71461009a57806385df51fd146100a0578063de9ee35e146100b357600080fd5b8063125441401461006c57806318b8f61314610092575b600080fd5b61007f61007a3660046102e8565b6100c9565b6040519081526020015b60405180910390f35b61007f6101ea565b4361007f565b61007f6100ae3660046102e8565b6102bb565b60408051614e2081526014602082015201610089565b6000806100d7836004610330565b67ffffffffffffffff8111156100ef576100ef61034d565b6040519080825280601f01601f191660200182016040528015610119576020820181803683370190505b50905073420000000000000000000000000000000000000f73ffffffffffffffffffffffffffffffffffffffff166349948e0e82604051806060016040528060238152602001610474602391396040516020016101779291906103a0565b6040516020818303038152906040526040518263ffffffff1660e01b81526004016101a291906103cf565b602060405180830381865afa1580156101bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101e39190610420565b9392505050565b600073420000000000000000000000000000000000000f73ffffffffffffffffffffffffffffffffffffffff166349948e0e6000366040518060600160405280602381526020016104746023913960405160200161024a93929190610439565b6040516020818303038152906040526040518263ffffffff1660e01b815260040161027591906103cf565b602060405180830381865afa158015610292573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102b69190610420565b905090565b600043821015806102d657506101006102d48343610460565b115b156102e357506000919050565b504090565b6000602082840312156102fa57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808202811582820484141761034757610347610301565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60005b8381101561039757818101518382015260200161037f565b50506000910152565b600083516103b281846020880161037c565b8351908301906103c681836020880161037c565b01949350505050565b60208152600082518060208401526103ee81604085016020870161037c565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b60006020828403121561043257600080fd5b5051919050565b82848237600083820160008152835161045681836020880161037c565b0195945050505050565b818103818111156103475761034761030156feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa164736f6c6343000813000a", +} + +var OptimismModuleABI = OptimismModuleMetaData.ABI + +var OptimismModuleBin = OptimismModuleMetaData.Bin + +func DeployOptimismModule(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *OptimismModule, error) { + parsed, err := OptimismModuleMetaData.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(OptimismModuleBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &OptimismModule{address: address, abi: *parsed, OptimismModuleCaller: OptimismModuleCaller{contract: contract}, OptimismModuleTransactor: OptimismModuleTransactor{contract: contract}, OptimismModuleFilterer: OptimismModuleFilterer{contract: contract}}, nil +} + +type OptimismModule struct { + address common.Address + abi abi.ABI + OptimismModuleCaller + OptimismModuleTransactor + OptimismModuleFilterer +} + +type OptimismModuleCaller struct { + contract *bind.BoundContract +} + +type OptimismModuleTransactor struct { + contract *bind.BoundContract +} + +type OptimismModuleFilterer struct { + contract *bind.BoundContract +} + +type OptimismModuleSession struct { + Contract *OptimismModule + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type OptimismModuleCallerSession struct { + Contract *OptimismModuleCaller + CallOpts bind.CallOpts +} + +type OptimismModuleTransactorSession struct { + Contract *OptimismModuleTransactor + TransactOpts bind.TransactOpts +} + +type OptimismModuleRaw struct { + Contract *OptimismModule +} + +type OptimismModuleCallerRaw struct { + Contract *OptimismModuleCaller +} + +type OptimismModuleTransactorRaw struct { + Contract *OptimismModuleTransactor +} + +func NewOptimismModule(address common.Address, backend bind.ContractBackend) (*OptimismModule, error) { + abi, err := abi.JSON(strings.NewReader(OptimismModuleABI)) + if err != nil { + return nil, err + } + contract, err := bindOptimismModule(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &OptimismModule{address: address, abi: abi, OptimismModuleCaller: OptimismModuleCaller{contract: contract}, OptimismModuleTransactor: OptimismModuleTransactor{contract: contract}, OptimismModuleFilterer: OptimismModuleFilterer{contract: contract}}, nil +} + +func NewOptimismModuleCaller(address common.Address, caller bind.ContractCaller) (*OptimismModuleCaller, error) { + contract, err := bindOptimismModule(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &OptimismModuleCaller{contract: contract}, nil +} + +func NewOptimismModuleTransactor(address common.Address, transactor bind.ContractTransactor) (*OptimismModuleTransactor, error) { + contract, err := bindOptimismModule(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &OptimismModuleTransactor{contract: contract}, nil +} + +func NewOptimismModuleFilterer(address common.Address, filterer bind.ContractFilterer) (*OptimismModuleFilterer, error) { + contract, err := bindOptimismModule(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &OptimismModuleFilterer{contract: contract}, nil +} + +func bindOptimismModule(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := OptimismModuleMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_OptimismModule *OptimismModuleRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _OptimismModule.Contract.OptimismModuleCaller.contract.Call(opts, result, method, params...) +} + +func (_OptimismModule *OptimismModuleRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _OptimismModule.Contract.OptimismModuleTransactor.contract.Transfer(opts) +} + +func (_OptimismModule *OptimismModuleRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _OptimismModule.Contract.OptimismModuleTransactor.contract.Transact(opts, method, params...) +} + +func (_OptimismModule *OptimismModuleCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _OptimismModule.Contract.contract.Call(opts, result, method, params...) +} + +func (_OptimismModule *OptimismModuleTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _OptimismModule.Contract.contract.Transfer(opts) +} + +func (_OptimismModule *OptimismModuleTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _OptimismModule.Contract.contract.Transact(opts, method, params...) +} + +func (_OptimismModule *OptimismModuleCaller) BlockHash(opts *bind.CallOpts, n *big.Int) ([32]byte, error) { + var out []interface{} + err := _OptimismModule.contract.Call(opts, &out, "blockHash", n) + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +func (_OptimismModule *OptimismModuleSession) BlockHash(n *big.Int) ([32]byte, error) { + return _OptimismModule.Contract.BlockHash(&_OptimismModule.CallOpts, n) +} + +func (_OptimismModule *OptimismModuleCallerSession) BlockHash(n *big.Int) ([32]byte, error) { + return _OptimismModule.Contract.BlockHash(&_OptimismModule.CallOpts, n) +} + +func (_OptimismModule *OptimismModuleCaller) BlockNumber(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _OptimismModule.contract.Call(opts, &out, "blockNumber") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_OptimismModule *OptimismModuleSession) BlockNumber() (*big.Int, error) { + return _OptimismModule.Contract.BlockNumber(&_OptimismModule.CallOpts) +} + +func (_OptimismModule *OptimismModuleCallerSession) BlockNumber() (*big.Int, error) { + return _OptimismModule.Contract.BlockNumber(&_OptimismModule.CallOpts) +} + +func (_OptimismModule *OptimismModuleCaller) GetCurrentL1Fee(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _OptimismModule.contract.Call(opts, &out, "getCurrentL1Fee") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_OptimismModule *OptimismModuleSession) GetCurrentL1Fee() (*big.Int, error) { + return _OptimismModule.Contract.GetCurrentL1Fee(&_OptimismModule.CallOpts) +} + +func (_OptimismModule *OptimismModuleCallerSession) GetCurrentL1Fee() (*big.Int, error) { + return _OptimismModule.Contract.GetCurrentL1Fee(&_OptimismModule.CallOpts) +} + +func (_OptimismModule *OptimismModuleCaller) GetGasOverhead(opts *bind.CallOpts) (GetGasOverhead, + + error) { + var out []interface{} + err := _OptimismModule.contract.Call(opts, &out, "getGasOverhead") + + outstruct := new(GetGasOverhead) + if err != nil { + return *outstruct, err + } + + outstruct.ChainModuleFixedOverhead = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + outstruct.ChainModulePerByteOverhead = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int) + + return *outstruct, err + +} + +func (_OptimismModule *OptimismModuleSession) GetGasOverhead() (GetGasOverhead, + + error) { + return _OptimismModule.Contract.GetGasOverhead(&_OptimismModule.CallOpts) +} + +func (_OptimismModule *OptimismModuleCallerSession) GetGasOverhead() (GetGasOverhead, + + error) { + return _OptimismModule.Contract.GetGasOverhead(&_OptimismModule.CallOpts) +} + +func (_OptimismModule *OptimismModuleCaller) GetMaxL1Fee(opts *bind.CallOpts, dataSize *big.Int) (*big.Int, error) { + var out []interface{} + err := _OptimismModule.contract.Call(opts, &out, "getMaxL1Fee", dataSize) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_OptimismModule *OptimismModuleSession) GetMaxL1Fee(dataSize *big.Int) (*big.Int, error) { + return _OptimismModule.Contract.GetMaxL1Fee(&_OptimismModule.CallOpts, dataSize) +} + +func (_OptimismModule *OptimismModuleCallerSession) GetMaxL1Fee(dataSize *big.Int) (*big.Int, error) { + return _OptimismModule.Contract.GetMaxL1Fee(&_OptimismModule.CallOpts, dataSize) +} + +type GetGasOverhead struct { + ChainModuleFixedOverhead *big.Int + ChainModulePerByteOverhead *big.Int +} + +func (_OptimismModule *OptimismModule) Address() common.Address { + return _OptimismModule.address +} + +type OptimismModuleInterface interface { + BlockHash(opts *bind.CallOpts, n *big.Int) ([32]byte, error) + + BlockNumber(opts *bind.CallOpts) (*big.Int, error) + + GetCurrentL1Fee(opts *bind.CallOpts) (*big.Int, error) + + GetGasOverhead(opts *bind.CallOpts) (GetGasOverhead, + + error) + + GetMaxL1Fee(opts *bind.CallOpts, dataSize *big.Int) (*big.Int, error) + + Address() common.Address +} diff --git a/core/gethwrappers/generated/scroll_module/scroll_module.go b/core/gethwrappers/generated/scroll_module/scroll_module.go new file mode 100644 index 00000000000..2bb37a8d480 --- /dev/null +++ b/core/gethwrappers/generated/scroll_module/scroll_module.go @@ -0,0 +1,313 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package scroll_module + +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 ScrollModuleMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"n\",\"type\":\"uint256\"}],\"name\":\"blockHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"blockNumber\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentL1Fee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"chainModuleFixedOverhead\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"chainModulePerByteOverhead\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"dataSize\",\"type\":\"uint256\"}],\"name\":\"getMaxL1Fee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b506104f8806100206000396000f3fe608060405234801561001057600080fd5b50600436106100675760003560e01c806357e871e71161005057806357e871e71461009a57806385df51fd146100a0578063de9ee35e146100b357600080fd5b8063125441401461006c57806318b8f61314610092575b600080fd5b61007f61007a3660046102e8565b6100c9565b6040519081526020015b60405180910390f35b61007f6101ea565b4361007f565b61007f6100ae3660046102e8565b6102bb565b60408051614e2081526014602082015201610089565b6000806100d7836004610330565b67ffffffffffffffff8111156100ef576100ef61034d565b6040519080825280601f01601f191660200182016040528015610119576020820181803683370190505b50905073530000000000000000000000000000000000000273ffffffffffffffffffffffffffffffffffffffff166349948e0e826040518060a0016040528060788152602001610474607891396040516020016101779291906103a0565b6040516020818303038152906040526040518263ffffffff1660e01b81526004016101a291906103cf565b602060405180830381865afa1580156101bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101e39190610420565b9392505050565b600073530000000000000000000000000000000000000273ffffffffffffffffffffffffffffffffffffffff166349948e0e6000366040518060a00160405280607881526020016104746078913960405160200161024a93929190610439565b6040516020818303038152906040526040518263ffffffff1660e01b815260040161027591906103cf565b602060405180830381865afa158015610292573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102b69190610420565b905090565b600043821015806102d657506101006102d48343610460565b115b156102e357506000919050565b504090565b6000602082840312156102fa57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808202811582820484141761034757610347610301565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60005b8381101561039757818101518382015260200161037f565b50506000910152565b600083516103b281846020880161037c565b8351908301906103c681836020880161037c565b01949350505050565b60208152600082518060208401526103ee81604085016020870161037c565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b60006020828403121561043257600080fd5b5051919050565b82848237600083820160008152835161045681836020880161037c565b0195945050505050565b818103818111156103475761034761030156feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa164736f6c6343000813000a", +} + +var ScrollModuleABI = ScrollModuleMetaData.ABI + +var ScrollModuleBin = ScrollModuleMetaData.Bin + +func DeployScrollModule(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *ScrollModule, error) { + parsed, err := ScrollModuleMetaData.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(ScrollModuleBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &ScrollModule{address: address, abi: *parsed, ScrollModuleCaller: ScrollModuleCaller{contract: contract}, ScrollModuleTransactor: ScrollModuleTransactor{contract: contract}, ScrollModuleFilterer: ScrollModuleFilterer{contract: contract}}, nil +} + +type ScrollModule struct { + address common.Address + abi abi.ABI + ScrollModuleCaller + ScrollModuleTransactor + ScrollModuleFilterer +} + +type ScrollModuleCaller struct { + contract *bind.BoundContract +} + +type ScrollModuleTransactor struct { + contract *bind.BoundContract +} + +type ScrollModuleFilterer struct { + contract *bind.BoundContract +} + +type ScrollModuleSession struct { + Contract *ScrollModule + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type ScrollModuleCallerSession struct { + Contract *ScrollModuleCaller + CallOpts bind.CallOpts +} + +type ScrollModuleTransactorSession struct { + Contract *ScrollModuleTransactor + TransactOpts bind.TransactOpts +} + +type ScrollModuleRaw struct { + Contract *ScrollModule +} + +type ScrollModuleCallerRaw struct { + Contract *ScrollModuleCaller +} + +type ScrollModuleTransactorRaw struct { + Contract *ScrollModuleTransactor +} + +func NewScrollModule(address common.Address, backend bind.ContractBackend) (*ScrollModule, error) { + abi, err := abi.JSON(strings.NewReader(ScrollModuleABI)) + if err != nil { + return nil, err + } + contract, err := bindScrollModule(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &ScrollModule{address: address, abi: abi, ScrollModuleCaller: ScrollModuleCaller{contract: contract}, ScrollModuleTransactor: ScrollModuleTransactor{contract: contract}, ScrollModuleFilterer: ScrollModuleFilterer{contract: contract}}, nil +} + +func NewScrollModuleCaller(address common.Address, caller bind.ContractCaller) (*ScrollModuleCaller, error) { + contract, err := bindScrollModule(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &ScrollModuleCaller{contract: contract}, nil +} + +func NewScrollModuleTransactor(address common.Address, transactor bind.ContractTransactor) (*ScrollModuleTransactor, error) { + contract, err := bindScrollModule(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &ScrollModuleTransactor{contract: contract}, nil +} + +func NewScrollModuleFilterer(address common.Address, filterer bind.ContractFilterer) (*ScrollModuleFilterer, error) { + contract, err := bindScrollModule(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &ScrollModuleFilterer{contract: contract}, nil +} + +func bindScrollModule(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := ScrollModuleMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_ScrollModule *ScrollModuleRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _ScrollModule.Contract.ScrollModuleCaller.contract.Call(opts, result, method, params...) +} + +func (_ScrollModule *ScrollModuleRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ScrollModule.Contract.ScrollModuleTransactor.contract.Transfer(opts) +} + +func (_ScrollModule *ScrollModuleRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ScrollModule.Contract.ScrollModuleTransactor.contract.Transact(opts, method, params...) +} + +func (_ScrollModule *ScrollModuleCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _ScrollModule.Contract.contract.Call(opts, result, method, params...) +} + +func (_ScrollModule *ScrollModuleTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ScrollModule.Contract.contract.Transfer(opts) +} + +func (_ScrollModule *ScrollModuleTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ScrollModule.Contract.contract.Transact(opts, method, params...) +} + +func (_ScrollModule *ScrollModuleCaller) BlockHash(opts *bind.CallOpts, n *big.Int) ([32]byte, error) { + var out []interface{} + err := _ScrollModule.contract.Call(opts, &out, "blockHash", n) + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +func (_ScrollModule *ScrollModuleSession) BlockHash(n *big.Int) ([32]byte, error) { + return _ScrollModule.Contract.BlockHash(&_ScrollModule.CallOpts, n) +} + +func (_ScrollModule *ScrollModuleCallerSession) BlockHash(n *big.Int) ([32]byte, error) { + return _ScrollModule.Contract.BlockHash(&_ScrollModule.CallOpts, n) +} + +func (_ScrollModule *ScrollModuleCaller) BlockNumber(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _ScrollModule.contract.Call(opts, &out, "blockNumber") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_ScrollModule *ScrollModuleSession) BlockNumber() (*big.Int, error) { + return _ScrollModule.Contract.BlockNumber(&_ScrollModule.CallOpts) +} + +func (_ScrollModule *ScrollModuleCallerSession) BlockNumber() (*big.Int, error) { + return _ScrollModule.Contract.BlockNumber(&_ScrollModule.CallOpts) +} + +func (_ScrollModule *ScrollModuleCaller) GetCurrentL1Fee(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _ScrollModule.contract.Call(opts, &out, "getCurrentL1Fee") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_ScrollModule *ScrollModuleSession) GetCurrentL1Fee() (*big.Int, error) { + return _ScrollModule.Contract.GetCurrentL1Fee(&_ScrollModule.CallOpts) +} + +func (_ScrollModule *ScrollModuleCallerSession) GetCurrentL1Fee() (*big.Int, error) { + return _ScrollModule.Contract.GetCurrentL1Fee(&_ScrollModule.CallOpts) +} + +func (_ScrollModule *ScrollModuleCaller) GetGasOverhead(opts *bind.CallOpts) (GetGasOverhead, + + error) { + var out []interface{} + err := _ScrollModule.contract.Call(opts, &out, "getGasOverhead") + + outstruct := new(GetGasOverhead) + if err != nil { + return *outstruct, err + } + + outstruct.ChainModuleFixedOverhead = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + outstruct.ChainModulePerByteOverhead = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int) + + return *outstruct, err + +} + +func (_ScrollModule *ScrollModuleSession) GetGasOverhead() (GetGasOverhead, + + error) { + return _ScrollModule.Contract.GetGasOverhead(&_ScrollModule.CallOpts) +} + +func (_ScrollModule *ScrollModuleCallerSession) GetGasOverhead() (GetGasOverhead, + + error) { + return _ScrollModule.Contract.GetGasOverhead(&_ScrollModule.CallOpts) +} + +func (_ScrollModule *ScrollModuleCaller) GetMaxL1Fee(opts *bind.CallOpts, dataSize *big.Int) (*big.Int, error) { + var out []interface{} + err := _ScrollModule.contract.Call(opts, &out, "getMaxL1Fee", dataSize) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_ScrollModule *ScrollModuleSession) GetMaxL1Fee(dataSize *big.Int) (*big.Int, error) { + return _ScrollModule.Contract.GetMaxL1Fee(&_ScrollModule.CallOpts, dataSize) +} + +func (_ScrollModule *ScrollModuleCallerSession) GetMaxL1Fee(dataSize *big.Int) (*big.Int, error) { + return _ScrollModule.Contract.GetMaxL1Fee(&_ScrollModule.CallOpts, dataSize) +} + +type GetGasOverhead struct { + ChainModuleFixedOverhead *big.Int + ChainModulePerByteOverhead *big.Int +} + +func (_ScrollModule *ScrollModule) Address() common.Address { + return _ScrollModule.address +} + +type ScrollModuleInterface interface { + BlockHash(opts *bind.CallOpts, n *big.Int) ([32]byte, error) + + BlockNumber(opts *bind.CallOpts) (*big.Int, error) + + GetCurrentL1Fee(opts *bind.CallOpts) (*big.Int, error) + + GetGasOverhead(opts *bind.CallOpts) (GetGasOverhead, + + error) + + GetMaxL1Fee(opts *bind.CallOpts, dataSize *big.Int) (*big.Int, 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 d112efac94f..29e0ffacf85 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,6 +1,7 @@ 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 +arbitrum_module: ../../contracts/solc/v0.8.19/ArbitrumModule/ArbitrumModule.abi ../../contracts/solc/v0.8.19/ArbitrumModule/ArbitrumModule.bin 9048e5ccf9a6274cbf131cb5d7ef24b783e2fc9595153e0abacddfd2b6668469 authorized_forwarder: ../../contracts/solc/v0.8.19/AuthorizedForwarder/AuthorizedForwarder.abi ../../contracts/solc/v0.8.19/AuthorizedForwarder/AuthorizedForwarder.bin 8ea76c883d460f8353a45a493f2aebeb5a2d9a7b4619d1bc4fff5fb590bb3e10 authorized_receiver: ../../contracts/solc/v0.8.19/AuthorizedReceiver/AuthorizedReceiver.abi ../../contracts/solc/v0.8.19/AuthorizedReceiver/AuthorizedReceiver.bin 18e8969ba3234b027e1b16c11a783aca58d0ea5c2361010ec597f134b7bf1c4f automation_consumer_benchmark: ../../contracts/solc/v0.8.16/AutomationConsumerBenchmark/AutomationConsumerBenchmark.abi ../../contracts/solc/v0.8.16/AutomationConsumerBenchmark/AutomationConsumerBenchmark.bin f52c76f1aaed4be541d82d97189d70f5aa027fc9838037dd7a7d21910c8c488e @@ -16,6 +17,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 73cb626b5cb2c3464655b61b8ac42fe7a1963fe25e6a5eea40b8e4d5bff3de36 blockhash_store: ../../contracts/solc/v0.8.6/BlockhashStore/BlockhashStore.abi ../../contracts/solc/v0.8.6/BlockhashStore/BlockhashStore.bin 12b0662f1636a341c8863bdec7a20f2ddd97c3a4fd1a7ae353fe316609face4e +chain_module_base: ../../contracts/solc/v0.8.19/ChainModuleBase/ChainModuleBase.abi ../../contracts/solc/v0.8.19/ChainModuleBase/ChainModuleBase.bin 41331cdf20464ea860ab57324642ff4797feb20e376908921726ce7e5cb2cf34 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 @@ -27,6 +29,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_automation_registry_master_wrapper_2_2: ../../contracts/solc/v0.8.19/IAutomationRegistryMaster/IAutomationRegistryMaster.abi ../../contracts/solc/v0.8.19/IAutomationRegistryMaster/IAutomationRegistryMaster.bin 0886dd1df1f4dcf5b08012f8adcf30fd96caab28999610e70ce02beb2170c92f +i_chain_module: ../../contracts/solc/v0.8.19/IChainModule/IChainModule.abi ../../contracts/solc/v0.8.19/IChainModule/IChainModule.bin 383611981c86c70522f41b8750719faacc7d7933a22849d5004799ebef3371fa 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_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 @@ -55,8 +58,10 @@ multiwordconsumer_wrapper: ../../contracts/solc/v0.7/MultiWordConsumer/MultiWord 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 357203fabe3df436eb015e2d5094374c6967a9fc922ac8edc265b27aac4d67cf operator_wrapper: ../../contracts/solc/v0.8.19/Operator/Operator.abi ../../contracts/solc/v0.8.19/Operator/Operator.bin c5e1db81070d940a82ef100b0bce38e055593cbeebbc73abf9d45c30d6020cd2 +optimism_module: ../../contracts/solc/v0.8.19/OptimismModule/OptimismModule.abi ../../contracts/solc/v0.8.19/OptimismModule/OptimismModule.bin 7df8460e07edd0cc849f7e7b111a4135d9e4740ad8fb25003d6a21dc2b6ffe25 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 +scroll_module: ../../contracts/solc/v0.8.19/ScrollModule/ScrollModule.abi ../../contracts/solc/v0.8.19/ScrollModule/ScrollModule.bin c7283da6f6bd4874f8c5b2d6fb93fd1d9dfbab30d0f39adf5c9d448d88acdb88 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 diff --git a/core/gethwrappers/go_generate.go b/core/gethwrappers/go_generate.go index 00da87af56b..c582583b783 100644 --- a/core/gethwrappers/go_generate.go +++ b/core/gethwrappers/go_generate.go @@ -62,6 +62,11 @@ package gethwrappers //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/AutomationRegistryLogicB2_2/AutomationRegistryLogicB2_2.abi ../../contracts/solc/v0.8.19/AutomationRegistryLogicB2_2/AutomationRegistryLogicB2_2.bin AutomationRegistryLogicB automation_registry_logic_b_wrapper_2_2 //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/IAutomationRegistryMaster/IAutomationRegistryMaster.abi ../../contracts/solc/v0.8.19/IAutomationRegistryMaster/IAutomationRegistryMaster.bin IAutomationRegistryMaster i_automation_registry_master_wrapper_2_2 //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/AutomationUtils2_2/AutomationUtils2_2.abi ../../contracts/solc/v0.8.19/AutomationUtils2_2/AutomationUtils2_2.bin AutomationUtils automation_utils_2_2 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/ArbitrumModule/ArbitrumModule.abi ../../contracts/solc/v0.8.19/ArbitrumModule/ArbitrumModule.bin ArbitrumModule arbitrum_module +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/ChainModuleBase/ChainModuleBase.abi ../../contracts/solc/v0.8.19/ChainModuleBase/ChainModuleBase.bin ChainModuleBase chain_module_base +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/OptimismModule/OptimismModule.abi ../../contracts/solc/v0.8.19/OptimismModule/OptimismModule.bin OptimismModule optimism_module +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/ScrollModule/ScrollModule.abi ../../contracts/solc/v0.8.19/ScrollModule/ScrollModule.bin ScrollModule scroll_module +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/IChainModule/IChainModule.abi ../../contracts/solc/v0.8.19/IChainModule/IChainModule.bin IChainModule i_chain_module //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 diff --git a/integration-tests/actions/automation_ocr_helpers.go b/integration-tests/actions/automation_ocr_helpers.go index 38df2e00b5c..9b564409c2a 100644 --- a/integration-tests/actions/automation_ocr_helpers.go +++ b/integration-tests/actions/automation_ocr_helpers.go @@ -20,17 +20,15 @@ import ( ocr2keepers20config "github.com/smartcontractkit/chainlink-automation/pkg/v2/config" ocr2keepers30config "github.com/smartcontractkit/chainlink-automation/pkg/v3/config" - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/logging" - "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/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" + "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" ) func BuildAutoOCR2ConfigVars( @@ -39,8 +37,10 @@ func BuildAutoOCR2ConfigVars( registryConfig contracts.KeeperRegistrySettings, registrar string, deltaStage time.Duration, + chainModuleAddress common.Address, + reorgProtectionEnabled bool, ) (contracts.OCRv2Config, error) { - return BuildAutoOCR2ConfigVarsWithKeyIndex(t, chainlinkNodes, registryConfig, registrar, deltaStage, 0, common.Address{}) + return BuildAutoOCR2ConfigVarsWithKeyIndex(t, chainlinkNodes, registryConfig, registrar, deltaStage, 0, common.Address{}, chainModuleAddress, reorgProtectionEnabled) } func BuildAutoOCR2ConfigVarsWithKeyIndex( @@ -51,6 +51,8 @@ func BuildAutoOCR2ConfigVarsWithKeyIndex( deltaStage time.Duration, keyIndex int, registryOwnerAddress common.Address, + chainModuleAddress common.Address, + reorgProtectionEnabled bool, ) (contracts.OCRv2Config, error) { l := logging.GetTestLogger(t) S, oracleIdentities, err := GetOracleIdentitiesWithKeyIndex(chainlinkNodes, keyIndex) @@ -65,7 +67,7 @@ func BuildAutoOCR2ConfigVarsWithKeyIndex( var offchainConfigVersion uint64 var offchainConfig []byte - if registryConfig.RegistryVersion == ethereum.RegistryVersion_2_1 { + if registryConfig.RegistryVersion == ethereum.RegistryVersion_2_1 || registryConfig.RegistryVersion == ethereum.RegistryVersion_2_2 { offC, err = json.Marshal(ocr2keepers30config.OffchainConfig{ TargetProbability: "0.999", TargetInRounds: 1, @@ -151,7 +153,7 @@ func BuildAutoOCR2ConfigVarsWithKeyIndex( transmitters = append(transmitters, common.HexToAddress(string(transmitter))) } - onchainConfig, err := registryConfig.EncodeOnChainConfig(registrar, registryOwnerAddress) + onchainConfig, err := registryConfig.EncodeOnChainConfig(registrar, registryOwnerAddress, chainModuleAddress, reorgProtectionEnabled) if err != nil { return contracts.OCRv2Config{}, err } @@ -183,12 +185,13 @@ func CreateOCRKeeperJobs( bootstrapP2PId := bootstrapP2PIds.Data[0].Attributes.PeerID var contractVersion string - if registryVersion == ethereum.RegistryVersion_2_1 { + // TODO: use v2.1 for registry 2.2 for now until AUTO-9033 is done + if registryVersion == ethereum.RegistryVersion_2_1 || registryVersion == ethereum.RegistryVersion_2_2 { contractVersion = "v2.1" } else if registryVersion == ethereum.RegistryVersion_2_0 { contractVersion = "v2.0" } else { - require.FailNow(t, "v2.0 and v2.1 are the only supported versions") + require.FailNow(t, "v2.0, v2.1, and v2.2 are the only supported versions") } bootstrapSpec := &client.OCR2TaskJobSpec{ diff --git a/integration-tests/actions/automation_ocr_helpers_local.go b/integration-tests/actions/automation_ocr_helpers_local.go index e591e75a983..6c72956045a 100644 --- a/integration-tests/actions/automation_ocr_helpers_local.go +++ b/integration-tests/actions/automation_ocr_helpers_local.go @@ -18,13 +18,12 @@ import ( ocr2keepers20config "github.com/smartcontractkit/chainlink-automation/pkg/v2/config" ocr2keepers30config "github.com/smartcontractkit/chainlink-automation/pkg/v3/config" - "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/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" + "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" ) func BuildAutoOCR2ConfigVarsLocal( @@ -34,8 +33,10 @@ func BuildAutoOCR2ConfigVarsLocal( registrar string, deltaStage time.Duration, registryOwnerAddress common.Address, + chainModuleAddress common.Address, + reorgProtectionEnabled bool, ) (contracts.OCRv2Config, error) { - return BuildAutoOCR2ConfigVarsWithKeyIndexLocal(l, chainlinkNodes, registryConfig, registrar, deltaStage, 0, registryOwnerAddress) + return BuildAutoOCR2ConfigVarsWithKeyIndexLocal(l, chainlinkNodes, registryConfig, registrar, deltaStage, 0, registryOwnerAddress, chainModuleAddress, reorgProtectionEnabled) } func BuildAutoOCR2ConfigVarsWithKeyIndexLocal( @@ -46,6 +47,8 @@ func BuildAutoOCR2ConfigVarsWithKeyIndexLocal( deltaStage time.Duration, keyIndex int, registryOwnerAddress common.Address, + chainModuleAddress common.Address, + reorgProtectionEnabled bool, ) (contracts.OCRv2Config, error) { S, oracleIdentities, err := GetOracleIdentitiesWithKeyIndexLocal(chainlinkNodes, keyIndex) if err != nil { @@ -59,7 +62,7 @@ func BuildAutoOCR2ConfigVarsWithKeyIndexLocal( var offchainConfigVersion uint64 var offchainConfig []byte - if registryConfig.RegistryVersion == ethereum.RegistryVersion_2_1 { + if registryConfig.RegistryVersion == ethereum.RegistryVersion_2_1 || registryConfig.RegistryVersion == ethereum.RegistryVersion_2_2 { offC, err = json.Marshal(ocr2keepers30config.OffchainConfig{ TargetProbability: "0.999", TargetInRounds: 1, @@ -149,7 +152,7 @@ func BuildAutoOCR2ConfigVarsWithKeyIndexLocal( transmitters = append(transmitters, common.HexToAddress(string(transmitter))) } - onchainConfig, err := registryConfig.EncodeOnChainConfig(registrar, registryOwnerAddress) + onchainConfig, err := registryConfig.EncodeOnChainConfig(registrar, registryOwnerAddress, chainModuleAddress, reorgProtectionEnabled) if err != nil { return contracts.OCRv2Config{}, err } @@ -183,12 +186,13 @@ func CreateOCRKeeperJobsLocal( bootstrapP2PId := bootstrapP2PIds.Data[0].Attributes.PeerID var contractVersion string - if registryVersion == ethereum.RegistryVersion_2_1 { + // TODO: use v2.1 for registry 2.2 for now until AUTO-9033 is done + if registryVersion == ethereum.RegistryVersion_2_1 || registryVersion == ethereum.RegistryVersion_2_2 { contractVersion = "v2.1" } else if registryVersion == ethereum.RegistryVersion_2_0 { contractVersion = "v2.0" } else { - return fmt.Errorf("v2.0 and v2.1 are the only supported versions") + return fmt.Errorf("v2.0, v2.1, and v2.2 are the only supported versions") } bootstrapSpec := &client.OCR2TaskJobSpec{ diff --git a/integration-tests/actions/automationv2/actions.go b/integration-tests/actions/automationv2/actions.go index bccd3ef1675..5c181cc449b 100644 --- a/integration-tests/actions/automationv2/actions.go +++ b/integration-tests/actions/automationv2/actions.go @@ -11,8 +11,6 @@ import ( "testing" "time" - "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" - "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/lib/pq" @@ -24,16 +22,19 @@ import ( "golang.org/x/sync/errgroup" "gopkg.in/guregu/null.v4" + "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_registrar_wrapper2_1" + ocr2keepers20config "github.com/smartcontractkit/chainlink-automation/pkg/v2/config" ocr2keepers30config "github.com/smartcontractkit/chainlink-automation/pkg/v3/config" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "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" @@ -371,12 +372,13 @@ func (a *AutomationTest) AddBootstrapJob() error { func (a *AutomationTest) AddAutomationJobs() error { var contractVersion string - if a.RegistrySettings.RegistryVersion == ethereum.RegistryVersion_2_1 { + // TODO: use v2.1 for registry 2.2 for now until AUTO-9033 is done + if a.RegistrySettings.RegistryVersion == ethereum.RegistryVersion_2_1 || a.RegistrySettings.RegistryVersion == ethereum.RegistryVersion_2_2 { contractVersion = "v2.1" } else if a.RegistrySettings.RegistryVersion == ethereum.RegistryVersion_2_0 { contractVersion = "v2.0" } else { - return fmt.Errorf("v2.0 and v2.1 are the only supported versions") + return fmt.Errorf("v2.0, v2.1, and v2.2 are the only supported versions") } for i := 1; i < len(a.ChainlinkNodes); i++ { autoOCR2JobSpec := client.OCR2TaskJobSpec{ @@ -411,7 +413,6 @@ func (a *AutomationTest) SetConfigOnRegistry() error { donNodes := a.NodeDetails[1:] S := make([]int, len(donNodes)) oracleIdentities := make([]confighelper.OracleIdentityExtra, len(donNodes)) - var offC []byte var signerOnchainPublicKeys []types.OnchainPublicKey var transmitterAccounts []types.Account var f uint8 @@ -470,63 +471,17 @@ func (a *AutomationTest) SetConfigOnRegistry() error { switch a.RegistrySettings.RegistryVersion { case ethereum.RegistryVersion_2_0: - offC, err = json.Marshal(ocr2keepers20config.OffchainConfig{ - TargetProbability: a.PluginConfig.TargetProbability, - TargetInRounds: a.PluginConfig.TargetInRounds, - PerformLockoutWindow: a.PluginConfig.PerformLockoutWindow, - GasLimitPerReport: a.PluginConfig.GasLimitPerReport, - GasOverheadPerUpkeep: a.PluginConfig.GasOverheadPerUpkeep, - MinConfirmations: a.PluginConfig.MinConfirmations, - MaxUpkeepBatchSize: a.PluginConfig.MaxUpkeepBatchSize, - }) - if err != nil { - return errors.Join(err, fmt.Errorf("failed to marshal plugin config")) - } - - signerOnchainPublicKeys, transmitterAccounts, f, _, offchainConfigVersion, offchainConfig, err = ocr2.ContractSetConfigArgsForTests( - a.PublicConfig.DeltaProgress, a.PublicConfig.DeltaResend, - a.PublicConfig.DeltaRound, a.PublicConfig.DeltaGrace, - a.PublicConfig.DeltaStage, uint8(a.PublicConfig.RMax), - S, oracleIdentities, offC, - a.PublicConfig.MaxDurationQuery, a.PublicConfig.MaxDurationObservation, - 1200*time.Millisecond, - a.PublicConfig.MaxDurationShouldAcceptAttestedReport, - a.PublicConfig.MaxDurationShouldTransmitAcceptedReport, - a.PublicConfig.F, a.PublicConfig.OnchainConfig, - ) + signerOnchainPublicKeys, transmitterAccounts, f, _, offchainConfigVersion, offchainConfig, err = calculateOCR2ConfigArgs(a, S, oracleIdentities) if err != nil { return errors.Join(err, fmt.Errorf("failed to build config args")) } - - case ethereum.RegistryVersion_2_1: - offC, err = json.Marshal(ocr2keepers30config.OffchainConfig{ - TargetProbability: a.PluginConfig.TargetProbability, - TargetInRounds: a.PluginConfig.TargetInRounds, - PerformLockoutWindow: a.PluginConfig.PerformLockoutWindow, - GasLimitPerReport: a.PluginConfig.GasLimitPerReport, - GasOverheadPerUpkeep: a.PluginConfig.GasOverheadPerUpkeep, - MinConfirmations: a.PluginConfig.MinConfirmations, - MaxUpkeepBatchSize: a.PluginConfig.MaxUpkeepBatchSize, - }) - if err != nil { - return errors.Join(err, fmt.Errorf("failed to marshal plugin config")) - } - - signerOnchainPublicKeys, transmitterAccounts, f, _, offchainConfigVersion, offchainConfig, err = ocr3.ContractSetConfigArgsForTests( - a.PublicConfig.DeltaProgress, a.PublicConfig.DeltaResend, a.PublicConfig.DeltaInitial, - a.PublicConfig.DeltaRound, a.PublicConfig.DeltaGrace, a.PublicConfig.DeltaCertifiedCommitRequest, - a.PublicConfig.DeltaStage, a.PublicConfig.RMax, - S, oracleIdentities, offC, - a.PublicConfig.MaxDurationQuery, a.PublicConfig.MaxDurationObservation, - a.PublicConfig.MaxDurationShouldAcceptAttestedReport, - a.PublicConfig.MaxDurationShouldTransmitAcceptedReport, - a.PublicConfig.F, a.PublicConfig.OnchainConfig, - ) + case ethereum.RegistryVersion_2_1, ethereum.RegistryVersion_2_2: + signerOnchainPublicKeys, transmitterAccounts, f, _, offchainConfigVersion, offchainConfig, err = calculateOCR3ConfigArgs(a, S, oracleIdentities) if err != nil { return errors.Join(err, fmt.Errorf("failed to build config args")) } default: - return fmt.Errorf("v2.0 and v2.1 are the only supported versions") + return fmt.Errorf("v2.0, v2.1, and v2.2 are the only supported versions") } var signers []common.Address @@ -545,7 +500,7 @@ func (a *AutomationTest) SetConfigOnRegistry() error { transmitters = append(transmitters, common.HexToAddress(string(transmitter))) } - onchainConfig, err := a.RegistrySettings.EncodeOnChainConfig(a.Registrar.Address(), a.UpkeepPrivilegeManager) + onchainConfig, err := a.RegistrySettings.EncodeOnChainConfig(a.Registrar.Address(), a.UpkeepPrivilegeManager, a.Registry.ChainModuleAddress(), a.Registry.ReorgProtectionEnabled()) if err != nil { return errors.Join(err, fmt.Errorf("failed to encode onchain config")) } @@ -566,6 +521,69 @@ func (a *AutomationTest) SetConfigOnRegistry() error { return nil } +func calculateOCR2ConfigArgs(a *AutomationTest, S []int, oracleIdentities []confighelper.OracleIdentityExtra) ( + signers []types.OnchainPublicKey, + transmitters []types.Account, + f_ uint8, + onchainConfig_ []byte, + offchainConfigVersion uint64, + offchainConfig []byte, + err error, +) { + offC, _ := json.Marshal(ocr2keepers20config.OffchainConfig{ + TargetProbability: a.PluginConfig.TargetProbability, + TargetInRounds: a.PluginConfig.TargetInRounds, + PerformLockoutWindow: a.PluginConfig.PerformLockoutWindow, + GasLimitPerReport: a.PluginConfig.GasLimitPerReport, + GasOverheadPerUpkeep: a.PluginConfig.GasOverheadPerUpkeep, + MinConfirmations: a.PluginConfig.MinConfirmations, + MaxUpkeepBatchSize: a.PluginConfig.MaxUpkeepBatchSize, + }) + + return ocr2.ContractSetConfigArgsForTests( + a.PublicConfig.DeltaProgress, a.PublicConfig.DeltaResend, + a.PublicConfig.DeltaRound, a.PublicConfig.DeltaGrace, + a.PublicConfig.DeltaStage, uint8(a.PublicConfig.RMax), + S, oracleIdentities, offC, + a.PublicConfig.MaxDurationQuery, a.PublicConfig.MaxDurationObservation, + 1200*time.Millisecond, + a.PublicConfig.MaxDurationShouldAcceptAttestedReport, + a.PublicConfig.MaxDurationShouldTransmitAcceptedReport, + a.PublicConfig.F, a.PublicConfig.OnchainConfig, + ) +} + +func calculateOCR3ConfigArgs(a *AutomationTest, S []int, oracleIdentities []confighelper.OracleIdentityExtra) ( + signers []types.OnchainPublicKey, + transmitters []types.Account, + f_ uint8, + onchainConfig_ []byte, + offchainConfigVersion uint64, + offchainConfig []byte, + err error, +) { + offC, _ := json.Marshal(ocr2keepers30config.OffchainConfig{ + TargetProbability: a.PluginConfig.TargetProbability, + TargetInRounds: a.PluginConfig.TargetInRounds, + PerformLockoutWindow: a.PluginConfig.PerformLockoutWindow, + GasLimitPerReport: a.PluginConfig.GasLimitPerReport, + GasOverheadPerUpkeep: a.PluginConfig.GasOverheadPerUpkeep, + MinConfirmations: a.PluginConfig.MinConfirmations, + MaxUpkeepBatchSize: a.PluginConfig.MaxUpkeepBatchSize, + }) + + return ocr3.ContractSetConfigArgsForTests( + a.PublicConfig.DeltaProgress, a.PublicConfig.DeltaResend, a.PublicConfig.DeltaInitial, + a.PublicConfig.DeltaRound, a.PublicConfig.DeltaGrace, a.PublicConfig.DeltaCertifiedCommitRequest, + a.PublicConfig.DeltaStage, a.PublicConfig.RMax, + S, oracleIdentities, offC, + a.PublicConfig.MaxDurationQuery, a.PublicConfig.MaxDurationObservation, + a.PublicConfig.MaxDurationShouldAcceptAttestedReport, + a.PublicConfig.MaxDurationShouldTransmitAcceptedReport, + a.PublicConfig.F, a.PublicConfig.OnchainConfig, + ) +} + func (a *AutomationTest) RegisterUpkeeps(upkeepConfigs []UpkeepConfig) ([]common.Hash, error) { var registrarABI *abi.ABI var err error @@ -588,7 +606,7 @@ func (a *AutomationTest) RegisterUpkeeps(upkeepConfigs []UpkeepConfig) ([]common if err != nil { return nil, errors.Join(err, fmt.Errorf("failed to pack registrar request")) } - case ethereum.RegistryVersion_2_1: + case ethereum.RegistryVersion_2_1, ethereum.RegistryVersion_2_2: // 2.1 and 2.2 use the same registrar registrarABI, err = automation_registrar_wrapper2_1.AutomationRegistrarMetaData.GetAbi() if err != nil { return nil, errors.Join(err, fmt.Errorf("failed to get registrar abi")) @@ -603,7 +621,7 @@ func (a *AutomationTest) RegisterUpkeeps(upkeepConfigs []UpkeepConfig) ([]common return nil, errors.Join(err, fmt.Errorf("failed to pack registrar request")) } default: - return nil, fmt.Errorf("v2.0 and v2.1 are the only supported versions") + return nil, fmt.Errorf("v2.0, v2.1, and v2.2 are the only supported versions") } tx, err := a.LinkToken.TransferAndCall(a.Registrar.Address(), upkeepConfig.FundingAmount, registrationRequest) if err != nil { diff --git a/integration-tests/benchmark/keeper_test.go b/integration-tests/benchmark/keeper_test.go index 12a7e4c75aa..5ca52d26a9e 100644 --- a/integration-tests/benchmark/keeper_test.go +++ b/integration-tests/benchmark/keeper_test.go @@ -205,15 +205,21 @@ func addRegistry(config *tc.TestConfig) []eth_contracts.KeeperRegistryVersion { return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_0} case "2_1": return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_1} + case "2_2": + return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_2} case "2_0-1_3": return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_0, eth_contracts.RegistryVersion_1_3} case "2_1-2_0-1_3": return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_1, eth_contracts.RegistryVersion_2_0, eth_contracts.RegistryVersion_1_3} + case "2_2-2_1": + return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_2, eth_contracts.RegistryVersion_2_1} case "2_0-Multiple": return repeatRegistries(eth_contracts.RegistryVersion_2_0, *config.Keeper.Common.NumberOfRegistries) case "2_1-Multiple": return repeatRegistries(eth_contracts.RegistryVersion_2_1, *config.Keeper.Common.NumberOfRegistries) + case "2_2-Multiple": + return repeatRegistries(eth_contracts.RegistryVersion_2_2, *config.Keeper.Common.NumberOfRegistries) default: return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_0} } diff --git a/integration-tests/chaos/automation_chaos_test.go b/integration-tests/chaos/automation_chaos_test.go index 711a3557307..1d1fda451b3 100644 --- a/integration-tests/chaos/automation_chaos_test.go +++ b/integration-tests/chaos/automation_chaos_test.go @@ -126,6 +126,7 @@ func TestAutomationChaos(t *testing.T) { registryVersions := map[string]eth_contracts.KeeperRegistryVersion{ "registry_2_0": eth_contracts.RegistryVersion_2_0, "registry_2_1": eth_contracts.RegistryVersion_2_1, + "registry_2_2": eth_contracts.RegistryVersion_2_2, } for name, registryVersion := range registryVersions { @@ -276,7 +277,7 @@ func TestAutomationChaos(t *testing.T) { actions.CreateOCRKeeperJobs(t, chainlinkNodes, registry.Address(), network.ChainID, 0, registryVersion) nodesWithoutBootstrap := chainlinkNodes[1:] - ocrConfig, err := actions.BuildAutoOCR2ConfigVars(t, nodesWithoutBootstrap, defaultOCRRegistryConfig, registrar.Address(), 30*time.Second) + ocrConfig, err := actions.BuildAutoOCR2ConfigVars(t, nodesWithoutBootstrap, defaultOCRRegistryConfig, registrar.Address(), 30*time.Second, registry.ChainModuleAddress(), registry.ReorgProtectionEnabled()) require.NoError(t, err, "Error building OCR config vars") err = registry.SetConfig(defaultOCRRegistryConfig, ocrConfig) require.NoError(t, err, "Registry config should be be set successfully") diff --git a/integration-tests/contracts/contract_deployer.go b/integration-tests/contracts/contract_deployer.go index 47c654b5d46..f53a709779d 100644 --- a/integration-tests/contracts/contract_deployer.go +++ b/integration-tests/contracts/contract_deployer.go @@ -9,7 +9,6 @@ import ( "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" @@ -21,19 +20,26 @@ 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" + eth_contracts "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" "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" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/arbitrum_module" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_consumer_benchmark" automationForwarderLogic "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_forwarder_logic" registrar21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_registrar_wrapper2_1" + registrylogica22 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_registry_logic_a_wrapper_2_2" + registrylogicb22 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_registry_logic_b_wrapper_2_2" + registry22 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_registry_wrapper_2_2" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/chain_module_base" "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/gethwrappers/generated/functions_billing_registry_events_mock" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/functions_oracle_events_mock" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/gas_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/gas_wrapper_mock" + iregistry22 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_automation_registry_master_wrapper_2_2" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_chain_module" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_consumer_performance_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_consumer_wrapper" @@ -58,21 +64,22 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/mock_ethlink_aggregator_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/mock_gas_aggregator_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/operator_factory" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/optimism_module" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/oracle_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/perform_data_checker_wrapper" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/scroll_module" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/streams_lookup_upkeep_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/test_api_consumer_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/upkeep_counter_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/gethwrappers/generated/vrf_mock_ethlink_aggregator" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/fee_manager" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/reward_manager" "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/gethwrappers/shared/generated/werc20_mock" - - eth_contracts "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" ) // ContractDeployer is an interface for abstracting the contract deployment methods across network implementations @@ -840,7 +847,7 @@ func (e *EthereumContractDeployer) DeployKeeperRegistrar(registryVersion eth_con registrar20: instance.(*keeper_registrar_wrapper2_0.KeeperRegistrar), address: address, }, err - } else if registryVersion == eth_contracts.RegistryVersion_2_1 { + } else if registryVersion == eth_contracts.RegistryVersion_2_1 || registryVersion == eth_contracts.RegistryVersion_2_2 { // both 2.1 and 2.2 registry use registrar 2.1 // deploy registrar 2.1 address, _, instance, err := e.client.DeployContract("AutomationRegistrar", func( opts *bind.TransactOpts, @@ -1139,21 +1146,11 @@ func (e *EthereumContractDeployer) DeployKeeperRegistry( }, err case eth_contracts.RegistryVersion_2_1: - automationForwarderLogicAddr, _, _, err := e.client.DeployContract("automationForwarderLogic", func( - auth *bind.TransactOpts, - backend bind.ContractBackend, - ) (common.Address, *types.Transaction, interface{}, error) { - return automationForwarderLogic.DeployAutomationForwarderLogic(auth, backend) - }) - + automationForwarderLogicAddr, err := deployAutomationForwarderLogic(e.client) if err != nil { return nil, err } - if err := e.client.WaitForEvents(); err != nil { - return nil, err - } - registryLogicBAddr, _, _, err := e.client.DeployContract("KeeperRegistryLogicB2_1", func( auth *bind.TransactOpts, backend bind.ContractBackend, @@ -1225,11 +1222,162 @@ func (e *EthereumContractDeployer) DeployKeeperRegistry( registry2_1: registryMaster, address: address, }, err + case eth_contracts.RegistryVersion_2_2: + var chainModuleAddr *common.Address + var err error + chainId := e.client.GetChainID().Int64() + + if chainId == 534352 || chainId == 534351 { // Scroll / Scroll Sepolia + chainModuleAddr, _, _, err = e.client.DeployContract("ScrollModule", func( + auth *bind.TransactOpts, + backend bind.ContractBackend, + ) (common.Address, *types.Transaction, interface{}, error) { + return scroll_module.DeployScrollModule(auth, backend) + }) + } else if chainId == 42161 || chainId == 421614 || chainId == 421613 { // Arbitrum One / Sepolia / Goerli + chainModuleAddr, _, _, err = e.client.DeployContract("ArbitrumModule", func( + auth *bind.TransactOpts, + backend bind.ContractBackend, + ) (common.Address, *types.Transaction, interface{}, error) { + return arbitrum_module.DeployArbitrumModule(auth, backend) + }) + } else if chainId == 10 || chainId == 11155420 { // Optimism / Optimism Sepolia + chainModuleAddr, _, _, err = e.client.DeployContract("OptimismModule", func( + auth *bind.TransactOpts, + backend bind.ContractBackend, + ) (common.Address, *types.Transaction, interface{}, error) { + return optimism_module.DeployOptimismModule(auth, backend) + }) + } else { + chainModuleAddr, _, _, err = e.client.DeployContract("ChainModuleBase", func( + auth *bind.TransactOpts, + backend bind.ContractBackend, + ) (common.Address, *types.Transaction, interface{}, error) { + return chain_module_base.DeployChainModuleBase(auth, backend) + }) + } + if err != nil { + return nil, err + } + if err = e.client.WaitForEvents(); err != nil { + return nil, err + } + + automationForwarderLogicAddr, err := deployAutomationForwarderLogic(e.client) + if err != nil { + return nil, err + } + + var allowedReadOnlyAddress common.Address + if chainId == 1101 || chainId == 1442 || chainId == 2442 { + allowedReadOnlyAddress = common.HexToAddress("0x1111111111111111111111111111111111111111") + } else { + allowedReadOnlyAddress = common.HexToAddress("0x0000000000000000000000000000000000000000") + } + registryLogicBAddr, _, _, err := e.client.DeployContract("AutomationRegistryLogicB2_2", func( + auth *bind.TransactOpts, + backend bind.ContractBackend, + ) (common.Address, *types.Transaction, interface{}, error) { + + return registrylogicb22.DeployAutomationRegistryLogicB( + auth, + backend, + common.HexToAddress(opts.LinkAddr), + common.HexToAddress(opts.ETHFeedAddr), + common.HexToAddress(opts.GasFeedAddr), + *automationForwarderLogicAddr, + allowedReadOnlyAddress, + ) + }) + if err != nil { + return nil, err + } + + if err := e.client.WaitForEvents(); err != nil { + return nil, err + } + + registryLogicAAddr, _, _, err := e.client.DeployContract("AutomationRegistryLogicA2_2", func( + auth *bind.TransactOpts, + backend bind.ContractBackend, + ) (common.Address, *types.Transaction, interface{}, error) { + + return registrylogica22.DeployAutomationRegistryLogicA( + auth, + backend, + *registryLogicBAddr, + ) + }) + if err != nil { + return nil, err + } + if err := e.client.WaitForEvents(); err != nil { + return nil, err + } + + address, _, _, err := e.client.DeployContract("AutomationRegistry2_2", func( + auth *bind.TransactOpts, + backend bind.ContractBackend, + ) (common.Address, *types.Transaction, interface{}, error) { + return registry22.DeployAutomationRegistry( + auth, + backend, + *registryLogicAAddr, + ) + }) + if err != nil { + return nil, err + } + if err := e.client.WaitForEvents(); err != nil { + return nil, err + } + + registryMaster, err := iregistry22.NewIAutomationRegistryMaster( + *address, + e.client.Backend(), + ) + if err != nil { + return nil, err + } + + chainModule, err := i_chain_module.NewIChainModule( + *chainModuleAddr, + e.client.Backend(), + ) + if err != nil { + return nil, err + } + + return &EthereumKeeperRegistry{ + client: e.client, + version: eth_contracts.RegistryVersion_2_2, + registry2_2: registryMaster, + chainModule: chainModule, + address: address, + }, err default: return nil, fmt.Errorf("keeper registry version %d is not supported", opts.RegistryVersion) } } +func deployAutomationForwarderLogic(client blockchain.EVMClient) (*common.Address, error) { + automationForwarderLogicAddr, _, _, err := client.DeployContract("automationForwarderLogic", func( + auth *bind.TransactOpts, + backend bind.ContractBackend, + ) (common.Address, *types.Transaction, interface{}, error) { + return automationForwarderLogic.DeployAutomationForwarderLogic(auth, backend) + }) + + if err != nil { + return nil, err + } + + if err := client.WaitForEvents(); err != nil { + return nil, err + } + return automationForwarderLogicAddr, nil +} + // LoadKeeperRegistry returns deployed on given address EthereumKeeperRegistry func (e *EthereumContractDeployer) LoadKeeperRegistry(address common.Address, registryVersion eth_contracts.KeeperRegistryVersion) (KeeperRegistry, error) { switch registryVersion { @@ -1313,6 +1461,22 @@ func (e *EthereumContractDeployer) LoadKeeperRegistry(address common.Address, re registry2_1: instance.(*iregistry21.IKeeperRegistryMaster), version: registryVersion, }, err + case eth_contracts.RegistryVersion_2_2: // why the contract name is not the same as the actual contract name? + instance, err := e.client.LoadContract("AutomationRegistry", address, func( + address common.Address, + backend bind.ContractBackend, + ) (interface{}, error) { + return iregistry22.NewIAutomationRegistryMaster(address, backend) + }) + if err != nil { + return nil, err + } + return &EthereumKeeperRegistry{ + address: &address, + client: e.client, + registry2_2: instance.(*iregistry22.IAutomationRegistryMaster), + version: registryVersion, + }, err default: return nil, fmt.Errorf("keeper registry version %d is not supported", registryVersion) } diff --git a/integration-tests/contracts/ethereum/KeeperRegistryVersions.go b/integration-tests/contracts/ethereum/KeeperRegistryVersions.go index f15e43524d0..4aee8d75d21 100644 --- a/integration-tests/contracts/ethereum/KeeperRegistryVersions.go +++ b/integration-tests/contracts/ethereum/KeeperRegistryVersions.go @@ -18,4 +18,5 @@ const ( RegistryVersion_1_3 RegistryVersion_2_0 RegistryVersion_2_1 + RegistryVersion_2_2 ) diff --git a/integration-tests/contracts/ethereum_keeper_contracts.go b/integration-tests/contracts/ethereum_keeper_contracts.go index a15fcfc7aea..3622edcef5f 100644 --- a/integration-tests/contracts/ethereum_keeper_contracts.go +++ b/integration-tests/contracts/ethereum_keeper_contracts.go @@ -19,12 +19,20 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" + "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_registry_wrapper_2_2" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_2" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_automation_registry_master_wrapper_2_2" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_chain_module" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" + "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/keeper_registrar_wrapper1_2" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registrar_wrapper2_0" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper1_1" @@ -37,17 +45,13 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/perform_data_checker_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/streams_lookup_upkeep_wrapper" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/upkeep_counter_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/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" ) -var utilsABI = cltypes.MustGetABI(automation_utils_2_1.AutomationUtilsABI) +var utilsABI21 = cltypes.MustGetABI(automation_utils_2_1.AutomationUtilsABI) +var utilsABI22 = cltypes.MustGetABI(automation_utils_2_2.AutomationUtilsABI) var registrarABI = cltypes.MustGetABI(registrar21.AutomationRegistrarABI) type KeeperRegistrar interface { @@ -87,6 +91,8 @@ type KeeperRegistry interface { SetUpkeepTriggerConfig(id *big.Int, triggerConfig []byte) error SetUpkeepPrivilegeConfig(id *big.Int, privilegeConfig []byte) error RegistryOwnerAddress() common.Address + ChainModuleAddress() common.Address + ReorgProtectionEnabled() bool } type KeeperConsumer interface { @@ -212,10 +218,25 @@ type EthereumKeeperRegistry struct { registry1_3 *keeper_registry_wrapper1_3.KeeperRegistry registry2_0 *keeper_registry_wrapper2_0.KeeperRegistry registry2_1 *i_keeper_registry_master_wrapper_2_1.IKeeperRegistryMaster + registry2_2 *i_automation_registry_master_wrapper_2_2.IAutomationRegistryMaster + chainModule *i_chain_module.IChainModule address *common.Address l zerolog.Logger } +func (v *EthereumKeeperRegistry) ReorgProtectionEnabled() bool { + chainId := v.client.GetChainID().Uint64() + // reorg protection is disabled in polygon zkEVM and Scroll bc currently there is no way to get the block hash onchain + return v.version != ethereum.RegistryVersion_2_2 || (chainId != 1101 && chainId != 1442 && chainId != 2442 && chainId != 534352 && chainId != 534351) +} + +func (v *EthereumKeeperRegistry) ChainModuleAddress() common.Address { + if v.version == ethereum.RegistryVersion_2_2 { + return v.chainModule.Address() + } + return common.Address{} +} + func (v *EthereumKeeperRegistry) Address() string { return v.address.Hex() } @@ -228,7 +249,7 @@ func (v *EthereumKeeperRegistry) Fund(ethAmount *big.Float) error { return v.client.Fund(v.address.Hex(), ethAmount, gasEstimates) } -func (rcs *KeeperRegistrySettings) EncodeOnChainConfig(registrar string, registryOwnerAddress common.Address) ([]byte, error) { +func (rcs *KeeperRegistrySettings) EncodeOnChainConfig(registrar string, registryOwnerAddress, chainModuleAddress common.Address, reorgProtectionEnabled bool) ([]byte, error) { if rcs.RegistryVersion == ethereum.RegistryVersion_2_1 { onchainConfigStruct := registry21.KeeperRegistryBase21OnchainConfig{ PaymentPremiumPPB: rcs.PaymentPremiumPPB, @@ -248,9 +269,11 @@ func (rcs *KeeperRegistrySettings) EncodeOnChainConfig(registrar string, registr UpkeepPrivilegeManager: registryOwnerAddress, } - encodedOnchainConfig, err := utilsABI.Methods["_onChainConfig"].Inputs.Pack(&onchainConfigStruct) + encodedOnchainConfig, err := utilsABI21.Methods["_onChainConfig"].Inputs.Pack(&onchainConfigStruct) return encodedOnchainConfig, err + } else if rcs.RegistryVersion == ethereum.RegistryVersion_2_2 { + return rcs.encode22OnchainConfig(registrar, registryOwnerAddress, chainModuleAddress, reorgProtectionEnabled) } configType := goabi.MustNewType("tuple(uint32 paymentPremiumPPB,uint32 flatFeeMicroLink,uint32 checkGasLimit,uint24 stalenessSeconds,uint16 gasCeilingMultiplier,uint96 minUpkeepSpend,uint32 maxPerformGas,uint32 maxCheckDataSize,uint32 maxPerformDataSize,uint256 fallbackGasPrice,uint256 fallbackLinkPrice,address transcoder,address registrar)") onchainConfig, err := goabi.Encode(map[string]interface{}{ @@ -272,6 +295,32 @@ func (rcs *KeeperRegistrySettings) EncodeOnChainConfig(registrar string, registr } +func (rcs *KeeperRegistrySettings) encode22OnchainConfig(registrar string, registryOwnerAddress, chainModuleAddr common.Address, reorgProtectionEnabled bool) ([]byte, error) { + onchainConfigStruct := automation_registry_wrapper_2_2.AutomationRegistryBase22OnchainConfig{ + PaymentPremiumPPB: rcs.PaymentPremiumPPB, + FlatFeeMicroLink: rcs.FlatFeeMicroLINK, + CheckGasLimit: rcs.CheckGasLimit, + StalenessSeconds: rcs.StalenessSeconds, + GasCeilingMultiplier: rcs.GasCeilingMultiplier, + MinUpkeepSpend: rcs.MinUpkeepSpend, + MaxPerformGas: rcs.MaxPerformGas, + MaxCheckDataSize: rcs.MaxCheckDataSize, + MaxPerformDataSize: rcs.MaxPerformDataSize, + MaxRevertDataSize: uint32(1000), + FallbackGasPrice: rcs.FallbackGasPrice, + FallbackLinkPrice: rcs.FallbackLinkPrice, + Transcoder: common.Address{}, + Registrars: []common.Address{common.HexToAddress(registrar)}, + UpkeepPrivilegeManager: registryOwnerAddress, + ChainModule: chainModuleAddr, + ReorgProtectionEnabled: reorgProtectionEnabled, + } + + encodedOnchainConfig, err := utilsABI22.Methods["_onChainConfig"].Inputs.Pack(&onchainConfigStruct) + + return encodedOnchainConfig, err +} + func (v *EthereumKeeperRegistry) RegistryOwnerAddress() common.Address { callOpts := &bind.CallOpts{ Pending: false, @@ -279,6 +328,9 @@ func (v *EthereumKeeperRegistry) RegistryOwnerAddress() common.Address { //nolint: exhaustive switch v.version { + case ethereum.RegistryVersion_2_2: + ownerAddress, _ := v.registry2_2.Owner(callOpts) + return ownerAddress case ethereum.RegistryVersion_2_1: ownerAddress, _ := v.registry2_1.Owner(callOpts) return ownerAddress @@ -394,11 +446,28 @@ func (v *EthereumKeeperRegistry) SetConfig(config KeeperRegistrySettings, ocrCon return err } return v.client.ProcessTransaction(tx) + case ethereum.RegistryVersion_2_2: + return v.setConfig22(txOpts, ocrConfig) } return fmt.Errorf("keeper registry version %d is not supported", v.version) } +func (v *EthereumKeeperRegistry) setConfig22(txOpts *bind.TransactOpts, ocrConfig OCRv2Config) error { + tx, err := v.registry2_2.SetConfig(txOpts, + ocrConfig.Signers, + ocrConfig.Transmitters, + ocrConfig.F, + ocrConfig.OnchainConfig, + ocrConfig.OffchainConfigVersion, + ocrConfig.OffchainConfig, + ) + if err != nil { + return err + } + return v.client.ProcessTransaction(tx) +} + // Pause pauses the registry. func (v *EthereumKeeperRegistry) Pause() error { txOpts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) @@ -439,6 +508,12 @@ func (v *EthereumKeeperRegistry) Pause() error { return err } return v.client.ProcessTransaction(tx) + case ethereum.RegistryVersion_2_2: + tx, err = v.registry2_2.Pause(txOpts) + if err != nil { + return err + } + return v.client.ProcessTransaction(tx) } return fmt.Errorf("keeper registry version %d is not supported", v.version) @@ -552,6 +627,8 @@ func (v *EthereumKeeperRegistry) AddUpkeepFunds(id *big.Int, amount *big.Int) er tx, err = v.registry2_0.AddFunds(opts, id, amount) case ethereum.RegistryVersion_2_1: tx, err = v.registry2_1.AddFunds(opts, id, amount) + case ethereum.RegistryVersion_2_2: + tx, err = v.registry2_2.AddFunds(opts, id, amount) } if err != nil { @@ -644,11 +721,32 @@ func (v *EthereumKeeperRegistry) GetUpkeepInfo(ctx context.Context, id *big.Int) Paused: uk.Paused, OffchainConfig: uk.OffchainConfig, }, nil + case ethereum.RegistryVersion_2_2: + return v.getUpkeepInfo22(opts, id) } return nil, fmt.Errorf("keeper registry version %d is not supported", v.version) } +func (v *EthereumKeeperRegistry) getUpkeepInfo22(opts *bind.CallOpts, id *big.Int) (*UpkeepInfo, error) { + uk, err := v.registry2_2.GetUpkeep(opts, id) + if err != nil { + return nil, err + } + return &UpkeepInfo{ + Target: uk.Target.Hex(), + ExecuteGas: uk.PerformGas, + CheckData: uk.CheckData, + Balance: uk.Balance, + Admin: uk.Admin.Hex(), + MaxValidBlocknumber: uk.MaxValidBlocknumber, + LastPerformBlockNumber: uk.LastPerformedBlockNumber, + AmountSpent: uk.AmountSpent, + Paused: uk.Paused, + OffchainConfig: uk.OffchainConfig, + }, nil +} + func (v *EthereumKeeperRegistry) GetKeeperInfo(ctx context.Context, keeperAddr string) (*KeeperInfo, error) { opts := &bind.CallOpts{ From: common.HexToAddress(v.client.GetDefaultWallet().Address()), @@ -668,7 +766,7 @@ func (v *EthereumKeeperRegistry) GetKeeperInfo(ctx context.Context, keeperAddr s info, err = v.registry1_2.GetKeeperInfo(opts, common.HexToAddress(keeperAddr)) case ethereum.RegistryVersion_1_3: info, err = v.registry1_3.GetKeeperInfo(opts, common.HexToAddress(keeperAddr)) - case ethereum.RegistryVersion_2_0, ethereum.RegistryVersion_2_1: + case ethereum.RegistryVersion_2_0, ethereum.RegistryVersion_2_1, ethereum.RegistryVersion_2_2: // this is not used anywhere return nil, fmt.Errorf("not supported") } @@ -714,7 +812,7 @@ func (v *EthereumKeeperRegistry) SetKeepers(keepers []string, payees []string, o ocrConfig.OffchainConfigVersion, ocrConfig.OffchainConfig, ) - case ethereum.RegistryVersion_2_1: + case ethereum.RegistryVersion_2_1, ethereum.RegistryVersion_2_2: return fmt.Errorf("not supported") } @@ -766,7 +864,7 @@ func (v *EthereumKeeperRegistry) RegisterUpkeep(target string, gasLimit uint32, checkData, nil, //offchain config ) - case ethereum.RegistryVersion_2_1: + case ethereum.RegistryVersion_2_1, ethereum.RegistryVersion_2_2: return fmt.Errorf("not supported") } @@ -810,6 +908,11 @@ func (v *EthereumKeeperRegistry) CancelUpkeep(id *big.Int) error { if err != nil { return err } + case ethereum.RegistryVersion_2_2: + tx, err = v.registry2_2.CancelUpkeep(opts, id) + if err != nil { + return err + } } v.l.Info(). @@ -849,6 +952,11 @@ func (v *EthereumKeeperRegistry) SetUpkeepGasLimit(id *big.Int, gas uint32) erro if err != nil { return err } + case ethereum.RegistryVersion_2_2: + tx, err = v.registry2_2.SetUpkeepGasLimit(opts, id, gas) + if err != nil { + return err + } default: return fmt.Errorf("keeper registry version %d is not supported for SetUpkeepGasLimit", v.version) } @@ -885,7 +993,7 @@ func (v *EthereumKeeperRegistry) GetKeeperList(ctx context.Context) ([]string, e return []string{}, err } list = state.Transmitters - case ethereum.RegistryVersion_2_1: + case ethereum.RegistryVersion_2_1, ethereum.RegistryVersion_2_2: return nil, fmt.Errorf("not supported") } @@ -936,6 +1044,17 @@ func (v *EthereumKeeperRegistry) UpdateCheckData(id *big.Int, newCheckData []byt return err } return v.client.ProcessTransaction(tx) + case ethereum.RegistryVersion_2_2: + opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) + if err != nil { + return err + } + + tx, err := v.registry2_2.SetUpkeepCheckData(opts, id, newCheckData) + if err != nil { + return err + } + return v.client.ProcessTransaction(tx) default: return fmt.Errorf("UpdateCheckData is not supported by keeper registry version %d", v.version) } @@ -956,6 +1075,17 @@ func (v *EthereumKeeperRegistry) SetUpkeepTriggerConfig(id *big.Int, triggerConf return err } return v.client.ProcessTransaction(tx) + case ethereum.RegistryVersion_2_2: + opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) + if err != nil { + return err + } + + tx, err := v.registry2_2.SetUpkeepTriggerConfig(opts, id, triggerConfig) + if err != nil { + return err + } + return v.client.ProcessTransaction(tx) default: return fmt.Errorf("SetUpkeepTriggerConfig is not supported by keeper registry version %d", v.version) } @@ -976,6 +1106,17 @@ func (v *EthereumKeeperRegistry) SetUpkeepPrivilegeConfig(id *big.Int, privilege return err } return v.client.ProcessTransaction(tx) + case ethereum.RegistryVersion_2_2: + opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) + if err != nil { + return err + } + + tx, err := v.registry2_2.SetUpkeepPrivilegeConfig(opts, id, privilegeConfig) + if err != nil { + return err + } + return v.client.ProcessTransaction(tx) default: return fmt.Errorf("SetUpkeepPrivilegeConfig is not supported by keeper registry version %d", v.version) } @@ -1017,6 +1158,17 @@ func (v *EthereumKeeperRegistry) PauseUpkeep(id *big.Int) error { return err } return v.client.ProcessTransaction(tx) + case ethereum.RegistryVersion_2_2: + opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) + if err != nil { + return err + } + + tx, err := v.registry2_2.PauseUpkeep(opts, id) + if err != nil { + return err + } + return v.client.ProcessTransaction(tx) default: return fmt.Errorf("PauseUpkeep is not supported by keeper registry version %d", v.version) } @@ -1058,6 +1210,17 @@ func (v *EthereumKeeperRegistry) UnpauseUpkeep(id *big.Int) error { return err } return v.client.ProcessTransaction(tx) + case ethereum.RegistryVersion_2_2: + opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) + if err != nil { + return err + } + + tx, err := v.registry2_2.UnpauseUpkeep(opts, id) + if err != nil { + return err + } + return v.client.ProcessTransaction(tx) default: return fmt.Errorf("UnpauseUpkeep is not supported by keeper registry version %d", v.version) } @@ -1116,6 +1279,16 @@ func (v *EthereumKeeperRegistry) ParseUpkeepPerformedLog(log *types.Log) (*Upkee Success: parsedLog.Success, From: utils.ZeroAddress, }, nil + case ethereum.RegistryVersion_2_2: + parsedLog, err := v.registry2_2.ParseUpkeepPerformed(*log) + if err != nil { + return nil, err + } + return &UpkeepPerformedLog{ + Id: parsedLog.Id, + Success: parsedLog.Success, + From: utils.ZeroAddress, + }, nil } return nil, fmt.Errorf("keeper registry version %d is not supported", v.version) } @@ -1140,6 +1313,14 @@ func (v *EthereumKeeperRegistry) ParseStaleUpkeepReportLog(log *types.Log) (*Sta return &StaleUpkeepReportLog{ Id: parsedLog.Id, }, nil + case ethereum.RegistryVersion_2_2: + parsedLog, err := v.registry2_2.ParseStaleUpkeepReport(*log) + if err != nil { + return nil, err + } + return &StaleUpkeepReportLog{ + Id: parsedLog.Id, + }, nil } return nil, fmt.Errorf("keeper registry version %d is not supported", v.version) } @@ -1177,6 +1358,12 @@ func (v *EthereumKeeperRegistry) ParseUpkeepIdFromRegisteredLog(log *types.Log) return nil, err } return parsedLog.Id, nil + case ethereum.RegistryVersion_2_2: + parsedLog, err := v.registry2_2.ParseUpkeepRegistered(*log) + if err != nil { + return nil, err + } + return parsedLog.Id, nil } return nil, fmt.Errorf("keeper registry version %d is not supported", v.version) @@ -2057,7 +2244,7 @@ func (v *EthereumKeeperRegistrar) EncodeRegisterRequest(name string, email []byt Topic2: bytes0, Topic3: bytes0, } - encodedLogTriggerConfig, err := utilsABI.Methods["_logTriggerConfig"].Inputs.Pack(&logTriggerConfigStruct) + encodedLogTriggerConfig, err := utilsABI21.Methods["_logTriggerConfig"].Inputs.Pack(&logTriggerConfigStruct) if err != nil { return nil, err } diff --git a/integration-tests/reorg/automation_reorg_test.go b/integration-tests/reorg/automation_reorg_test.go index 36b5d3eb985..e889a1c6123 100644 --- a/integration-tests/reorg/automation_reorg_test.go +++ b/integration-tests/reorg/automation_reorg_test.go @@ -25,7 +25,6 @@ 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" ) @@ -129,14 +128,16 @@ 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, + "registry_2_2_conditional": ethereum.RegistryVersion_2_2, + "registry_2_2_logtrigger": ethereum.RegistryVersion_2_2, } - for name, registryVersion := range registryVersions { - name := name - registryVersion := registryVersion + for n, rv := range registryVersions { + name := n + registryVersion := rv t.Run(name, func(t *testing.T) { t.Parallel() config, err := tc.GetConfig("Reorg", tc.Automation) @@ -213,7 +214,7 @@ func TestAutomationReorg(t *testing.T) { actions.CreateOCRKeeperJobs(t, chainlinkNodes, registry.Address(), network.ChainID, 0, registryVersion) nodesWithoutBootstrap := chainlinkNodes[1:] - ocrConfig, err := actions.BuildAutoOCR2ConfigVars(t, nodesWithoutBootstrap, defaultOCRRegistryConfig, registrar.Address(), 5*time.Second) + ocrConfig, err := actions.BuildAutoOCR2ConfigVars(t, nodesWithoutBootstrap, defaultOCRRegistryConfig, registrar.Address(), 5*time.Second, registry.ChainModuleAddress(), registry.ReorgProtectionEnabled()) require.NoError(t, err, "OCR2 config should be built successfully") err = registry.SetConfig(defaultOCRRegistryConfig, ocrConfig) require.NoError(t, err, "Registry config should be be set successfully") diff --git a/integration-tests/smoke/automation_test.go b/integration-tests/smoke/automation_test.go index 3f69e7f829c..c51b90c1f57 100644 --- a/integration-tests/smoke/automation_test.go +++ b/integration-tests/smoke/automation_test.go @@ -10,37 +10,34 @@ import ( "testing" "time" - ctfTestEnv "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" - - ocr3 "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" - - ocr2keepers30config "github.com/smartcontractkit/chainlink-automation/pkg/v3/config" - - "github.com/smartcontractkit/chainlink/integration-tests/actions/automationv2" - 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" "github.com/stretchr/testify/require" + ocr3 "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" + + ocr2keepers30config "github.com/smartcontractkit/chainlink-automation/pkg/v3/config" + commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" + ctfTestEnv "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-testing-framework/utils/testcontext" - commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/integration-tests/actions" + "github.com/smartcontractkit/chainlink/integration-tests/actions/automationv2" "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" + "github.com/smartcontractkit/chainlink/integration-tests/types" "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) +var utilsABI21 = cltypes.MustGetABI(automation_utils_2_1.AutomationUtilsABI) const ( automationDefaultUpkeepGasLimit = uint32(2500000) @@ -96,11 +93,16 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool, automationTestConfig t "registry_2_1_with_mercury_v02": ethereum.RegistryVersion_2_1, "registry_2_1_with_mercury_v03": ethereum.RegistryVersion_2_1, "registry_2_1_with_logtrigger_and_mercury_v02": ethereum.RegistryVersion_2_1, + "registry_2_2_conditional": ethereum.RegistryVersion_2_2, + "registry_2_2_logtrigger": ethereum.RegistryVersion_2_2, + "registry_2_2_with_mercury_v02": ethereum.RegistryVersion_2_2, + "registry_2_2_with_mercury_v03": ethereum.RegistryVersion_2_2, + "registry_2_2_with_logtrigger_and_mercury_v02": ethereum.RegistryVersion_2_2, } - for name, registryVersion := range registryVersions { - name := name - registryVersion := registryVersion + for n, rv := range registryVersions { + name := n + registryVersion := rv t.Run(name, func(t *testing.T) { cfg := tc.MustCopy(automationTestConfig) t.Parallel() @@ -114,9 +116,9 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool, automationTestConfig t } // Use the name to determine if this is a log trigger or mercury - isLogTrigger := name == "registry_2_1_logtrigger" || name == "registry_2_1_with_logtrigger_and_mercury_v02" - isMercuryV02 := name == "registry_2_1_with_mercury_v02" || name == "registry_2_1_with_logtrigger_and_mercury_v02" - isMercuryV03 := name == "registry_2_1_with_mercury_v03" + isLogTrigger := name == "registry_2_1_logtrigger" || name == "registry_2_1_with_logtrigger_and_mercury_v02" || name == "registry_2_2_logtrigger" || name == "registry_2_2_with_logtrigger_and_mercury_v02" + isMercuryV02 := name == "registry_2_1_with_mercury_v02" || name == "registry_2_1_with_logtrigger_and_mercury_v02" || name == "registry_2_2_with_mercury_v02" || name == "registry_2_2_with_logtrigger_and_mercury_v02" + isMercuryV03 := name == "registry_2_1_with_mercury_v03" || name == "registry_2_2_with_mercury_v03" isMercury := isMercuryV02 || isMercuryV03 a := setupAutomationTestDocker( @@ -234,172 +236,184 @@ func TestSetUpkeepTriggerConfig(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - config, err := tc.GetConfig("Smoke", tc.Automation) - if err != nil { - t.Fatal(err) + registryVersions := map[string]ethereum.KeeperRegistryVersion{ + "registry_2_1": ethereum.RegistryVersion_2_1, + "registry_2_2": ethereum.RegistryVersion_2_2, } - a := setupAutomationTestDocker( - t, ethereum.RegistryVersion_2_1, automationDefaultRegistryConfig, false, false, &config, - ) - - consumers, upkeepIDs := actions.DeployConsumers( - t, - a.Registry, - a.Registrar, - a.LinkToken, - a.Deployer, - a.ChainClient, - defaultAmountOfUpkeeps, - big.NewInt(automationDefaultLinkFunds), - automationDefaultUpkeepGasLimit, - true, - false, - ) - - // Start log trigger based upkeeps for all consumers - for i := 0; i < len(consumers); i++ { - err := consumers[i].Start() - if err != nil { - return - } - } + for n, rv := range registryVersions { + name := n + registryVersion := rv + t.Run(name, func(t *testing.T) { + t.Parallel() + config, err := tc.GetConfig("Smoke", tc.Automation) + if err != nil { + t.Fatal(err) + } - l.Info().Msg("Waiting for all upkeeps to perform") - gom := gomega.NewGomegaWithT(t) - gom.Eventually(func(g gomega.Gomega) { - // Check if the upkeeps are performing multiple times by analyzing their counters - for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(testcontext.Get(t)) - require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) - expect := 5 - l.Info().Int64("Upkeeps Performed", counter.Int64()).Int("Upkeep Index", i).Msg("Number of upkeeps performed") - 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 - - topic0InBytesMatch := [32]byte{ - 61, 83, 163, 149, 80, 224, 70, 136, - 6, 88, 39, 243, 187, 134, 88, 76, - 176, 7, 171, 158, 188, 167, 235, - 213, 40, 231, 48, 28, 156, 49, 235, 93, - } // bytes representation of 0x3d53a39550e04688065827f3bb86584cb007ab9ebca7ebd528e7301c9c31eb5d - - topic0InBytesNoMatch := [32]byte{ - 62, 83, 163, 149, 80, 224, 70, 136, - 6, 88, 39, 243, 187, 134, 88, 76, - 176, 7, 171, 158, 188, 167, 235, - 213, 40, 231, 48, 28, 156, 49, 235, 93, - } // changed the first byte from 61 to 62 to make it not match - - bytes0 := [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, 0, - } // bytes representation of 0x0000000000000000000000000000000000000000000000000000000000000000 - - // Update the trigger config so no upkeeps are triggered - for i := 0; i < len(consumers); i++ { - upkeepAddr := consumers[i].Address() - - logTriggerConfigStruct := automation_utils_2_1.LogTriggerConfig{ - ContractAddress: common.HexToAddress(upkeepAddr), - FilterSelector: 0, - Topic0: topic0InBytesNoMatch, - Topic1: bytes0, - Topic2: bytes0, - Topic3: bytes0, - } - encodedLogTriggerConfig, err := utilsABI.Methods["_logTriggerConfig"].Inputs.Pack(&logTriggerConfigStruct) - if err != nil { - return - } + a := setupAutomationTestDocker( + t, registryVersion, automationDefaultRegistryConfig, false, false, &config, + ) - err = a.Registry.SetUpkeepTriggerConfig(upkeepIDs[i], encodedLogTriggerConfig) - require.NoError(t, err, "Could not set upkeep trigger config at index %d", i) - } + consumers, upkeepIDs := actions.DeployConsumers( + t, + a.Registry, + a.Registrar, + a.LinkToken, + a.Deployer, + a.ChainClient, + defaultAmountOfUpkeeps, + big.NewInt(automationDefaultLinkFunds), + automationDefaultUpkeepGasLimit, + true, + false, + ) + + // Start log trigger based upkeeps for all consumers + for i := 0; i < len(consumers); i++ { + err := consumers[i].Start() + if err != nil { + return + } + } + + l.Info().Msg("Waiting for all upkeeps to perform") + gom := gomega.NewGomegaWithT(t) + gom.Eventually(func(g gomega.Gomega) { + // Check if the upkeeps are performing multiple times by analyzing their counters + for i := 0; i < len(upkeepIDs); i++ { + counter, err := consumers[i].Counter(testcontext.Get(t)) + require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) + expect := 5 + l.Info().Int64("Upkeeps Performed", counter.Int64()).Int("Upkeep Index", i).Msg("Number of upkeeps performed") + 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 - err = a.ChainClient.WaitForEvents() - require.NoError(t, err, "Error encountered when waiting for setting trigger config for upkeeps") + topic0InBytesMatch := [32]byte{ + 61, 83, 163, 149, 80, 224, 70, 136, + 6, 88, 39, 243, 187, 134, 88, 76, + 176, 7, 171, 158, 188, 167, 235, + 213, 40, 231, 48, 28, 156, 49, 235, 93, + } // bytes representation of 0x3d53a39550e04688065827f3bb86584cb007ab9ebca7ebd528e7301c9c31eb5d + + topic0InBytesNoMatch := [32]byte{ + 62, 83, 163, 149, 80, 224, 70, 136, + 6, 88, 39, 243, 187, 134, 88, 76, + 176, 7, 171, 158, 188, 167, 235, + 213, 40, 231, 48, 28, 156, 49, 235, 93, + } // changed the first byte from 61 to 62 to make it not match + + bytes0 := [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, 0, + } // bytes representation of 0x0000000000000000000000000000000000000000000000000000000000000000 + + // Update the trigger config so no upkeeps are triggered + for i := 0; i < len(consumers); i++ { + upkeepAddr := consumers[i].Address() + + logTriggerConfigStruct := automation_utils_2_1.LogTriggerConfig{ + ContractAddress: common.HexToAddress(upkeepAddr), + FilterSelector: 0, + Topic0: topic0InBytesNoMatch, + Topic1: bytes0, + Topic2: bytes0, + Topic3: bytes0, + } + encodedLogTriggerConfig, err := utilsABI21.Methods["_logTriggerConfig"].Inputs.Pack(&logTriggerConfigStruct) + if err != nil { + return + } - var countersAfterSetNoMatch = make([]*big.Int, len(upkeepIDs)) + err = a.Registry.SetUpkeepTriggerConfig(upkeepIDs[i], encodedLogTriggerConfig) + require.NoError(t, err, "Could not set upkeep trigger config at index %d", i) + } - // Wait for 10 seconds to let in-flight upkeeps finish - time.Sleep(10 * time.Second) - for i := 0; i < len(upkeepIDs); i++ { - // Obtain the amount of times the upkeep has been executed so far - countersAfterSetNoMatch[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().Int64("Upkeep Count", countersAfterSetNoMatch[i].Int64()).Int("Upkeep Index", i).Msg("Upkeep") - } + err = a.ChainClient.WaitForEvents() + require.NoError(t, err, "Error encountered when waiting for setting trigger config for upkeeps") - l.Info().Msg("Making sure the counter stays consistent") - gom.Consistently(func(g gomega.Gomega) { - for i := 0; i < len(upkeepIDs); i++ { - // Expect the counter to remain constant (At most increase by 2 to account for stale performs) because the upkeep trigger config is not met - bufferCount := int64(2) - latestCounter, 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(latestCounter.Int64()).Should(gomega.BeNumerically("<=", countersAfterSetNoMatch[i].Int64()+bufferCount), - "Expected consumer counter to remain less than or equal to %d, but got %d", - countersAfterSetNoMatch[i].Int64()+bufferCount, latestCounter.Int64()) - } - }, "1m", "1s").Should(gomega.Succeed()) - - // Update the trigger config, so upkeeps start performing again - for i := 0; i < len(consumers); i++ { - upkeepAddr := consumers[i].Address() - - logTriggerConfigStruct := automation_utils_2_1.LogTriggerConfig{ - ContractAddress: common.HexToAddress(upkeepAddr), - FilterSelector: 0, - Topic0: topic0InBytesMatch, - Topic1: bytes0, - Topic2: bytes0, - Topic3: bytes0, - } - encodedLogTriggerConfig, err := utilsABI.Methods["_logTriggerConfig"].Inputs.Pack(&logTriggerConfigStruct) - if err != nil { - return - } + var countersAfterSetNoMatch = make([]*big.Int, len(upkeepIDs)) - err = a.Registry.SetUpkeepTriggerConfig(upkeepIDs[i], encodedLogTriggerConfig) - require.NoError(t, err, "Could not set upkeep trigger config at index %d", i) - } + // Wait for 10 seconds to let in-flight upkeeps finish + time.Sleep(10 * time.Second) + for i := 0; i < len(upkeepIDs); i++ { + // Obtain the amount of times the upkeep has been executed so far + countersAfterSetNoMatch[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().Int64("Upkeep Count", countersAfterSetNoMatch[i].Int64()).Int("Upkeep Index", i).Msg("Upkeep") + } + + l.Info().Msg("Making sure the counter stays consistent") + gom.Consistently(func(g gomega.Gomega) { + for i := 0; i < len(upkeepIDs); i++ { + // Expect the counter to remain constant (At most increase by 2 to account for stale performs) because the upkeep trigger config is not met + bufferCount := int64(2) + latestCounter, 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(latestCounter.Int64()).Should(gomega.BeNumerically("<=", countersAfterSetNoMatch[i].Int64()+bufferCount), + "Expected consumer counter to remain less than or equal to %d, but got %d", + countersAfterSetNoMatch[i].Int64()+bufferCount, latestCounter.Int64()) + } + }, "1m", "1s").Should(gomega.Succeed()) - err = a.ChainClient.WaitForEvents() - require.NoError(t, err, "Error encountered when waiting for setting trigger config for upkeeps") + // Update the trigger config, so upkeeps start performing again + for i := 0; i < len(consumers); i++ { + upkeepAddr := consumers[i].Address() + + logTriggerConfigStruct := automation_utils_2_1.LogTriggerConfig{ + ContractAddress: common.HexToAddress(upkeepAddr), + FilterSelector: 0, + Topic0: topic0InBytesMatch, + Topic1: bytes0, + Topic2: bytes0, + Topic3: bytes0, + } + encodedLogTriggerConfig, err := utilsABI21.Methods["_logTriggerConfig"].Inputs.Pack(&logTriggerConfigStruct) + if err != nil { + return + } - var countersAfterSetMatch = make([]*big.Int, len(upkeepIDs)) + err = a.Registry.SetUpkeepTriggerConfig(upkeepIDs[i], encodedLogTriggerConfig) + require.NoError(t, err, "Could not set upkeep trigger config at index %d", i) + } - for i := 0; i < len(upkeepIDs); i++ { - // Obtain the amount of times the upkeep has been executed so far - countersAfterSetMatch[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().Int64("Upkeep Count", countersAfterSetMatch[i].Int64()).Int("Upkeep Index", i).Msg("Upkeep") - } + err = a.ChainClient.WaitForEvents() + require.NoError(t, err, "Error encountered when waiting for setting trigger config for upkeeps") - // Wait for 30 seconds to make sure backend is ready - time.Sleep(30 * time.Second) - // Start the consumers again - for i := 0; i < len(consumers); i++ { - err := consumers[i].Start() - if err != nil { - return - } - } + var countersAfterSetMatch = make([]*big.Int, len(upkeepIDs)) - l.Info().Msg("Making sure the counter starts increasing again") - gom.Eventually(func(g gomega.Gomega) { - // Check if the upkeeps are performing multiple times by analyzing their counters - for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(testcontext.Get(t)) - require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) - expect := int64(5) - l.Info().Int64("Upkeeps Performed", counter.Int64()).Int("Upkeep Index", i).Msg("Number of upkeeps performed") - g.Expect(counter.Int64()).Should(gomega.BeNumerically(">=", countersAfterSetMatch[i].Int64()+expect), - "Expected consumer counter to be greater than %d, but got %d", countersAfterSetMatch[i].Int64()+expect, counter.Int64()) - } - }, "5m", "1s").Should(gomega.Succeed()) // ~1m for cluster setup, ~2m for performing each upkeep 5 times, ~2m buffer + for i := 0; i < len(upkeepIDs); i++ { + // Obtain the amount of times the upkeep has been executed so far + countersAfterSetMatch[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().Int64("Upkeep Count", countersAfterSetMatch[i].Int64()).Int("Upkeep Index", i).Msg("Upkeep") + } + + // Wait for 30 seconds to make sure backend is ready + time.Sleep(30 * time.Second) + // Start the consumers again + for i := 0; i < len(consumers); i++ { + err := consumers[i].Start() + if err != nil { + return + } + } + + l.Info().Msg("Making sure the counter starts increasing again") + gom.Eventually(func(g gomega.Gomega) { + // Check if the upkeeps are performing multiple times by analyzing their counters + for i := 0; i < len(upkeepIDs); i++ { + counter, err := consumers[i].Counter(testcontext.Get(t)) + require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) + expect := int64(5) + l.Info().Int64("Upkeeps Performed", counter.Int64()).Int("Upkeep Index", i).Msg("Number of upkeeps performed") + g.Expect(counter.Int64()).Should(gomega.BeNumerically(">=", countersAfterSetMatch[i].Int64()+expect), + "Expected consumer counter to be greater than %d, but got %d", countersAfterSetMatch[i].Int64()+expect, counter.Int64()) + } + }, "5m", "1s").Should(gomega.Succeed()) // ~1m for cluster setup, ~2m for performing each upkeep 5 times, ~2m buffer + }) + } } func TestAutomationAddFunds(t *testing.T) { @@ -407,11 +421,12 @@ func TestAutomationAddFunds(t *testing.T) { registryVersions := map[string]ethereum.KeeperRegistryVersion{ "registry_2_0": ethereum.RegistryVersion_2_0, "registry_2_1": ethereum.RegistryVersion_2_1, + "registry_2_2": ethereum.RegistryVersion_2_2, } - for name, registryVersion := range registryVersions { - name := name - registryVersion := registryVersion + for n, rv := range registryVersions { + name := n + registryVersion := rv t.Run(name, func(t *testing.T) { t.Parallel() config, err := tc.GetConfig("Smoke", tc.Automation) @@ -473,6 +488,7 @@ func TestAutomationPauseUnPause(t *testing.T) { registryVersions := map[string]ethereum.KeeperRegistryVersion{ "registry_2_0": ethereum.RegistryVersion_2_0, "registry_2_1": ethereum.RegistryVersion_2_1, + "registry_2_2": ethereum.RegistryVersion_2_2, } for n, rv := range registryVersions { @@ -572,11 +588,12 @@ func TestAutomationRegisterUpkeep(t *testing.T) { registryVersions := map[string]ethereum.KeeperRegistryVersion{ "registry_2_0": ethereum.RegistryVersion_2_0, "registry_2_1": ethereum.RegistryVersion_2_1, + "registry_2_2": ethereum.RegistryVersion_2_2, } - for name, registryVersion := range registryVersions { - name := name - registryVersion := registryVersion + for n, rv := range registryVersions { + name := n + registryVersion := rv t.Run(name, func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) @@ -660,11 +677,12 @@ func TestAutomationPauseRegistry(t *testing.T) { registryVersions := map[string]ethereum.KeeperRegistryVersion{ "registry_2_0": ethereum.RegistryVersion_2_0, "registry_2_1": ethereum.RegistryVersion_2_1, + "registry_2_2": ethereum.RegistryVersion_2_2, } - for name, registryVersion := range registryVersions { - name := name - registryVersion := registryVersion + for n, rv := range registryVersions { + name := n + registryVersion := rv t.Run(name, func(t *testing.T) { t.Parallel() config, err := tc.GetConfig("Smoke", tc.Automation) @@ -733,11 +751,12 @@ func TestAutomationKeeperNodesDown(t *testing.T) { registryVersions := map[string]ethereum.KeeperRegistryVersion{ "registry_2_0": ethereum.RegistryVersion_2_0, "registry_2_1": ethereum.RegistryVersion_2_1, + "registry_2_2": ethereum.RegistryVersion_2_2, } - for name, registryVersion := range registryVersions { - name := name - registryVersion := registryVersion + for n, rv := range registryVersions { + name := n + registryVersion := rv t.Run(name, func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) @@ -837,11 +856,12 @@ func TestAutomationPerformSimulation(t *testing.T) { registryVersions := map[string]ethereum.KeeperRegistryVersion{ "registry_2_0": ethereum.RegistryVersion_2_0, "registry_2_1": ethereum.RegistryVersion_2_1, + "registry_2_2": ethereum.RegistryVersion_2_2, } - for name, registryVersion := range registryVersions { - name := name - registryVersion := registryVersion + for n, rv := range registryVersions { + name := n + registryVersion := rv t.Run(name, func(t *testing.T) { t.Parallel() config, err := tc.GetConfig("Smoke", tc.Automation) @@ -904,11 +924,12 @@ func TestAutomationCheckPerformGasLimit(t *testing.T) { registryVersions := map[string]ethereum.KeeperRegistryVersion{ "registry_2_0": ethereum.RegistryVersion_2_0, "registry_2_1": ethereum.RegistryVersion_2_1, + "registry_2_2": ethereum.RegistryVersion_2_2, } - for name, registryVersion := range registryVersions { - name := name - registryVersion := registryVersion + for n, rv := range registryVersions { + name := n + registryVersion := rv t.Run(name, func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) @@ -997,7 +1018,7 @@ func TestAutomationCheckPerformGasLimit(t *testing.T) { highCheckGasLimit.CheckGasLimit = uint32(5000000) highCheckGasLimit.RegistryVersion = registryVersion - ocrConfig, err := actions.BuildAutoOCR2ConfigVarsLocal(l, nodesWithoutBootstrap, highCheckGasLimit, a.Registrar.Address(), 30*time.Second, a.Registry.RegistryOwnerAddress()) + ocrConfig, err := actions.BuildAutoOCR2ConfigVarsLocal(l, nodesWithoutBootstrap, highCheckGasLimit, a.Registrar.Address(), 30*time.Second, a.Registry.RegistryOwnerAddress(), a.Registry.ChainModuleAddress(), a.Registry.ReorgProtectionEnabled()) require.NoError(t, err, "Error building OCR config") err = a.Registry.SetConfig(highCheckGasLimit, ocrConfig) @@ -1022,11 +1043,12 @@ func TestUpdateCheckData(t *testing.T) { registryVersions := map[string]ethereum.KeeperRegistryVersion{ "registry_2_0": ethereum.RegistryVersion_2_0, "registry_2_1": ethereum.RegistryVersion_2_1, + "registry_2_2": ethereum.RegistryVersion_2_2, } - for name, registryVersion := range registryVersions { - name := name - registryVersion := registryVersion + for n, rv := range registryVersions { + name := n + registryVersion := rv 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 b88684599c7..3e7a82effd3 100644 --- a/integration-tests/smoke/automation_test.go_test_list.json +++ b/integration-tests/smoke/automation_test.go_test_list.json @@ -3,19 +3,31 @@ { "name": "TestAutomationBasic", "label": "ubuntu-latest", - "nodes": 2, + "nodes": 3, "run":[ {"name":"registry_2_0"}, - {"name":"registry_2_1_conditional"} + {"name":"registry_2_1_conditional"}, + {"name":"registry_2_1_logtrigger"} ] }, { "name": "TestAutomationBasic", "label": "ubuntu-latest", - "nodes": 2, + "nodes": 3, "run":[ - {"name":"registry_2_1_logtrigger"}, - {"name":"registry_2_1_with_mercury_v02"} + {"name":"registry_2_1_with_mercury_v02"}, + {"name":"registry_2_1_with_mercury_v03"}, + {"name":"registry_2_1_with_logtrigger_and_mercury_v02"} + ] + }, + { + "name": "TestAutomationBasic", + "label": "ubuntu-latest", + "nodes": 3, + "run":[ + {"name":"registry_2_2_conditional"}, + {"name":"registry_2_2_logtrigger"}, + {"name":"registry_2_2_with_mercury_v02"} ] }, { @@ -23,52 +35,54 @@ "label": "ubuntu-latest", "nodes": 2, "run":[ - {"name":"registry_2_1_with_mercury_v03"}, - {"name":"registry_2_1_with_logtrigger_and_mercury_v02"} + {"name":"registry_2_2_with_mercury_v03"}, + {"name":"registry_2_2_with_logtrigger_and_mercury_v02"} ] }, { - "name": "TestSetUpkeepTriggerConfig" + "name": "TestSetUpkeepTriggerConfig", + "label": "ubuntu-latest", + "nodes": 2 }, { "name": "TestAutomationAddFunds", "label": "ubuntu-latest", - "nodes": 2 + "nodes": 3 }, { "name": "TestAutomationPauseUnPause", "label": "ubuntu-latest", - "nodes": 2 + "nodes": 3 }, { "name": "TestAutomationRegisterUpkeep", "label": "ubuntu-latest", - "nodes": 2 + "nodes": 3 }, { "name": "TestAutomationPauseRegistry", "label": "ubuntu-latest", - "nodes": 2 + "nodes": 3 }, { "name": "TestAutomationKeeperNodesDown", "label": "ubuntu-latest", - "nodes": 2 + "nodes": 3 }, { "name": "TestAutomationPerformSimulation", "label": "ubuntu-latest", - "nodes": 2 + "nodes": 3 }, { "name": "TestAutomationCheckPerformGasLimit", "label": "ubuntu-latest", - "nodes": 2 + "nodes": 3 }, { "name": "TestUpdateCheckData", "label": "ubuntu-latest", - "nodes": 2 + "nodes": 3 } ] } \ No newline at end of file diff --git a/integration-tests/testsetups/keeper_benchmark.go b/integration-tests/testsetups/keeper_benchmark.go index 1330ea291b8..4be6eb9c59c 100644 --- a/integration-tests/testsetups/keeper_benchmark.go +++ b/integration-tests/testsetups/keeper_benchmark.go @@ -27,18 +27,18 @@ import ( reportModel "github.com/smartcontractkit/chainlink-testing-framework/testreporters" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" - iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper1_1" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper1_2" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper1_3" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper2_0" - "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/testreporters" tt "github.com/smartcontractkit/chainlink/integration-tests/types" + iregistry22 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_automation_registry_master_wrapper_2_2" + iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper1_1" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper1_2" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper1_3" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper2_0" ) // KeeperBenchmarkTest builds a test to check that chainlink nodes are able to upkeep a specified amount of Upkeep @@ -197,7 +197,7 @@ func (k *KeeperBenchmarkTest) Setup(env *environment.Environment, config tt.Keep for index := range keysToFund { // Fund chainlink nodes nodesToFund := k.chainlinkNodes - if inputs.RegistryVersions[index] == ethereum.RegistryVersion_2_0 || inputs.RegistryVersions[index] == ethereum.RegistryVersion_2_1 { + if inputs.RegistryVersions[index] == ethereum.RegistryVersion_2_0 || inputs.RegistryVersions[index] == ethereum.RegistryVersion_2_1 || inputs.RegistryVersions[index] == ethereum.RegistryVersion_2_2 { nodesToFund = k.chainlinkNodes[1:] } err = actions.FundChainlinkNodesAddress(nodesToFund, k.chainClient, k.Inputs.ChainlinkNodeFunding, index) @@ -247,12 +247,12 @@ func (k *KeeperBenchmarkTest) Run() { txKeyId = 0 } ocrConfig, err := actions.BuildAutoOCR2ConfigVarsWithKeyIndex( - k.t, nodesWithoutBootstrap, *inputs.KeeperRegistrySettings, k.keeperRegistrars[rIndex].Address(), k.Inputs.DeltaStage, txKeyId, common.Address{}, + k.t, nodesWithoutBootstrap, *inputs.KeeperRegistrySettings, k.keeperRegistrars[rIndex].Address(), k.Inputs.DeltaStage, txKeyId, common.Address{}, k.keeperRegistries[rIndex].ChainModuleAddress(), k.keeperRegistries[rIndex].ReorgProtectionEnabled(), ) require.NoError(k.t, err, "Building OCR config shouldn't fail") // Send keeper jobs to registry and chainlink nodes - if inputs.RegistryVersions[rIndex] == ethereum.RegistryVersion_2_0 || inputs.RegistryVersions[rIndex] == ethereum.RegistryVersion_2_1 { + if inputs.RegistryVersions[rIndex] == ethereum.RegistryVersion_2_0 || inputs.RegistryVersions[rIndex] == ethereum.RegistryVersion_2_1 || inputs.RegistryVersions[rIndex] == ethereum.RegistryVersion_2_2 { actions.CreateOCRKeeperJobs(k.t, k.chainlinkNodes, k.keeperRegistries[rIndex].Address(), k.chainClient.GetChainID().Int64(), txKeyId, inputs.RegistryVersions[rIndex]) err = k.keeperRegistries[rIndex].SetConfig(*inputs.KeeperRegistrySettings, ocrConfig) require.NoError(k.t, err, "Registry config should be be set successfully") @@ -538,6 +538,8 @@ func (k *KeeperBenchmarkTest) contractABI(rIndex int) *abi.ABI { contractABI, err = keeper_registry_wrapper2_0.KeeperRegistryMetaData.GetAbi() case ethereum.RegistryVersion_2_1: contractABI, err = iregistry21.IKeeperRegistryMasterMetaData.GetAbi() + case ethereum.RegistryVersion_2_2: + contractABI, err = iregistry22.IAutomationRegistryMasterMetaData.GetAbi() default: contractABI, err = keeper_registry_wrapper2_0.KeeperRegistryMetaData.GetAbi() } @@ -652,7 +654,7 @@ func (k *KeeperBenchmarkTest) DeployBenchmarkKeeperContracts(index int) { // Fund the registry with LINK err := k.linkToken.Transfer(registry.Address(), big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(int64(k.Inputs.Upkeeps.NumberOfUpkeeps)))) require.NoError(k.t, err, "Funding keeper registry contract shouldn't fail") - ocrConfig, err := actions.BuildAutoOCR2ConfigVars(k.t, k.chainlinkNodes[1:], *k.Inputs.KeeperRegistrySettings, registrar.Address(), k.Inputs.DeltaStage) + ocrConfig, err := actions.BuildAutoOCR2ConfigVars(k.t, k.chainlinkNodes[1:], *k.Inputs.KeeperRegistrySettings, registrar.Address(), k.Inputs.DeltaStage, registry.ChainModuleAddress(), registry.ReorgProtectionEnabled()) k.log.Debug().Interface("KeeperRegistrySettings", *k.Inputs.KeeperRegistrySettings).Interface("OCRConfig", ocrConfig).Msg("Config") require.NoError(k.t, err, "Error building OCR config vars") err = registry.SetConfig(*k.Inputs.KeeperRegistrySettings, ocrConfig) diff --git a/integration-tests/universal/log_poller/helpers.go b/integration-tests/universal/log_poller/helpers.go index db7eaee625b..b91156a3784 100644 --- a/integration-tests/universal/log_poller/helpers.go +++ b/integration-tests/universal/log_poller/helpers.go @@ -31,6 +31,7 @@ import ( "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" @@ -1200,7 +1201,7 @@ func SetupLogPollerTestDocker( err = actions.CreateOCRKeeperJobsLocal(l, nodeClients, registry.Address(), network.ChainID, 0, registryVersion) require.NoError(t, err, "Error creating OCR Keeper Jobs") - ocrConfig, err := actions.BuildAutoOCR2ConfigVarsLocal(l, workerNodes, registryConfig, registrar.Address(), 30*time.Second, registry.RegistryOwnerAddress()) + ocrConfig, err := actions.BuildAutoOCR2ConfigVarsLocal(l, workerNodes, registryConfig, registrar.Address(), 30*time.Second, registry.RegistryOwnerAddress(), registry.ChainModuleAddress(), registry.ReorgProtectionEnabled()) require.NoError(t, err, "Error building OCR config vars") err = registry.SetConfig(automationDefaultRegistryConfig, ocrConfig) require.NoError(t, err, "Registry config should be set successfully") diff --git a/sonar-project.properties b/sonar-project.properties index 7d0c1be5fc5..67da119cd2a 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -53,7 +53,8 @@ sonar.cpd.exclusions=\ **/config.go,\ **/core/services/ocr2/plugins/ocr2keeper/evm/**/*,\ **/core/services/ocr2/plugins/mercury/plugin.go,\ -**/integration-tests/load/**/* +**/integration-tests/load/**/*,\ +**/integration-tests/contracts/ethereum_keeper_contracts.go # Tests' root folder, inclusions (tests to check and count) and exclusions sonar.tests=. From b60acb4c33586543c04b2658b7b27fae29008cf2 Mon Sep 17 00:00:00 2001 From: Radek Scheibinger Date: Thu, 22 Feb 2024 00:12:08 +0100 Subject: [PATCH 06/24] chore: export ABIs in the compile step (#12083) By default ABIs where not exported in the compile step. Updated the dir path to fetch ABIs from the target location used by prepublish script. Co-authored-by: chainchad <96362174+chainchad@users.noreply.github.com> --- contracts/hardhat.config.ts | 1 + contracts/package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/hardhat.config.ts b/contracts/hardhat.config.ts index 5306827b8e3..6a0b36ad0c7 100644 --- a/contracts/hardhat.config.ts +++ b/contracts/hardhat.config.ts @@ -35,6 +35,7 @@ subtask(TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS).setAction( let config = { abiExporter: { path: './abi', + runOnCompile: true, }, paths: { artifacts: './artifacts', diff --git a/contracts/package.json b/contracts/package.json index 654ec1d8958..20717c036bc 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -22,7 +22,7 @@ }, "files": [ "src/v0.8", - "abi/src/v0.8" + "abi/v0.8" ], "pnpm": { "_comment": "See https://github.com/ethers-io/ethers.js/discussions/2849#discussioncomment-2696454", From c14789651ecff7c1db31393d33baa81a48127c8a Mon Sep 17 00:00:00 2001 From: Bolek <1416262+bolekk@users.noreply.github.com> Date: Wed, 21 Feb 2024 22:55:55 -0800 Subject: [PATCH 07/24] [KS-55] Minor fixes to Engine and Write target (#12135) 1. Fix default gas limit in the Write target 2. Use mocked IDs with exactly 32 bytes 3. Pass observations as a list (of size 1) 4. More logging --- core/capabilities/targets/write_target.go | 16 +++++--- .../capabilities/targets/write_target_test.go | 3 +- core/services/workflows/delegate.go | 2 +- core/services/workflows/engine.go | 39 ++++++++++++------- 4 files changed, 39 insertions(+), 21 deletions(-) diff --git a/core/capabilities/targets/write_target.go b/core/capabilities/targets/write_target.go index c6d34271662..531730cc089 100644 --- a/core/capabilities/targets/write_target.go +++ b/core/capabilities/targets/write_target.go @@ -22,14 +22,15 @@ import ( evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/forwarder" + "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" ) var forwardABI = evmtypes.MustGetABI(forwarder.KeystoneForwarderMetaData.ABI) -func InitializeWrite(registry commontypes.CapabilitiesRegistry, legacyEVMChains legacyevm.LegacyChainContainer) error { +func InitializeWrite(registry commontypes.CapabilitiesRegistry, legacyEVMChains legacyevm.LegacyChainContainer, lggr logger.Logger) error { for _, chain := range legacyEVMChains.Slice() { - capability := NewEvmWrite(chain) + capability := NewEvmWrite(chain, lggr) if err := registry.Add(context.TODO(), capability); err != nil { return err } @@ -41,12 +42,15 @@ var ( _ capabilities.ActionCapability = &EvmWrite{} ) +const defaultGasLimit = 200000 + type EvmWrite struct { chain legacyevm.Chain capabilities.CapabilityInfo + lggr logger.Logger } -func NewEvmWrite(chain legacyevm.Chain) *EvmWrite { +func NewEvmWrite(chain legacyevm.Chain, lggr logger.Logger) *EvmWrite { // generate ID based on chain selector name := fmt.Sprintf("write_%v", chain.ID()) chainName, err := chainselectors.NameFromChainId(chain.ID().Uint64()) @@ -64,6 +68,7 @@ func NewEvmWrite(chain legacyevm.Chain) *EvmWrite { return &EvmWrite{ chain, info, + lggr.Named("EvmWrite"), } } @@ -153,6 +158,7 @@ func encodePayload(args []any, rawSelector string) ([]byte, error) { } func (cap *EvmWrite) Execute(ctx context.Context, callback chan<- capabilities.CapabilityResponse, request capabilities.CapabilityRequest) error { + cap.lggr.Debugw("Execute", "request", request) // TODO: idempotency // TODO: extract into ChainWriter? @@ -184,8 +190,6 @@ func (cap *EvmWrite) Execute(ctx context.Context, callback chan<- capabilities.C // TODO: validate encoded report is prefixed with workflowID and executionID that match the request meta - // unlimited gas in the MVP demo - gasLimit := 0 // No signature validation in the MVP demo signatures := [][]byte{} @@ -208,7 +212,7 @@ func (cap *EvmWrite) Execute(ctx context.Context, callback chan<- capabilities.C FromAddress: config.FromAddress().Address(), ToAddress: config.ForwarderAddress().Address(), EncodedPayload: calldata, - FeeLimit: uint32(gasLimit), + FeeLimit: uint32(defaultGasLimit), Meta: txMeta, Strategy: strategy, Checker: checker, diff --git a/core/capabilities/targets/write_target_test.go b/core/capabilities/targets/write_target_test.go index 68ca890cc0c..c99e84beb75 100644 --- a/core/capabilities/targets/write_target_test.go +++ b/core/capabilities/targets/write_target_test.go @@ -15,6 +15,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" @@ -45,7 +46,7 @@ func TestEvmWrite(t *testing.T) { evmcfg := evmtest.NewChainScopedConfig(t, cfg) chain.On("Config").Return(evmcfg) - capability := targets.NewEvmWrite(chain) + capability := targets.NewEvmWrite(chain, logger.TestLogger(t)) ctx := testutils.Context(t) config, err := values.NewMap(map[string]any{ diff --git a/core/services/workflows/delegate.go b/core/services/workflows/delegate.go index 13a8bda4043..6faa0bacdb8 100644 --- a/core/services/workflows/delegate.go +++ b/core/services/workflows/delegate.go @@ -44,7 +44,7 @@ func (d *Delegate) ServicesForSpec(spec job.Job) ([]job.ServiceCtx, error) { func NewDelegate(logger logger.Logger, registry types.CapabilitiesRegistry, legacyEVMChains legacyevm.LegacyChainContainer) *Delegate { // NOTE: we temporarily do registration inside NewDelegate, this will be moved out of job specs in the future - _ = targets.InitializeWrite(registry, legacyEVMChains) + _ = targets.InitializeWrite(registry, legacyEVMChains, logger) return &Delegate{logger: logger, registry: registry} } diff --git a/core/services/workflows/engine.go b/core/services/workflows/engine.go index 3260702e66b..01d1326e072 100644 --- a/core/services/workflows/engine.go +++ b/core/services/workflows/engine.go @@ -15,9 +15,10 @@ import ( ) const ( - mockedWorkflowID = "aaaaaaaa-f4d1-422f-a4b2-8ce0a1075f0a" - mockedExecutionID = "bbbbbbbb-f4d1-422f-a4b2-8ce0a1075f0a" - mockedTriggerID = "cccccccc-5cac-4071-be62-0152dd9adb0f" + // NOTE: max 32 bytes per ID - consider enforcing exactly 32 bytes? + mockedWorkflowID = "aaaaaaaaaa0000000000000000000000" + mockedExecutionID = "bbbbbbbbbb0000000000000000000000" + mockedTriggerID = "cccccccccc0000000000000000000000" ) type Engine struct { @@ -143,13 +144,20 @@ func (e *Engine) handleExecution(ctx context.Context, event capabilities.Capabil if err != nil { return err } + if len(results.Underlying) == 0 { + return fmt.Errorf("consensus returned no reports") + } + if len(results.Underlying) > 1 { + e.logger.Debugw("consensus returned more than one report") + } - _, err = e.handleTarget(ctx, results) + // we're expecting exactly one report + _, err = e.handleTarget(ctx, results.Underlying[0]) return err } -func (e *Engine) handleTarget(ctx context.Context, resp *values.List) (*values.List, error) { - +func (e *Engine) handleTarget(ctx context.Context, resp values.Value) (*values.List, error) { + e.logger.Debugw("handle target") inputs := map[string]values.Value{ "report": resp, } @@ -158,23 +166,28 @@ func (e *Engine) handleTarget(ctx context.Context, resp *values.List) (*values.L Inputs: &values.Map{Underlying: inputs}, Config: e.targetConfig, Metadata: capabilities.RequestMetadata{ - WorkflowID: mockedWorkflowID, + WorkflowID: mockedWorkflowID, + WorkflowExecutionID: mockedExecutionID, }, } return capabilities.ExecuteSync(ctx, e.target, tr) } -func (e *Engine) handleConsensus(ctx context.Context, resp capabilities.CapabilityResponse) (*values.List, error) { - e.logger.Debugw("running consensus", "resp", resp) - inputs := map[string]values.Value{ - "observations": resp.Value, - } +func (e *Engine) handleConsensus(ctx context.Context, event capabilities.CapabilityResponse) (*values.List, error) { + e.logger.Debugw("running consensus", "event", event) cr := capabilities.CapabilityRequest{ Metadata: capabilities.RequestMetadata{ WorkflowID: mockedWorkflowID, WorkflowExecutionID: mockedExecutionID, }, - Inputs: &values.Map{Underlying: inputs}, + Inputs: &values.Map{ + Underlying: map[string]values.Value{ + // each node provides a single observation - outputs of mercury trigger + "observations": &values.List{ + Underlying: []values.Value{event.Value}, + }, + }, + }, Config: e.consensusConfig, } return capabilities.ExecuteSync(ctx, e.consensus, cr) From 2b99f070e71e40cc4dd3fcde5380400ca4cd1386 Mon Sep 17 00:00:00 2001 From: "app-token-issuer-infra-releng[bot]" <120227048+app-token-issuer-infra-releng[bot]@users.noreply.github.com> Date: Thu, 22 Feb 2024 14:33:42 +0200 Subject: [PATCH 08/24] Update Operator UI from v0.8.0-8da47c3 to v0.8.0-a2b54a2 (#12059) * Update Operator UI from v0.8.0-8da47c3 to v0.8.0-a2b54a2 * Sig scanner check * Add web assets --------- Co-authored-by: github-merge-queue Co-authored-by: george-dorin --- core/web/assets/index.html | 2 +- core/web/assets/index.html.gz | Bin 420 -> 419 bytes ...614139.js => main.f42e73c0c7811e9907db.js} | 2 +- ....js.gz => main.f42e73c0c7811e9907db.js.gz} | Bin 1196933 -> 1196936 bytes operator_ui/TAG | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) rename core/web/assets/{main.74b124ef5d2ef3614139.js => main.f42e73c0c7811e9907db.js} (93%) rename core/web/assets/{main.74b124ef5d2ef3614139.js.gz => main.f42e73c0c7811e9907db.js.gz} (92%) diff --git a/core/web/assets/index.html b/core/web/assets/index.html index 77331861516..21811e104e8 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 afbd1b1a38b50f16f3c36b98a2bcef67890ed40b..2067ae210af32b1dbe31cb9cd36fbe814930f729 100644 GIT binary patch literal 419 zcmV;U0bKqciwFP!000021C^3NYa1~Th5w2w$f@>fouoJk(&i8-q)-ZNa>#Kk&3dPm zG>bIe#Q(mm?HCG$(u;&1%=c!V#(oIwWj~@CB*o62PK40egaXPBP%4Vk&rhG1H`<)! z3<;f{2wz7oqLt??n8oQ=3NpBW6M0-79t+AO1aVnCM0Bf}AtVvIZzC2EF6#NwnxkYR z^!Hj zV67LI4?ULzGBnq>-_04L9x3}#&Y?_}{ch(#Uoi)eB*>Ko* zHuFh-;hoBeK?y6&u!*R?zyv>zw!*6HvXX(Sf^lrDITzYMlcL~sVV2n^(9~IT#GAva N{st{JBxt|_006$~&U^p> 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(zVcbH=_},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);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 +`+(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.f42e73c0c7811e9907db.js.gz similarity index 92% rename from core/web/assets/main.74b124ef5d2ef3614139.js.gz rename to core/web/assets/main.f42e73c0c7811e9907db.js.gz index 667a96c1ef3afc61afc8e61da17f2e8de3003008..408214985c13d62f443c26eb2c3f701a08d78228 100644 GIT binary patch delta 92868 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 z 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 >rKT@(Z30ei{L$E+7t!ZC=0fy$5n*FWohSvS$ou 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 z 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 Date: Thu, 22 Feb 2024 18:49:58 +0400 Subject: [PATCH 09/24] [AUTO-9079] fix automation nightly and on-demand tests (#12136) * fix automation nightly tests * fix automation on-demand tests * fix automation on-demand tests * fix automation nightly upgrade tests * fix automation nightly upgrade tests * fix automation nightly upgrade tests * fix chaos, reorg tests * fix ondemand action * fix nightly action * fix nightly action --- .../workflows/automation-nightly-tests.yml | 22 ++++++++++++---- .../workflows/automation-ondemand-tests.yml | 26 ++++++++++++++----- .../chaos/automation_chaos_test.go | 14 +++++++--- .../reorg/automation_reorg_test.go | 1 + integration-tests/smoke/automation_test.go | 2 +- 5 files changed, 49 insertions(+), 16 deletions(-) diff --git a/.github/workflows/automation-nightly-tests.yml b/.github/workflows/automation-nightly-tests.yml index 6df574e58d0..81d9606ef17 100644 --- a/.github/workflows/automation-nightly-tests.yml +++ b/.github/workflows/automation-nightly-tests.yml @@ -54,18 +54,30 @@ jobs: env: CHAINLINK_COMMIT_SHA: ${{ github.sha }} CHAINLINK_ENV_USER: ${{ github.actor }} - TEST_LOG_LEVEL: debug + TEST_LOG_LEVEL: info SELECTED_NETWORKS: "SIMULATED" strategy: fail-fast: false matrix: tests: - - name: Upgrade + - name: Upgrade 2.0 suite: smoke - nodes: 6 + nodes: 1 os: ubuntu20.04-8cores-32GB network: SIMULATED - command: -run ^TestAutomationNodeUpgrade$ ./smoke + command: -run ^TestAutomationNodeUpgrade/registry_2_0 ./smoke + - name: Upgrade 2.1 + suite: smoke + nodes: 5 + os: ubuntu20.04-8cores-32GB + network: SIMULATED + command: -run ^TestAutomationNodeUpgrade/registry_2_1 ./smoke + - name: Upgrade 2.2 + suite: smoke + nodes: 5 + os: ubuntu20.04-8cores-32GB + network: SIMULATED + command: -run ^TestAutomationNodeUpgrade/registry_2_2 ./smoke runs-on: ${{ matrix.tests.os }} name: Automation ${{ matrix.tests.name }} Test steps: @@ -184,7 +196,7 @@ jobs: strategy: fail-fast: false matrix: - name: [ Upgrade ] + name: [ Upgrade 2.0, Upgrade 2.1, Upgrade 2.2 ] steps: - name: Get Results id: test-results diff --git a/.github/workflows/automation-ondemand-tests.yml b/.github/workflows/automation-ondemand-tests.yml index ed528d2490c..87952216d2c 100644 --- a/.github/workflows/automation-ondemand-tests.yml +++ b/.github/workflows/automation-ondemand-tests.yml @@ -121,32 +121,46 @@ jobs: env: CHAINLINK_COMMIT_SHA: ${{ github.sha }} CHAINLINK_ENV_USER: ${{ github.actor }} - TEST_LOG_LEVEL: debug + TEST_LOG_LEVEL: info strategy: fail-fast: false matrix: tests: - name: chaos suite: chaos - nodes: 5 + nodes: 15 os: ubuntu-latest pyroscope_env: ci-automation-on-demand-chaos network: SIMULATED command: -run ^TestAutomationChaos$ ./chaos - name: reorg suite: reorg - nodes: 1 + nodes: 5 os: ubuntu-latest pyroscope_env: ci-automation-on-demand-reorg network: SIMULATED_NONDEV command: -run ^TestAutomationReorg$ ./reorg - - name: upgrade + - name: upgrade 2.0 + suite: smoke + nodes: 1 + os: ubuntu20.04-8cores-32GB + pyroscope_env: ci-automation-on-demand-upgrade + network: SIMULATED + command: -run ^TestAutomationNodeUpgrade/registry_2_0 ./smoke + - name: upgrade 2.1 suite: smoke - nodes: 6 + nodes: 1 + os: ubuntu20.04-8cores-32GB + pyroscope_env: ci-automation-on-demand-upgrade + network: SIMULATED + command: -run ^TestAutomationNodeUpgrade/registry_2_1 ./smoke + - name: upgrade 2.2 + suite: smoke + nodes: 1 os: ubuntu20.04-8cores-32GB pyroscope_env: ci-automation-on-demand-upgrade network: SIMULATED - command: -run ^TestAutomationNodeUpgrade$ ./smoke + command: -run ^TestAutomationNodeUpgrade/registry_2_2 ./smoke runs-on: ${{ matrix.tests.os }} name: Automation On Demand ${{ matrix.tests.name }} Test steps: diff --git a/integration-tests/chaos/automation_chaos_test.go b/integration-tests/chaos/automation_chaos_test.go index 1d1fda451b3..f427b75f5a2 100644 --- a/integration-tests/chaos/automation_chaos_test.go +++ b/integration-tests/chaos/automation_chaos_test.go @@ -277,17 +277,23 @@ func TestAutomationChaos(t *testing.T) { actions.CreateOCRKeeperJobs(t, chainlinkNodes, registry.Address(), network.ChainID, 0, registryVersion) nodesWithoutBootstrap := chainlinkNodes[1:] + defaultOCRRegistryConfig.RegistryVersion = registryVersion ocrConfig, err := actions.BuildAutoOCR2ConfigVars(t, nodesWithoutBootstrap, defaultOCRRegistryConfig, registrar.Address(), 30*time.Second, registry.ChainModuleAddress(), registry.ReorgProtectionEnabled()) require.NoError(t, err, "Error building OCR config vars") err = registry.SetConfig(defaultOCRRegistryConfig, ocrConfig) require.NoError(t, err, "Registry config should be be set successfully") require.NoError(t, chainClient.WaitForEvents(), "Waiting for config to be set") - consumers_conditional, upkeepIDs_conditional := actions.DeployConsumers(t, registry, registrar, linkToken, contractDeployer, chainClient, numberOfUpkeeps, big.NewInt(defaultLinkFunds), defaultUpkeepGasLimit, false, false) - consumers_logtrigger, upkeepIDs_logtrigger := actions.DeployConsumers(t, registry, registrar, linkToken, contractDeployer, chainClient, numberOfUpkeeps, big.NewInt(defaultLinkFunds), defaultUpkeepGasLimit, true, false) + consumersConditional, upkeepidsConditional := actions.DeployConsumers(t, registry, registrar, linkToken, contractDeployer, chainClient, numberOfUpkeeps, big.NewInt(defaultLinkFunds), defaultUpkeepGasLimit, false, false) + consumersLogtrigger, upkeepidsLogtrigger := actions.DeployConsumers(t, registry, registrar, linkToken, contractDeployer, chainClient, numberOfUpkeeps, big.NewInt(defaultLinkFunds), defaultUpkeepGasLimit, true, false) - consumers := append(consumers_conditional, consumers_logtrigger...) - upkeepIDs := append(upkeepIDs_conditional, upkeepIDs_logtrigger...) + consumers := append(consumersConditional, consumersLogtrigger...) + upkeepIDs := append(upkeepidsConditional, upkeepidsLogtrigger...) + + for _, c := range consumersLogtrigger { + err = c.Start() + require.NoError(t, err, "Error starting consumer") + } l.Info().Msg("Waiting for all upkeeps to be performed") diff --git a/integration-tests/reorg/automation_reorg_test.go b/integration-tests/reorg/automation_reorg_test.go index e889a1c6123..643101dfb92 100644 --- a/integration-tests/reorg/automation_reorg_test.go +++ b/integration-tests/reorg/automation_reorg_test.go @@ -214,6 +214,7 @@ func TestAutomationReorg(t *testing.T) { actions.CreateOCRKeeperJobs(t, chainlinkNodes, registry.Address(), network.ChainID, 0, registryVersion) nodesWithoutBootstrap := chainlinkNodes[1:] + defaultOCRRegistryConfig.RegistryVersion = registryVersion ocrConfig, err := actions.BuildAutoOCR2ConfigVars(t, nodesWithoutBootstrap, defaultOCRRegistryConfig, registrar.Address(), 5*time.Second, registry.ChainModuleAddress(), registry.ReorgProtectionEnabled()) require.NoError(t, err, "OCR2 config should be built successfully") err = registry.SetConfig(defaultOCRRegistryConfig, ocrConfig) diff --git a/integration-tests/smoke/automation_test.go b/integration-tests/smoke/automation_test.go index c51b90c1f57..29bd61b9257 100644 --- a/integration-tests/smoke/automation_test.go +++ b/integration-tests/smoke/automation_test.go @@ -182,7 +182,7 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool, automationTestConfig t 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(*cfg.GetChainlinkImageConfig().Image, *cfg.GetChainlinkImageConfig().Version, a.DockerEnv.ClCluster.Nodes[i]) + err = actions.UpgradeChainlinkNodeVersionsLocal(*cfg.GetChainlinkUpgradeImageConfig().Image, *cfg.GetChainlinkUpgradeImageConfig().Version, a.DockerEnv.ClCluster.Nodes[i]) require.NoError(t, err, "Error when upgrading node %d", i) time.Sleep(time.Second * 10) expect = expect + 5 From 8c01c7db62f1c8fb82e80c20c1da1f096bb87cf1 Mon Sep 17 00:00:00 2001 From: ilija42 <57732589+ilija42@users.noreply.github.com> Date: Thu, 22 Feb 2024 15:58:33 +0100 Subject: [PATCH 10/24] Fix chain reader test flake and make it faster (#12138) --- core/services/relay/evm/chain_reader_test.go | 24 ++++++++++++-------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/core/services/relay/evm/chain_reader_test.go b/core/services/relay/evm/chain_reader_test.go index 02e9d4e3f6a..c5fe0a16ed4 100644 --- a/core/services/relay/evm/chain_reader_test.go +++ b/core/services/relay/evm/chain_reader_test.go @@ -52,19 +52,21 @@ func TestChainReader(t *testing.T) { 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) + // bind event before firing it to avoid log poller race + ctx := testutils.Context(t) + cr := it.GetChainReader(t) + require.NoError(t, cr.Bind(ctx, it.GetBindings(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) @@ -84,20 +86,24 @@ func TestChainReader(t *testing.T) { t.Run("Multiple topics can filter together", func(t *testing.T) { it.Setup(t) + + // bind event before firing it to avoid log poller race + ctx := testutils.Context(t) + cr := it.GetChainReader(t) + require.NoError(t, cr.Bind(ctx, it.GetBindings(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.Eventually(t, func() bool { + return cr.GetLatestValue(ctx, AnyContractName, triggerWithAllTopics, params, &latest) == nil + }, it.MaxWaitTimeForEvents(), time.Millisecond*10) - 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) From d19cb01a6ce4ec375c5181e3f9bf32ffe4c36e76 Mon Sep 17 00:00:00 2001 From: Oliver Townsend <133903322+ogtownsend@users.noreply.github.com> Date: Thu, 22 Feb 2024 07:37:22 -0800 Subject: [PATCH 11/24] Add metric for num logs in buffer and missed logs (#11852) * Add metric for num logs in buffer * Set metrics in buffer, remove ticker * Add missed logs metric * Remove logsInBuffer var * Add more metrics * Change to inc and dec --- .../ocr2keeper/evmregistry/v21/active_list.go | 8 +++- .../evmregistry/v21/logprovider/buffer.go | 3 ++ .../evmregistry/v21/logprovider/provider.go | 2 + .../evmregistry/v21/logprovider/recoverer.go | 8 +++- .../evmregistry/v21/prommetrics/metrics.go | 38 +++++++++++++++++++ 5 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/prommetrics/metrics.go 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 55c01939cb8..27c13f079b2 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/active_list.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/active_list.go @@ -9,6 +9,7 @@ import ( 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/prommetrics" ) // ActiveUpkeepList is a list to manage active upkeep IDs @@ -49,9 +50,10 @@ func (al *activeList) Reset(ids ...*big.Int) { for _, id := range ids { al.items[id.String()] = true } + prommetrics.AutomationActiveUpkeeps.Set(float64(len(al.items))) } -// Add adds new entries to the list +// Add adds new entries to the list. Returns the number of items added func (al *activeList) Add(ids ...*big.Int) int { al.lock.Lock() defer al.lock.Unlock() @@ -63,10 +65,11 @@ func (al *activeList) Add(ids ...*big.Int) int { al.items[key] = true } } + prommetrics.AutomationActiveUpkeeps.Set(float64(len(al.items))) return count } -// Remove removes entries from the list +// Remove removes entries from the list. Returns the number of items removed func (al *activeList) Remove(ids ...*big.Int) int { al.lock.Lock() defer al.lock.Unlock() @@ -79,6 +82,7 @@ func (al *activeList) Remove(ids ...*big.Int) int { delete(al.items, key) } } + prommetrics.AutomationActiveUpkeeps.Set(float64(len(al.items))) return count } 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 9f11a1fca01..6418d683869 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer.go @@ -12,6 +12,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/prommetrics" ) var ( @@ -230,6 +231,7 @@ func (b *logEventBuffer) enqueue(id *big.Int, logs ...logpoller.Log) int { } if added > 0 { lggr.Debugw("Added logs to buffer", "addedLogs", added, "dropped", dropped, "latestBlock", latestBlock) + prommetrics.AutomationLogsInLogBuffer.Add(float64(added - dropped)) } return added - dropped @@ -331,6 +333,7 @@ func (b *logEventBuffer) dequeueRange(start, end int64, upkeepLimit, totalLimit if len(results) > 0 { b.lggr.Debugw("Dequeued logs", "results", len(results), "start", start, "end", end) + prommetrics.AutomationLogsInLogBuffer.Sub(float64(len(results))) } return results 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 d1360faaf6d..e06593a9109 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider.go @@ -24,6 +24,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_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/prommetrics" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -162,6 +163,7 @@ func (p *logEventProvider) GetLatestPayloads(ctx context.Context) ([]ocr2keepers if err != nil { return nil, fmt.Errorf("%w: %s", ErrHeadNotAvailable, err) } + prommetrics.AutomationLogProviderLatestBlock.Set(float64(latest.BlockNumber)) start := latest.BlockNumber - p.opts.LookbackBlocks if start <= 0 { start = 1 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 13b8bb17245..2eef5db17d9 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/recoverer.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/recoverer.go @@ -27,6 +27,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "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/prommetrics" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -305,7 +306,7 @@ func (r *logRecoverer) GetRecoveryProposals(ctx context.Context) ([]ocr2keepers. var results, pending []ocr2keepers.UpkeepPayload for _, payload := range r.pending { if allLogsCounter >= MaxProposals { - // we have enough proposals, pushed the rest are pushed back to pending + // we have enough proposals, the rest are pushed back to pending pending = append(pending, payload) continue } @@ -321,6 +322,7 @@ func (r *logRecoverer) GetRecoveryProposals(ctx context.Context) ([]ocr2keepers. } r.pending = pending + prommetrics.AutomationRecovererPendingPayloads.Set(float64(len(r.pending))) r.lggr.Debugf("found %d recoverable payloads", len(results)) @@ -417,6 +419,7 @@ func (r *logRecoverer) recoverFilter(ctx context.Context, f upkeepFilter, startB added, alreadyPending, ok := r.populatePending(f, filteredLogs) if added > 0 { r.lggr.Debugw("found missed logs", "added", added, "alreadyPending", alreadyPending, "upkeepID", f.upkeepID) + prommetrics.AutomationRecovererMissedLogs.Add(float64(added)) } if !ok { r.lggr.Debugw("failed to add all logs to pending", "upkeepID", f.upkeepID) @@ -673,6 +676,7 @@ func (r *logRecoverer) addPending(payload ocr2keepers.UpkeepPayload) error { } if !exist { r.pending = append(pending, payload) + prommetrics.AutomationRecovererPendingPayloads.Inc() } return nil } @@ -684,6 +688,8 @@ func (r *logRecoverer) removePending(workID string) { for _, p := range r.pending { if p.WorkID != workID { updated = append(updated, p) + } else { + prommetrics.AutomationRecovererPendingPayloads.Dec() } } r.pending = updated diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/prommetrics/metrics.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/prommetrics/metrics.go new file mode 100644 index 00000000000..cebbac59884 --- /dev/null +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/prommetrics/metrics.go @@ -0,0 +1,38 @@ +package prommetrics + +import ( + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" +) + +// AutomationNamespace is the namespace for all Automation related metrics +const AutomationLogTriggerNamespace = "automation_log_trigger" + +// Automation metrics +var ( + AutomationLogsInLogBuffer = promauto.NewGauge(prometheus.GaugeOpts{ + Namespace: AutomationLogTriggerNamespace, + Name: "num_logs_in_log_buffer", + Help: "The total number of logs currently being stored in the log buffer", + }) + AutomationRecovererMissedLogs = promauto.NewCounter(prometheus.CounterOpts{ + Namespace: AutomationLogTriggerNamespace, + Name: "num_recoverer_missed_logs", + Help: "How many valid log triggers were identified as being missed by the recoverer", + }) + AutomationRecovererPendingPayloads = promauto.NewGauge(prometheus.GaugeOpts{ + Namespace: AutomationLogTriggerNamespace, + Name: "num_recoverer_pending_payloads", + Help: "How many log trigger payloads are currently pending in the recoverer", + }) + AutomationActiveUpkeeps = promauto.NewGauge(prometheus.GaugeOpts{ + Namespace: AutomationLogTriggerNamespace, + Name: "num_active_upkeeps", + Help: "How many log trigger upkeeps are currently active", + }) + AutomationLogProviderLatestBlock = promauto.NewGauge(prometheus.GaugeOpts{ + Namespace: AutomationLogTriggerNamespace, + Name: "log_provider_latest_block", + Help: "The latest block number the log provider has seen", + }) +) From 601aa07001bae0ae1ebad04cf83b0feb8956e15a Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 22 Feb 2024 12:30:38 -0500 Subject: [PATCH 12/24] Mercury 1.0 (parallel composition) (#10810) * Implement Data Streams plugin * Try to make tests a bit more deterministic * lint * Increase DeltaGrace * gomodtidy] * Increase timeouts significantly * Attempt test fix * Add comments * Remove useless comment * Add fromblock to LLO job spec --- GNUmakefile | 1 + core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 +- core/services/chainlink/application.go | 1 + core/services/job/orm.go | 58 +-- core/services/job/spawner_test.go | 2 +- core/services/llo/bm/dummy_transmitter.go | 79 ++++ .../services/llo/bm/dummy_transmitter_test.go | 36 ++ .../llo/channel_definition_cache_factory.go | 58 +++ core/services/llo/data_source.go | 103 +++++ core/services/llo/data_source_test.go | 95 +++++ core/services/llo/delegate.go | 123 ++++++ core/services/llo/evm/report_codec.go | 97 +++++ core/services/llo/evm/report_codec_test.go | 90 +++++ core/services/llo/keyring.go | 79 ++++ core/services/llo/keyring_test.go | 123 ++++++ core/services/llo/offchain_config_digester.go | 123 ++++++ .../llo/offchain_config_digester_test.go | 55 +++ .../llo/onchain_channel_definition_cache.go | 252 ++++++++++++ .../onchain_channel_definition_cache_test.go | 26 ++ core/services/llo/orm.go | 66 ++++ core/services/llo/orm_test.go | 101 +++++ .../llo/static_channel_definitions_cache.go | 56 +++ core/services/llo/transmitter.go | 153 ++++++++ core/services/llo/transmitter_test.go | 7 + core/services/ocr2/delegate.go | 142 ++++++- .../ocr2/plugins/llo/config/config.go | 112 ++++++ .../ocr2/plugins/llo/config/config_test.go | 142 +++++++ .../services/ocr2/plugins/llo/helpers_test.go | 356 +++++++++++++++++ .../ocr2/plugins/llo/integration_test.go | 370 ++++++++++++++++++ ...annel_definition_cache_integration_test.go | 213 ++++++++++ .../ocr2/plugins/mercury/integration_test.go | 14 +- core/services/ocr2/validate/validate.go | 12 + core/services/ocrbootstrap/delegate.go | 15 +- core/services/pipeline/mocks/runner.go | 30 ++ core/services/pipeline/runner.go | 1 + core/services/relay/evm/evm.go | 76 +++- .../services/relay/evm/llo_config_provider.go | 21 + core/services/relay/evm/llo_provider.go | 90 +++++ .../relay/evm/llo_verifier_decoder.go | 67 ++++ .../relay/evm/mercury/config_digest.go | 2 +- .../relay/evm/mercury/wsrpc/pb/mercury.pb.go | 8 +- .../relay/evm/mercury/wsrpc/pb/mercury.proto | 2 +- .../0224_create_channel_definition_caches.sql | 12 + docs/CHANGELOG.md | 1 + go.mod | 4 +- go.sum | 4 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +- integration-tests/load/go.mod | 2 +- integration-tests/load/go.sum | 4 +- 51 files changed, 3436 insertions(+), 60 deletions(-) create mode 100644 core/services/llo/bm/dummy_transmitter.go create mode 100644 core/services/llo/bm/dummy_transmitter_test.go create mode 100644 core/services/llo/channel_definition_cache_factory.go create mode 100644 core/services/llo/data_source.go create mode 100644 core/services/llo/data_source_test.go create mode 100644 core/services/llo/delegate.go create mode 100644 core/services/llo/evm/report_codec.go create mode 100644 core/services/llo/evm/report_codec_test.go create mode 100644 core/services/llo/keyring.go create mode 100644 core/services/llo/keyring_test.go create mode 100644 core/services/llo/offchain_config_digester.go create mode 100644 core/services/llo/offchain_config_digester_test.go create mode 100644 core/services/llo/onchain_channel_definition_cache.go create mode 100644 core/services/llo/onchain_channel_definition_cache_test.go create mode 100644 core/services/llo/orm.go create mode 100644 core/services/llo/orm_test.go create mode 100644 core/services/llo/static_channel_definitions_cache.go create mode 100644 core/services/llo/transmitter.go create mode 100644 core/services/llo/transmitter_test.go create mode 100644 core/services/ocr2/plugins/llo/config/config.go create mode 100644 core/services/ocr2/plugins/llo/config/config_test.go create mode 100644 core/services/ocr2/plugins/llo/helpers_test.go create mode 100644 core/services/ocr2/plugins/llo/integration_test.go create mode 100644 core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go create mode 100644 core/services/relay/evm/llo_config_provider.go create mode 100644 core/services/relay/evm/llo_provider.go create mode 100644 core/services/relay/evm/llo_verifier_decoder.go create mode 100644 core/store/migrate/migrations/0224_create_channel_definition_caches.sql diff --git a/GNUmakefile b/GNUmakefile index 20f3eb92d51..8cbd0953ee7 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -31,6 +31,7 @@ gomodtidy: ## Run go mod tidy on all modules. go mod tidy cd ./core/scripts && go mod tidy cd ./integration-tests && go mod tidy + cd ./integration-tests/load && go mod tidy .PHONY: godoc godoc: ## Install and run godoc diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 9cf5111cf52..91697eb5f9f 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -250,7 +250,7 @@ require ( github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chain-selectors v1.0.10 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 // indirect - github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240214203158-47dae5de1336 // indirect + github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20240119021347-3c541a78cdb8 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240216142700-c5869534c19e // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240213121419-1272736c2ac0 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index b6432cbe3fe..7a3f6b2f0c2 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1177,8 +1177,8 @@ github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6 github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 h1:I326nw5GwHQHsLKHwtu5Sb9EBLylC8CfUd7BFAS0jtg= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8/go.mod h1:a65NtrK4xZb01mf0dDNghPkN2wXgcqFQ55ADthVBgMc= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240214203158-47dae5de1336 h1:j00D0/EqE9HRu+63v7KwUOe4ZxLc4AN5SOJFiinkkH0= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240214203158-47dae5de1336/go.mod h1:umLyYLRGqyIuFfGpEREZP3So6+O8iL35cCCqW+OxX5w= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540/go.mod h1:sjAmX8K2kbQhvDarZE1ZZgDgmHJ50s0BBc/66vKY2ek= 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.20240216142700-c5869534c19e h1:k8HS3GsAFZnxXIW3141VsQP2+EL1XrTtOi/HDt7sdBE= diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index eebb70534c0..d95458838bc 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -417,6 +417,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) { bridgeORM, mercuryORM, pipelineRunner, + streamRegistry, peerWrapper, telemetryManager, legacyEVMChains, diff --git a/core/services/job/orm.go b/core/services/job/orm.go index 07b9cb95aae..2e7bb0a90a5 100644 --- a/core/services/job/orm.go +++ b/core/services/job/orm.go @@ -467,34 +467,40 @@ func (o *orm) CreateJob(jb *Job, qopts ...pg.QOpt) error { } // ValidateKeyStoreMatch confirms that the key has a valid match in the keystore -func ValidateKeyStoreMatch(spec *OCR2OracleSpec, keyStore keystore.Master, key string) error { - if spec.PluginType == types.Mercury { - _, err := keyStore.CSA().Get(key) +func ValidateKeyStoreMatch(spec *OCR2OracleSpec, keyStore keystore.Master, key string) (err error) { + switch spec.PluginType { + case types.Mercury, types.LLO: + _, err = keyStore.CSA().Get(key) if err != nil { - return errors.Errorf("no CSA key matching: %q", key) + err = errors.Errorf("no CSA key matching: %q", key) } - } else { - switch spec.Relay { - case relay.EVM: - _, err := keyStore.Eth().Get(key) - if err != nil { - return errors.Errorf("no EVM key matching: %q", key) - } - case relay.Cosmos: - _, err := keyStore.Cosmos().Get(key) - if err != nil { - return errors.Errorf("no Cosmos key matching: %q", key) - } - case relay.Solana: - _, err := keyStore.Solana().Get(key) - if err != nil { - return errors.Errorf("no Solana key matching: %q", key) - } - case relay.StarkNet: - _, err := keyStore.StarkNet().Get(key) - if err != nil { - return errors.Errorf("no Starknet key matching: %q", key) - } + default: + err = validateKeyStoreMatchForRelay(spec.Relay, keyStore, key) + } + return +} + +func validateKeyStoreMatchForRelay(network relay.Network, keyStore keystore.Master, key string) error { + switch network { + case relay.EVM: + _, err := keyStore.Eth().Get(key) + if err != nil { + return errors.Errorf("no EVM key matching: %q", key) + } + case relay.Cosmos: + _, err := keyStore.Cosmos().Get(key) + if err != nil { + return errors.Errorf("no Cosmos key matching: %q", key) + } + case relay.Solana: + _, err := keyStore.Solana().Get(key) + if err != nil { + return errors.Errorf("no Solana key matching: %q", key) + } + case relay.StarkNet: + _, err := keyStore.StarkNet().Get(key) + if err != nil { + return errors.Errorf("no Starknet key matching: %q", key) } } return nil diff --git a/core/services/job/spawner_test.go b/core/services/job/spawner_test.go index b23be49458e..9dde7a47721 100644 --- a/core/services/job/spawner_test.go +++ b/core/services/job/spawner_test.go @@ -304,7 +304,7 @@ func TestSpawner_CreateJobDeleteJob(t *testing.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) - d := ocr2.NewDelegate(nil, orm, nil, nil, nil, nil, monitoringEndpoint, legacyChains, lggr, ocr2DelegateConfig, + d := ocr2.NewDelegate(nil, orm, nil, nil, nil, nil, nil, monitoringEndpoint, legacyChains, lggr, ocr2DelegateConfig, keyStore.OCR2(), keyStore.DKGSign(), keyStore.DKGEncrypt(), ethKeyStore, testRelayGetter, mailMon, capabilities.NewRegistry(lggr)) delegateOCR2 := &delegate{jobOCR2VRF.Type, []job.ServiceCtx{}, 0, nil, d} diff --git a/core/services/llo/bm/dummy_transmitter.go b/core/services/llo/bm/dummy_transmitter.go new file mode 100644 index 00000000000..b998c19cb29 --- /dev/null +++ b/core/services/llo/bm/dummy_transmitter.go @@ -0,0 +1,79 @@ +package bm + +import ( + "context" + "crypto/ed25519" + "fmt" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" + "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + "github.com/smartcontractkit/chainlink-common/pkg/services" + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" + + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +// A dummy transmitter useful for benchmarking and testing + +var ( + transmitSuccessCount = promauto.NewCounter(prometheus.CounterOpts{ + Name: "llo_transmit_success_count", + Help: "Running count of successful transmits", + }) +) + +type Transmitter interface { + llotypes.Transmitter + services.Service +} + +type transmitter struct { + lggr logger.Logger + fromAccount string +} + +func NewTransmitter(lggr logger.Logger, fromAccount ed25519.PublicKey) Transmitter { + return &transmitter{ + lggr.Named("DummyTransmitter"), + fmt.Sprintf("%x", fromAccount), + } +} + +func (t *transmitter) Start(context.Context) error { + return nil +} + +func (t *transmitter) Close() error { + return nil +} + +func (t *transmitter) Transmit( + ctx context.Context, + digest types.ConfigDigest, + seqNr uint64, + report ocr3types.ReportWithInfo[llotypes.ReportInfo], + sigs []types.AttributedOnchainSignature, +) error { + transmitSuccessCount.Inc() + t.lggr.Debugw("Transmit", "digest", digest, "seqNr", seqNr, "report.Report", report.Report, "report.Info", report.Info, "sigs", sigs) + return nil +} + +// FromAccount returns the stringified (hex) CSA public key +func (t *transmitter) FromAccount() (ocr2types.Account, error) { + return ocr2types.Account(t.fromAccount), nil +} + +func (t *transmitter) Ready() error { return nil } + +func (t *transmitter) HealthReport() map[string]error { + report := map[string]error{t.Name(): nil} + return report +} + +func (t *transmitter) Name() string { return t.lggr.Name() } diff --git a/core/services/llo/bm/dummy_transmitter_test.go b/core/services/llo/bm/dummy_transmitter_test.go new file mode 100644 index 00000000000..055b150ad13 --- /dev/null +++ b/core/services/llo/bm/dummy_transmitter_test.go @@ -0,0 +1,36 @@ +package bm + +import ( + "crypto/ed25519" + "testing" + + "github.com/stretchr/testify/require" + "go.uber.org/zap/zapcore" + + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" + "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" + + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +func Test_DummyTransmitter(t *testing.T) { + lggr, observedLogs := logger.TestLoggerObserved(t, zapcore.DebugLevel) + tr := NewTransmitter(lggr, ed25519.PublicKey("dummy")) + + servicetest.Run(t, tr) + + err := tr.Transmit( + testutils.Context(t), + types.ConfigDigest{}, + 42, + ocr3types.ReportWithInfo[llotypes.ReportInfo]{}, + []types.AttributedOnchainSignature{}, + ) + require.NoError(t, err) + + testutils.RequireLogMessage(t, observedLogs, "Transmit") +} diff --git a/core/services/llo/channel_definition_cache_factory.go b/core/services/llo/channel_definition_cache_factory.go new file mode 100644 index 00000000000..51906e0ff1b --- /dev/null +++ b/core/services/llo/channel_definition_cache_factory.go @@ -0,0 +1,58 @@ +package llo + +import ( + "fmt" + "sync" + + "github.com/ethereum/go-ethereum/common" + + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" + + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" + "github.com/smartcontractkit/chainlink/v2/core/logger" + lloconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/llo/config" +) + +type ChannelDefinitionCacheFactory interface { + NewCache(cfg lloconfig.PluginConfig) (llotypes.ChannelDefinitionCache, error) +} + +var _ ChannelDefinitionCacheFactory = &channelDefinitionCacheFactory{} + +func NewChannelDefinitionCacheFactory(lggr logger.Logger, orm ChannelDefinitionCacheORM, lp logpoller.LogPoller) ChannelDefinitionCacheFactory { + return &channelDefinitionCacheFactory{ + lggr, + orm, + lp, + make(map[common.Address]struct{}), + sync.Mutex{}, + } +} + +type channelDefinitionCacheFactory struct { + lggr logger.Logger + orm ChannelDefinitionCacheORM + lp logpoller.LogPoller + + caches map[common.Address]struct{} + mu sync.Mutex +} + +func (f *channelDefinitionCacheFactory) NewCache(cfg lloconfig.PluginConfig) (llotypes.ChannelDefinitionCache, error) { + if cfg.ChannelDefinitions != "" { + return NewStaticChannelDefinitionCache(f.lggr, cfg.ChannelDefinitions) + } + + addr := cfg.ChannelDefinitionsContractAddress + fromBlock := cfg.ChannelDefinitionsContractFromBlock + + f.mu.Lock() + defer f.mu.Unlock() + + if _, exists := f.caches[addr]; exists { + // This shouldn't really happen and isn't supported + return nil, fmt.Errorf("cache already exists for contract address %s", addr.Hex()) + } + f.caches[addr] = struct{}{} + return NewChannelDefinitionCache(f.lggr, f.orm, f.lp, addr, fromBlock), nil +} diff --git a/core/services/llo/data_source.go b/core/services/llo/data_source.go new file mode 100644 index 00000000000..a9c3744f9e3 --- /dev/null +++ b/core/services/llo/data_source.go @@ -0,0 +1,103 @@ +package llo + +import ( + "context" + "fmt" + "math/big" + "sync" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" + "github.com/smartcontractkit/chainlink-data-streams/llo" + + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/streams" +) + +var ( + promMissingStreamCount = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "llo_stream_missing_count", + Help: "Number of times we tried to observe a stream, but it was missing", + }, + []string{"streamID"}, + ) + promObservationErrorCount = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "llo_stream_observation_error_count", + Help: "Number of times we tried to observe a stream, but it failed with an error", + }, + []string{"streamID"}, + ) +) + +type ErrMissingStream struct { + id string +} + +type Registry interface { + Get(streamID streams.StreamID) (strm streams.Stream, exists bool) +} + +func (e ErrMissingStream) Error() string { + return fmt.Sprintf("missing stream definition for: %q", e.id) +} + +var _ llo.DataSource = &dataSource{} + +type dataSource struct { + lggr logger.Logger + registry Registry +} + +func newDataSource(lggr logger.Logger, registry Registry) llo.DataSource { + return &dataSource{lggr.Named("DataSource"), registry} +} + +// Observe looks up all streams in the registry and returns a map of stream ID => value +func (d *dataSource) Observe(ctx context.Context, streamIDs map[llotypes.StreamID]struct{}) (llo.StreamValues, error) { + var wg sync.WaitGroup + wg.Add(len(streamIDs)) + sv := make(llo.StreamValues) + var mu sync.Mutex + + for streamID := range streamIDs { + go func(streamID llotypes.StreamID) { + defer wg.Done() + + var res llo.ObsResult[*big.Int] + + stream, exists := d.registry.Get(streamID) + if exists { + run, trrs, err := stream.Run(ctx) + if err != nil { + var runID int64 + if run != nil { + runID = run.ID + } + d.lggr.Debugw("Observation failed for stream", "err", err, "streamID", streamID, "runID", runID) + promObservationErrorCount.WithLabelValues(fmt.Sprintf("%d", streamID)).Inc() + } else { + // TODO: support types other than *big.Int + // https://smartcontract-it.atlassian.net/browse/MERC-3525 + val, err := streams.ExtractBigInt(trrs) + if err == nil { + res.Val = val + res.Valid = true + } + } + } else { + d.lggr.Errorw(fmt.Sprintf("Missing stream: %q", streamID), "streamID", streamID) + promMissingStreamCount.WithLabelValues(fmt.Sprintf("%d", streamID)).Inc() + } + + mu.Lock() + defer mu.Unlock() + sv[streamID] = res + }(streamID) + } + + wg.Wait() + + return sv, nil +} diff --git a/core/services/llo/data_source_test.go b/core/services/llo/data_source_test.go new file mode 100644 index 00000000000..c956e3770c9 --- /dev/null +++ b/core/services/llo/data_source_test.go @@ -0,0 +1,95 @@ +package llo + +import ( + "context" + "errors" + "math/big" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/smartcontractkit/chainlink-data-streams/llo" + "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/services/streams" +) + +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 +} + +type mockRegistry struct { + streams map[streams.StreamID]*mockStream +} + +func (m *mockRegistry) Get(streamID streams.StreamID) (strm streams.Stream, exists bool) { + strm, exists = m.streams[streamID] + return +} + +func makeStreamWithSingleResult[T any](res T, err error) *mockStream { + return &mockStream{ + trrs: []pipeline.TaskRunResult{pipeline.TaskRunResult{Task: &pipeline.MemoTask{}, Result: pipeline.Result{Value: res}}}, + err: err, + } +} + +func Test_DataSource(t *testing.T) { + lggr := logger.TestLogger(t) + reg := &mockRegistry{make(map[streams.StreamID]*mockStream)} + ds := newDataSource(lggr, reg) + ctx := testutils.Context(t) + + streamIDs := make(map[streams.StreamID]struct{}) + streamIDs[streams.StreamID(1)] = struct{}{} + streamIDs[streams.StreamID(2)] = struct{}{} + streamIDs[streams.StreamID(3)] = struct{}{} + + t.Run("Observe", func(t *testing.T) { + t.Run("returns errors if no streams are defined", func(t *testing.T) { + vals, err := ds.Observe(ctx, streamIDs) + assert.NoError(t, err) + + assert.Equal(t, llo.StreamValues{ + 2: llo.ObsResult[*big.Int]{Val: nil, Valid: false}, + 1: llo.ObsResult[*big.Int]{Val: nil, Valid: false}, + 3: llo.ObsResult[*big.Int]{Val: nil, Valid: false}, + }, vals) + }) + t.Run("observes each stream with success and returns values matching map argument", func(t *testing.T) { + reg.streams[1] = makeStreamWithSingleResult[*big.Int](big.NewInt(2181), nil) + reg.streams[2] = makeStreamWithSingleResult[*big.Int](big.NewInt(40602), nil) + reg.streams[3] = makeStreamWithSingleResult[*big.Int](big.NewInt(15), nil) + + vals, err := ds.Observe(ctx, streamIDs) + assert.NoError(t, err) + + assert.Equal(t, llo.StreamValues{ + 2: llo.ObsResult[*big.Int]{Val: big.NewInt(40602), Valid: true}, + 1: llo.ObsResult[*big.Int]{Val: big.NewInt(2181), Valid: true}, + 3: llo.ObsResult[*big.Int]{Val: big.NewInt(15), Valid: true}, + }, vals) + }) + t.Run("observes each stream and returns success/errors", func(t *testing.T) { + reg.streams[1] = makeStreamWithSingleResult[*big.Int](big.NewInt(2181), errors.New("something exploded")) + reg.streams[2] = makeStreamWithSingleResult[*big.Int](big.NewInt(40602), nil) + reg.streams[3] = makeStreamWithSingleResult[*big.Int](nil, errors.New("something exploded 2")) + + vals, err := ds.Observe(ctx, streamIDs) + assert.NoError(t, err) + + assert.Equal(t, llo.StreamValues{ + 2: llo.ObsResult[*big.Int]{Val: big.NewInt(40602), Valid: true}, + 1: llo.ObsResult[*big.Int]{Val: nil, Valid: false}, + 3: llo.ObsResult[*big.Int]{Val: nil, Valid: false}, + }, vals) + }) + }) +} diff --git a/core/services/llo/delegate.go b/core/services/llo/delegate.go new file mode 100644 index 00000000000..b64c9c590fe --- /dev/null +++ b/core/services/llo/delegate.go @@ -0,0 +1,123 @@ +package llo + +import ( + "context" + "errors" + "fmt" + + ocrcommontypes "github.com/smartcontractkit/libocr/commontypes" + ocr2plus "github.com/smartcontractkit/libocr/offchainreporting2plus" + ocr3types "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" + ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + "github.com/smartcontractkit/chainlink-common/pkg/services" + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" + "github.com/smartcontractkit/chainlink-data-streams/llo" + + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/job" + "github.com/smartcontractkit/chainlink/v2/core/services/llo/evm" + "github.com/smartcontractkit/chainlink/v2/core/services/pg" + "github.com/smartcontractkit/chainlink/v2/core/services/streams" +) + +var _ job.ServiceCtx = &delegate{} + +type Closer interface { + Close() error +} + +type delegate struct { + services.StateMachine + + cfg DelegateConfig + codecs map[llotypes.ReportFormat]llo.ReportCodec + + prrc llo.PredecessorRetirementReportCache + src llo.ShouldRetireCache + ds llo.DataSource + + oracle Closer +} + +type DelegateConfig struct { + Logger logger.Logger + Queryer pg.Queryer + Runner streams.Runner + Registry Registry + + // LLO + ChannelDefinitionCache llotypes.ChannelDefinitionCache + + // OCR3 + BinaryNetworkEndpointFactory ocr2types.BinaryNetworkEndpointFactory + V2Bootstrappers []ocrcommontypes.BootstrapperLocator + ContractConfigTracker ocr2types.ContractConfigTracker + ContractTransmitter ocr3types.ContractTransmitter[llotypes.ReportInfo] + Database ocr3types.Database + OCRLogger ocrcommontypes.Logger + MonitoringEndpoint ocrcommontypes.MonitoringEndpoint + OffchainConfigDigester ocr2types.OffchainConfigDigester + OffchainKeyring ocr2types.OffchainKeyring + OnchainKeyring ocr3types.OnchainKeyring[llotypes.ReportInfo] + LocalConfig ocr2types.LocalConfig +} + +func NewDelegate(cfg DelegateConfig) (job.ServiceCtx, error) { + if cfg.Queryer == nil { + return nil, errors.New("Queryer must not be nil") + } + if cfg.Runner == nil { + return nil, errors.New("Runner must not be nil") + } + if cfg.Registry == nil { + return nil, errors.New("Registry must not be nil") + } + codecs := make(map[llotypes.ReportFormat]llo.ReportCodec) + + // NOTE: All codecs must be specified here + codecs[llotypes.ReportFormatJSON] = llo.JSONReportCodec{} + codecs[llotypes.ReportFormatEVM] = evm.ReportCodec{} + + // TODO: Do these services need starting? + // https://smartcontract-it.atlassian.net/browse/MERC-3386 + prrc := llo.NewPredecessorRetirementReportCache() + src := llo.NewShouldRetireCache() + ds := newDataSource(cfg.Logger.Named("DataSource"), cfg.Registry) + + return &delegate{services.StateMachine{}, cfg, codecs, prrc, src, ds, nil}, nil +} + +func (d *delegate) Start(ctx context.Context) error { + return d.StartOnce("LLODelegate", func() error { + // create the oracle from config values + oracle, err := ocr2plus.NewOracle(ocr2plus.OCR3OracleArgs[llotypes.ReportInfo]{ + BinaryNetworkEndpointFactory: d.cfg.BinaryNetworkEndpointFactory, + V2Bootstrappers: d.cfg.V2Bootstrappers, + ContractConfigTracker: d.cfg.ContractConfigTracker, + ContractTransmitter: d.cfg.ContractTransmitter, + Database: d.cfg.Database, + LocalConfig: d.cfg.LocalConfig, + Logger: d.cfg.OCRLogger, + MonitoringEndpoint: d.cfg.MonitoringEndpoint, + OffchainConfigDigester: d.cfg.OffchainConfigDigester, + OffchainKeyring: d.cfg.OffchainKeyring, + OnchainKeyring: d.cfg.OnchainKeyring, + ReportingPluginFactory: llo.NewPluginFactory( + d.prrc, d.src, d.cfg.ChannelDefinitionCache, d.ds, d.cfg.Logger.Named("LLOReportingPlugin"), d.codecs, + ), + }) + + if err != nil { + return fmt.Errorf("%w: failed to create new OCR oracle", err) + } + + d.oracle = oracle + + return oracle.Start() + }) +} + +func (d *delegate) Close() error { + return d.StopOnce("LLODelegate", d.oracle.Close) +} diff --git a/core/services/llo/evm/report_codec.go b/core/services/llo/evm/report_codec.go new file mode 100644 index 00000000000..23ae02e1064 --- /dev/null +++ b/core/services/llo/evm/report_codec.go @@ -0,0 +1,97 @@ +package evm + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" + + chainselectors "github.com/smartcontractkit/chain-selectors" + "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" + + "github.com/smartcontractkit/chainlink-data-streams/llo" +) + +var ( + _ llo.ReportCodec = ReportCodec{} + Schema = getSchema() +) + +func getSchema() abi.Arguments { + mustNewType := func(t string) abi.Type { + result, err := abi.NewType(t, "", []abi.ArgumentMarshaling{}) + if err != nil { + panic(fmt.Sprintf("Unexpected error during abi.NewType: %s", err)) + } + return result + } + return abi.Arguments([]abi.Argument{ + {Name: "configDigest", Type: mustNewType("bytes32")}, + {Name: "chainId", Type: mustNewType("uint64")}, + // TODO: + // could also include address of verifier to make things more specific. + // downside is increased data size. + // for now we assume that a channelId will only be registered on a single + // verifier per chain. + // https://smartcontract-it.atlassian.net/browse/MERC-3652 + {Name: "seqNr", Type: mustNewType("uint64")}, + {Name: "channelId", Type: mustNewType("uint32")}, + {Name: "validAfterSeconds", Type: mustNewType("uint32")}, + {Name: "validUntilSeconds", Type: mustNewType("uint32")}, + {Name: "values", Type: mustNewType("int192[]")}, + {Name: "specimen", Type: mustNewType("bool")}, + }) +} + +type ReportCodec struct{} + +func NewReportCodec() ReportCodec { + return ReportCodec{} +} + +func (ReportCodec) Encode(report llo.Report) ([]byte, error) { + chainID, err := chainselectors.ChainIdFromSelector(report.ChainSelector) + if err != nil { + return nil, fmt.Errorf("failed to get chain ID for selector %d; %w", report.ChainSelector, err) + } + + b, err := Schema.Pack(report.ConfigDigest, chainID, report.SeqNr, report.ChannelID, report.ValidAfterSeconds, report.ValidUntilSeconds, report.Values, report.Specimen) + if err != nil { + return nil, fmt.Errorf("failed to encode report: %w", err) + } + return b, nil +} + +func (ReportCodec) Decode(encoded []byte) (llo.Report, error) { + type decode struct { + ConfigDigest types.ConfigDigest + ChainId uint64 + SeqNr uint64 + ChannelId llotypes.ChannelID + ValidAfterSeconds uint32 + ValidUntilSeconds uint32 + Values []*big.Int + Specimen bool + } + values, err := Schema.Unpack(encoded) + if err != nil { + return llo.Report{}, fmt.Errorf("failed to decode report: %w", err) + } + decoded := new(decode) + if err = Schema.Copy(decoded, values); err != nil { + return llo.Report{}, fmt.Errorf("failed to copy report values to struct: %w", err) + } + chainSelector, err := chainselectors.SelectorFromChainId(decoded.ChainId) + return llo.Report{ + ConfigDigest: decoded.ConfigDigest, + ChainSelector: chainSelector, + SeqNr: decoded.SeqNr, + ChannelID: decoded.ChannelId, + ValidAfterSeconds: decoded.ValidAfterSeconds, + ValidUntilSeconds: decoded.ValidUntilSeconds, + Values: decoded.Values, + Specimen: decoded.Specimen, + }, err +} diff --git a/core/services/llo/evm/report_codec_test.go b/core/services/llo/evm/report_codec_test.go new file mode 100644 index 00000000000..e00314306ae --- /dev/null +++ b/core/services/llo/evm/report_codec_test.go @@ -0,0 +1,90 @@ +package evm + +import ( + "math/big" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" + + "github.com/smartcontractkit/chainlink-data-streams/llo" +) + +const ethMainnetChainSelector uint64 = 5009297550715157269 + +func newValidReport() llo.Report { + return llo.Report{ + ConfigDigest: types.ConfigDigest{1, 2, 3}, + ChainSelector: ethMainnetChainSelector, // + SeqNr: 32, + ChannelID: llotypes.ChannelID(31), + ValidAfterSeconds: 33, + ValidUntilSeconds: 34, + Values: []*big.Int{big.NewInt(35), big.NewInt(36)}, + Specimen: true, + } +} + +func Test_ReportCodec(t *testing.T) { + rc := ReportCodec{} + + t.Run("Encode errors on zero fields", func(t *testing.T) { + _, err := rc.Encode(llo.Report{}) + require.Error(t, err) + + assert.Contains(t, err.Error(), "failed to get chain ID for selector 0; chain not found for chain selector 0") + }) + + t.Run("Encode constructs a report from observations", func(t *testing.T) { + report := newValidReport() + + encoded, err := rc.Encode(report) + require.NoError(t, err) + + reportElems := make(map[string]interface{}) + err = Schema.UnpackIntoMap(reportElems, encoded) + require.NoError(t, err) + + assert.Equal(t, [32]uint8{0x1, 0x2, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, reportElems["configDigest"]) + assert.Equal(t, uint64(1), reportElems["chainId"]) + assert.Equal(t, uint64(32), reportElems["seqNr"]) + assert.Equal(t, uint32(31), reportElems["channelId"]) + assert.Equal(t, uint32(33), reportElems["validAfterSeconds"]) + assert.Equal(t, uint32(34), reportElems["validUntilSeconds"]) + assert.Equal(t, []*big.Int{big.NewInt(35), big.NewInt(36)}, reportElems["values"]) + assert.Equal(t, true, reportElems["specimen"]) + + assert.Len(t, encoded, 352) + assert.Equal(t, []byte{0x1, 0x2, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x21, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x23, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24}, encoded) + + t.Run("Decode decodes the report", func(t *testing.T) { + decoded, err := rc.Decode(encoded) + require.NoError(t, err) + + assert.Equal(t, report.ConfigDigest, decoded.ConfigDigest) + assert.Equal(t, report.ChainSelector, decoded.ChainSelector) + assert.Equal(t, report.SeqNr, decoded.SeqNr) + assert.Equal(t, report.ChannelID, decoded.ChannelID) + assert.Equal(t, report.ValidAfterSeconds, decoded.ValidAfterSeconds) + assert.Equal(t, report.ValidUntilSeconds, decoded.ValidUntilSeconds) + assert.Equal(t, report.Values, decoded.Values) + assert.Equal(t, report.Specimen, decoded.Specimen) + }) + }) + + t.Run("Decode errors on invalid report", func(t *testing.T) { + _, err := rc.Decode([]byte{1, 2, 3}) + assert.EqualError(t, err, "failed to decode report: abi: cannot marshal in to go type: length insufficient 3 require 32") + + longBad := make([]byte, 64) + for i := 0; i < len(longBad); i++ { + longBad[i] = byte(i) + } + _, err = rc.Decode(longBad) + assert.EqualError(t, err, "failed to decode report: abi: improperly encoded uint64 value") + }) +} diff --git a/core/services/llo/keyring.go b/core/services/llo/keyring.go new file mode 100644 index 00000000000..1d6eaebad38 --- /dev/null +++ b/core/services/llo/keyring.go @@ -0,0 +1,79 @@ +package llo + +import ( + "fmt" + + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" + "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" + + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +type LLOOnchainKeyring ocr3types.OnchainKeyring[llotypes.ReportInfo] + +var _ LLOOnchainKeyring = &onchainKeyring{} + +type Key interface { + Sign3(digest ocrtypes.ConfigDigest, seqNr uint64, r ocrtypes.Report) (signature []byte, err error) + Verify3(publicKey ocrtypes.OnchainPublicKey, cd ocrtypes.ConfigDigest, seqNr uint64, r ocrtypes.Report, signature []byte) bool + PublicKey() ocrtypes.OnchainPublicKey + MaxSignatureLength() int +} + +type onchainKeyring struct { + lggr logger.Logger + keys map[llotypes.ReportFormat]Key +} + +func NewOnchainKeyring(lggr logger.Logger, keys map[llotypes.ReportFormat]Key) LLOOnchainKeyring { + return &onchainKeyring{ + lggr.Named("OnchainKeyring"), keys, + } +} + +func (okr *onchainKeyring) PublicKey() types.OnchainPublicKey { + // All public keys combined + var pk []byte + for _, k := range okr.keys { + pk = append(pk, k.PublicKey()...) + } + return pk +} + +func (okr *onchainKeyring) MaxSignatureLength() (n int) { + // Needs to be max of all chain sigs + for _, k := range okr.keys { + n += k.MaxSignatureLength() + } + return +} + +func (okr *onchainKeyring) Sign(digest types.ConfigDigest, seqNr uint64, r ocr3types.ReportWithInfo[llotypes.ReportInfo]) (signature []byte, err error) { + rf := r.Info.ReportFormat + // HACK: sign/verify JSON payloads with EVM keys for now, this makes + // debugging and testing easier + if rf == llotypes.ReportFormatJSON { + rf = llotypes.ReportFormatEVM + } + if key, exists := okr.keys[rf]; exists { + return key.Sign3(digest, seqNr, r.Report) + } + return nil, fmt.Errorf("Sign failed; unsupported report format: %q", r.Info.ReportFormat) +} + +func (okr *onchainKeyring) Verify(key types.OnchainPublicKey, digest types.ConfigDigest, seqNr uint64, r ocr3types.ReportWithInfo[llotypes.ReportInfo], signature []byte) bool { + rf := r.Info.ReportFormat + // HACK: sign/verify JSON payloads with EVM keys for now, this makes + // debugging and testing easier + if rf == llotypes.ReportFormatJSON { + rf = llotypes.ReportFormatEVM + } + if verifier, exists := okr.keys[rf]; exists { + return verifier.Verify3(key, digest, seqNr, r.Report, signature) + } + okr.lggr.Errorf("Verify failed; unsupported report format: %q", r.Info.ReportFormat) + return false +} diff --git a/core/services/llo/keyring_test.go b/core/services/llo/keyring_test.go new file mode 100644 index 00000000000..bf728813801 --- /dev/null +++ b/core/services/llo/keyring_test.go @@ -0,0 +1,123 @@ +package llo + +import ( + "fmt" + "math/rand" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + ocr3types "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" + "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" + + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +var _ Key = &mockKey{} + +type mockKey struct { + format llotypes.ReportFormat + verify bool + maxSignatureLen int +} + +func (m *mockKey) Sign3(digest ocrtypes.ConfigDigest, seqNr uint64, r ocrtypes.Report) (signature []byte, err error) { + return []byte(fmt.Sprintf("sig-%d", m.format)), nil +} + +func (m *mockKey) Verify3(publicKey ocrtypes.OnchainPublicKey, cd ocrtypes.ConfigDigest, seqNr uint64, r ocrtypes.Report, signature []byte) bool { + return m.verify +} + +func (m *mockKey) PublicKey() ocrtypes.OnchainPublicKey { + b := make([]byte, m.maxSignatureLen) + for i := 0; i < m.maxSignatureLen; i++ { + b[i] = byte(255) + } + return ocrtypes.OnchainPublicKey(b) +} + +func (m *mockKey) MaxSignatureLength() int { + return m.maxSignatureLen +} + +func (m *mockKey) reset(format llotypes.ReportFormat) { + m.format = format + m.verify = false +} + +func Test_Keyring(t *testing.T) { + lggr := logger.TestLogger(t) + + ks := map[llotypes.ReportFormat]Key{ + llotypes.ReportFormatEVM: &mockKey{format: llotypes.ReportFormatEVM, maxSignatureLen: 1}, + llotypes.ReportFormatSolana: &mockKey{format: llotypes.ReportFormatSolana, maxSignatureLen: 2}, + llotypes.ReportFormatCosmos: &mockKey{format: llotypes.ReportFormatCosmos, maxSignatureLen: 4}, + llotypes.ReportFormatStarknet: &mockKey{format: llotypes.ReportFormatStarknet, maxSignatureLen: 8}, + } + + kr := NewOnchainKeyring(lggr, ks) + + cases := []struct { + format llotypes.ReportFormat + }{ + { + llotypes.ReportFormatEVM, + }, + { + llotypes.ReportFormatSolana, + }, + { + llotypes.ReportFormatCosmos, + }, + { + llotypes.ReportFormatStarknet, + }, + } + + cd, err := ocrtypes.BytesToConfigDigest(testutils.MustRandBytes(32)) + require.NoError(t, err) + seqNr := rand.Uint64() + t.Run("Sign+Verify", func(t *testing.T) { + for _, tc := range cases { + t.Run(tc.format.String(), func(t *testing.T) { + k := ks[tc.format] + defer k.(*mockKey).reset(tc.format) + + sig, err := kr.Sign(cd, seqNr, ocr3types.ReportWithInfo[llotypes.ReportInfo]{Info: llotypes.ReportInfo{ReportFormat: tc.format}}) + require.NoError(t, err) + + assert.Equal(t, []byte(fmt.Sprintf("sig-%d", tc.format)), sig) + + assert.False(t, kr.Verify(nil, cd, seqNr, ocr3types.ReportWithInfo[llotypes.ReportInfo]{Info: llotypes.ReportInfo{ReportFormat: tc.format}}, sig)) + + k.(*mockKey).verify = true + + for _, tc2 := range cases { + verified := kr.Verify(nil, cd, seqNr, ocr3types.ReportWithInfo[llotypes.ReportInfo]{Info: llotypes.ReportInfo{ReportFormat: tc2.format}}, sig) + if tc.format == tc2.format { + assert.True(t, verified, "expected true for %s", tc2.format) + } else { + assert.False(t, verified, "expected false for %s", tc2.format) + } + } + }) + } + }) + + t.Run("MaxSignatureLength", func(t *testing.T) { + assert.Equal(t, 8+4+2+1, kr.MaxSignatureLength()) + }) + t.Run("PublicKey", func(t *testing.T) { + b := make([]byte, 8+4+2+1) + for i := 0; i < len(b); i++ { + b[i] = byte(255) + } + assert.Equal(t, types.OnchainPublicKey(b), kr.PublicKey()) + }) +} diff --git a/core/services/llo/offchain_config_digester.go b/core/services/llo/offchain_config_digester.go new file mode 100644 index 00000000000..cd4d9afa3a0 --- /dev/null +++ b/core/services/llo/offchain_config_digester.go @@ -0,0 +1,123 @@ +package llo + +import ( + "crypto/ed25519" + "encoding/binary" + "encoding/hex" + "fmt" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/pkg/errors" + + "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/wsrpc/credentials" + + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/exposed_channel_verifier" +) + +// Originally sourced from: https://github.com/smartcontractkit/offchain-reporting/blob/991ebe1462fd56826a1ddfb34287d542acb2baee/lib/offchainreporting2/chains/evmutil/offchain_config_digester.go + +var _ ocrtypes.OffchainConfigDigester = OffchainConfigDigester{} + +func NewOffchainConfigDigester(chainID *big.Int, contractAddress common.Address) OffchainConfigDigester { + return OffchainConfigDigester{chainID, contractAddress} +} + +type OffchainConfigDigester struct { + ChainID *big.Int + ContractAddress common.Address +} + +func (d OffchainConfigDigester) ConfigDigest(cc ocrtypes.ContractConfig) (ocrtypes.ConfigDigest, error) { + signers := []common.Address{} + for i, signer := range cc.Signers { + if len(signer) != 20 { + return ocrtypes.ConfigDigest{}, errors.Errorf("%v-th evm signer should be a 20 byte address, but got %x", i, signer) + } + a := common.BytesToAddress(signer) + signers = append(signers, a) + } + transmitters := []credentials.StaticSizedPublicKey{} + for i, transmitter := range cc.Transmitters { + if len(transmitter) != 2*ed25519.PublicKeySize { + return ocrtypes.ConfigDigest{}, errors.Errorf("%v-th evm transmitter should be a 64 character hex-encoded ed25519 public key, but got '%v' (%d chars)", i, transmitter, len(transmitter)) + } + var t credentials.StaticSizedPublicKey + b, err := hex.DecodeString(string(transmitter)) + if err != nil { + return ocrtypes.ConfigDigest{}, errors.Wrapf(err, "%v-th evm transmitter is not valid hex, got: %q", i, transmitter) + } + copy(t[:], b) + + transmitters = append(transmitters, t) + } + + return configDigest( + d.ChainID, + d.ContractAddress, + cc.ConfigCount, + signers, + transmitters, + cc.F, + cc.OnchainConfig, + cc.OffchainConfigVersion, + cc.OffchainConfig, + ) +} + +func (d OffchainConfigDigester) ConfigDigestPrefix() (ocrtypes.ConfigDigestPrefix, error) { + return ocrtypes.ConfigDigestPrefixLLO, nil +} + +func makeConfigDigestArgs() abi.Arguments { + abi, err := abi.JSON(strings.NewReader(exposed_channel_verifier.ExposedChannelVerifierABI)) + if err != nil { + // assertion + panic(fmt.Sprintf("could not parse aggregator ABI: %s", err.Error())) + } + return abi.Methods["exposedConfigDigestFromConfigData"].Inputs +} + +var configDigestArgs = makeConfigDigestArgs() + +func configDigest( + chainID *big.Int, + contractAddress common.Address, + configCount uint64, + oracles []common.Address, + transmitters []credentials.StaticSizedPublicKey, + f uint8, + onchainConfig []byte, + offchainConfigVersion uint64, + offchainConfig []byte, +) (types.ConfigDigest, error) { + msg, err := configDigestArgs.Pack( + chainID, + contractAddress, + configCount, + oracles, + transmitters, + f, + onchainConfig, + offchainConfigVersion, + offchainConfig, + ) + if err != nil { + return types.ConfigDigest{}, fmt.Errorf("could not pack config digest args: %v", err) + } + rawHash := crypto.Keccak256(msg) + configDigest := types.ConfigDigest{} + if n := copy(configDigest[:], rawHash); n != len(configDigest) { + return types.ConfigDigest{}, fmt.Errorf("copied too little data: %d/%d", n, len(configDigest)) + } + binary.BigEndian.PutUint16(configDigest[:2], uint16(ocrtypes.ConfigDigestPrefixLLO)) + if !(configDigest[0] == 0 && configDigest[1] == 9) { + return types.ConfigDigest{}, fmt.Errorf("wrong ConfigDigestPrefix; got: %x, expected: %d", configDigest[:2], ocrtypes.ConfigDigestPrefixLLO) + } + return configDigest, nil +} diff --git a/core/services/llo/offchain_config_digester_test.go b/core/services/llo/offchain_config_digester_test.go new file mode 100644 index 00000000000..0de9117e391 --- /dev/null +++ b/core/services/llo/offchain_config_digester_test.go @@ -0,0 +1,55 @@ +package llo + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/stretchr/testify/require" +) + +func Test_OffchainConfigDigester_ConfigDigest(t *testing.T) { + // ChainID and ContractAddress are taken into account for computation + cd1, err := OffchainConfigDigester{ChainID: big.NewInt(0)}.ConfigDigest(types.ContractConfig{}) + require.NoError(t, err) + cd2, err := OffchainConfigDigester{ChainID: big.NewInt(0)}.ConfigDigest(types.ContractConfig{}) + require.NoError(t, err) + cd3, err := OffchainConfigDigester{ChainID: big.NewInt(1)}.ConfigDigest(types.ContractConfig{}) + require.NoError(t, err) + cd4, err := OffchainConfigDigester{ChainID: big.NewInt(1), ContractAddress: common.Address{1}}.ConfigDigest(types.ContractConfig{}) + require.NoError(t, err) + + require.Equal(t, cd1, cd2) + require.NotEqual(t, cd2, cd3) + require.NotEqual(t, cd2, cd4) + require.NotEqual(t, cd3, cd4) + + // malformed signers + _, err = OffchainConfigDigester{}.ConfigDigest(types.ContractConfig{ + Signers: []types.OnchainPublicKey{{1, 2}}, + }) + require.Error(t, err) + + // malformed transmitters + _, err = OffchainConfigDigester{}.ConfigDigest(types.ContractConfig{ + Transmitters: []types.Account{"0x"}, + }) + require.Error(t, err) + + _, err = OffchainConfigDigester{}.ConfigDigest(types.ContractConfig{ + Transmitters: []types.Account{"7343581f55146951b0f678dc6cfa8fd360e2f353"}, + }) + require.Error(t, err) + + _, err = OffchainConfigDigester{}.ConfigDigest(types.ContractConfig{ + Transmitters: []types.Account{"7343581f55146951b0f678dc6cfa8fd360e2f353aabbccddeeffaaccddeeffaz"}, + }) + require.Error(t, err) + + // well-formed transmitters + _, err = OffchainConfigDigester{ChainID: big.NewInt(0)}.ConfigDigest(types.ContractConfig{ + Transmitters: []types.Account{"7343581f55146951b0f678dc6cfa8fd360e2f353aabbccddeeffaaccddeeffaa"}, + }) + require.NoError(t, err) +} diff --git a/core/services/llo/onchain_channel_definition_cache.go b/core/services/llo/onchain_channel_definition_cache.go new file mode 100644 index 00000000000..af35d237b98 --- /dev/null +++ b/core/services/llo/onchain_channel_definition_cache.go @@ -0,0 +1,252 @@ +package llo + +import ( + "context" + "database/sql" + "errors" + "fmt" + "maps" + "strings" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + + "github.com/smartcontractkit/chainlink-common/pkg/services" + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" + + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/channel_config_store" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/pg" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +type ChannelDefinitionCacheORM interface { + // TODO: What about delete/cleanup? + // https://smartcontract-it.atlassian.net/browse/MERC-3653 + LoadChannelDefinitions(ctx context.Context, addr common.Address) (dfns llotypes.ChannelDefinitions, blockNum int64, err error) + StoreChannelDefinitions(ctx context.Context, addr common.Address, dfns llotypes.ChannelDefinitions, blockNum int64) (err error) +} + +var channelConfigStoreABI abi.ABI + +func init() { + var err error + channelConfigStoreABI, err = abi.JSON(strings.NewReader(channel_config_store.ChannelConfigStoreABI)) + if err != nil { + panic(err) + } +} + +var _ llotypes.ChannelDefinitionCache = &channelDefinitionCache{} + +type channelDefinitionCache struct { + services.StateMachine + + orm ChannelDefinitionCacheORM + + filterName string + lp logpoller.LogPoller + fromBlock int64 + addr common.Address + lggr logger.Logger + + definitionsMu sync.RWMutex + definitions llotypes.ChannelDefinitions + definitionsBlockNum int64 + + wg sync.WaitGroup + chStop chan struct{} +} + +var ( + topicNewChannelDefinition = (channel_config_store.ChannelConfigStoreNewChannelDefinition{}).Topic() + topicChannelDefinitionRemoved = (channel_config_store.ChannelConfigStoreChannelDefinitionRemoved{}).Topic() + + allTopics = []common.Hash{topicNewChannelDefinition, topicChannelDefinitionRemoved} +) + +func NewChannelDefinitionCache(lggr logger.Logger, orm ChannelDefinitionCacheORM, lp logpoller.LogPoller, addr common.Address, fromBlock int64) llotypes.ChannelDefinitionCache { + filterName := logpoller.FilterName("OCR3 LLO ChannelDefinitionCachePoller", addr.String()) + return &channelDefinitionCache{ + services.StateMachine{}, + orm, + filterName, + lp, + 0, + addr, + lggr.Named("ChannelDefinitionCache").With("addr", addr, "fromBlock", fromBlock), + sync.RWMutex{}, + nil, + fromBlock, + sync.WaitGroup{}, + make(chan struct{}), + } +} + +func (c *channelDefinitionCache) Start(ctx context.Context) error { + // Initial load from DB, then async poll from chain thereafter + return c.StartOnce("ChannelDefinitionCache", func() (err error) { + err = c.lp.RegisterFilter(logpoller.Filter{Name: c.filterName, EventSigs: allTopics, Addresses: []common.Address{c.addr}}, pg.WithParentCtx(ctx)) + if err != nil { + return err + } + if definitions, definitionsBlockNum, err := c.orm.LoadChannelDefinitions(ctx, c.addr); err != nil { + return err + } else if definitions != nil { + c.definitions = definitions + c.definitionsBlockNum = definitionsBlockNum + } else { + // ensure non-nil map ready for assignment later + c.definitions = make(llotypes.ChannelDefinitions) + // leave c.definitionsBlockNum as provided fromBlock argument + } + c.wg.Add(1) + go c.poll() + return nil + }) +} + +// TODO: make this configurable? +const pollInterval = 1 * time.Second + +func (c *channelDefinitionCache) poll() { + defer c.wg.Done() + + pollT := time.NewTicker(utils.WithJitter(pollInterval)) + + for { + select { + case <-c.chStop: + return + case <-pollT.C: + if n, err := c.fetchFromChain(); err != nil { + // TODO: retry with backoff? + // https://smartcontract-it.atlassian.net/browse/MERC-3653 + c.lggr.Errorw("Failed to fetch channel definitions from chain", "err", err) + continue + } else { + if n > 0 { + c.lggr.Infow("Updated channel definitions", "nLogs", n, "definitionsBlockNum", c.definitionsBlockNum) + } else { + c.lggr.Debugw("No new channel definitions", "nLogs", 0, "definitionsBlockNum", c.definitionsBlockNum) + } + } + } + } +} + +func (c *channelDefinitionCache) fetchFromChain() (nLogs int, err error) { + // TODO: Pass context + // https://smartcontract-it.atlassian.net/browse/MERC-3653 + latest, err := c.lp.LatestBlock() + if errors.Is(err, sql.ErrNoRows) { + c.lggr.Debug("Logpoller has no logs yet, skipping poll") + return 0, nil + } else if err != nil { + return 0, err + } + toBlock := latest.BlockNumber + + fromBlock := c.definitionsBlockNum + + if toBlock <= fromBlock { + return 0, nil + } + + ctx, cancel := services.StopChan(c.chStop).NewCtx() + defer cancel() + // NOTE: We assume that log poller returns logs in ascending order chronologically + logs, err := c.lp.LogsWithSigs(fromBlock, toBlock, allTopics, c.addr, pg.WithParentCtx(ctx)) + if err != nil { + // TODO: retry? + // https://smartcontract-it.atlassian.net/browse/MERC-3653 + return 0, err + } + for _, log := range logs { + if err = c.applyLog(log); err != nil { + return 0, err + } + } + + // Use context.Background() here because we want to try to save even if we + // are closing + if err = c.orm.StoreChannelDefinitions(context.Background(), c.addr, c.Definitions(), toBlock); err != nil { + return 0, err + } + + c.definitionsBlockNum = toBlock + + return len(logs), nil +} + +func (c *channelDefinitionCache) applyLog(log logpoller.Log) error { + switch log.EventSig { + case topicNewChannelDefinition: + unpacked := new(channel_config_store.ChannelConfigStoreNewChannelDefinition) + + err := channelConfigStoreABI.UnpackIntoInterface(unpacked, "NewChannelDefinition", log.Data) + if err != nil { + return fmt.Errorf("failed to unpack log data: %w", err) + } + + c.applyNewChannelDefinition(unpacked) + case topicChannelDefinitionRemoved: + unpacked := new(channel_config_store.ChannelConfigStoreChannelDefinitionRemoved) + + err := channelConfigStoreABI.UnpackIntoInterface(unpacked, "ChannelDefinitionRemoved", log.Data) + if err != nil { + return fmt.Errorf("failed to unpack log data: %w", err) + } + + c.applyChannelDefinitionRemoved(unpacked) + default: + // don't return error here, we want to ignore unrecognized logs and + // continue rather than interrupting the loop + c.lggr.Errorw("Unexpected log topic", "topic", log.EventSig.Hex()) + } + return nil +} + +func (c *channelDefinitionCache) applyNewChannelDefinition(log *channel_config_store.ChannelConfigStoreNewChannelDefinition) { + streamIDs := make([]llotypes.StreamID, len(log.ChannelDefinition.StreamIDs)) + copy(streamIDs, log.ChannelDefinition.StreamIDs) + c.definitionsMu.Lock() + defer c.definitionsMu.Unlock() + c.definitions[log.ChannelId] = llotypes.ChannelDefinition{ + ReportFormat: llotypes.ReportFormat(log.ChannelDefinition.ReportFormat), + ChainSelector: log.ChannelDefinition.ChainSelector, + StreamIDs: streamIDs, + } +} + +func (c *channelDefinitionCache) applyChannelDefinitionRemoved(log *channel_config_store.ChannelConfigStoreChannelDefinitionRemoved) { + c.definitionsMu.Lock() + defer c.definitionsMu.Unlock() + delete(c.definitions, log.ChannelId) +} + +func (c *channelDefinitionCache) Close() error { + // TODO: unregister filter (on job delete)? + // https://smartcontract-it.atlassian.net/browse/MERC-3653 + return c.StopOnce("ChannelDefinitionCache", func() error { + close(c.chStop) + c.wg.Wait() + return nil + }) +} + +func (c *channelDefinitionCache) HealthReport() map[string]error { + report := map[string]error{c.Name(): c.Healthy()} + return report +} + +func (c *channelDefinitionCache) Name() string { return c.lggr.Name() } + +func (c *channelDefinitionCache) Definitions() llotypes.ChannelDefinitions { + c.definitionsMu.RLock() + defer c.definitionsMu.RUnlock() + return maps.Clone(c.definitions) +} diff --git a/core/services/llo/onchain_channel_definition_cache_test.go b/core/services/llo/onchain_channel_definition_cache_test.go new file mode 100644 index 00000000000..28e89a9c987 --- /dev/null +++ b/core/services/llo/onchain_channel_definition_cache_test.go @@ -0,0 +1,26 @@ +package llo + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" +) + +func Test_ChannelDefinitionCache(t *testing.T) { + t.Run("Definitions", func(t *testing.T) { + // NOTE: this is covered more thoroughly in the integration tests + dfns := llotypes.ChannelDefinitions(map[llotypes.ChannelID]llotypes.ChannelDefinition{ + 1: { + ReportFormat: llotypes.ReportFormat(43), + ChainSelector: 42, + StreamIDs: []llotypes.StreamID{1, 2, 3}, + }, + }) + + cdc := &channelDefinitionCache{definitions: dfns} + + assert.Equal(t, dfns, cdc.Definitions()) + }) +} diff --git a/core/services/llo/orm.go b/core/services/llo/orm.go new file mode 100644 index 00000000000..e046d62ad89 --- /dev/null +++ b/core/services/llo/orm.go @@ -0,0 +1,66 @@ +package llo + +import ( + "context" + "database/sql" + "encoding/json" + "errors" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" + + "github.com/smartcontractkit/chainlink/v2/core/services/pg" +) + +type ORM interface { + ChannelDefinitionCacheORM +} + +var _ ORM = &orm{} + +type orm struct { + q pg.Queryer + evmChainID *big.Int +} + +func NewORM(q pg.Queryer, evmChainID *big.Int) ORM { + return &orm{q, evmChainID} +} + +func (o *orm) LoadChannelDefinitions(ctx context.Context, addr common.Address) (dfns llotypes.ChannelDefinitions, blockNum int64, err error) { + type scd struct { + Definitions []byte `db:"definitions"` + BlockNum int64 `db:"block_num"` + } + var scanned scd + err = o.q.GetContext(ctx, &scanned, "SELECT definitions, block_num FROM channel_definitions WHERE evm_chain_id = $1 AND addr = $2", o.evmChainID.String(), addr) + if errors.Is(err, sql.ErrNoRows) { + return dfns, blockNum, nil + } else if err != nil { + return nil, 0, fmt.Errorf("failed to LoadChannelDefinitions; %w", err) + } + + if err = json.Unmarshal(scanned.Definitions, &dfns); err != nil { + return nil, 0, fmt.Errorf("failed to LoadChannelDefinitions; JSON Unmarshal failure; %w", err) + } + + return dfns, scanned.BlockNum, nil +} + +// TODO: Test this method +// https://smartcontract-it.atlassian.net/jira/software/c/projects/MERC/issues/MERC-3653 +func (o *orm) StoreChannelDefinitions(ctx context.Context, addr common.Address, dfns llotypes.ChannelDefinitions, blockNum int64) error { + _, err := o.q.ExecContext(ctx, ` +INSERT INTO channel_definitions (evm_chain_id, addr, definitions, block_num, updated_at) +VALUES ($1, $2, $3, $4, NOW()) +ON CONFLICT (evm_chain_id, addr) DO UPDATE +SET definitions = $3, block_num = $4, updated_at = NOW() +`, o.evmChainID.String(), addr, dfns, blockNum) + if err != nil { + return fmt.Errorf("StoreChannelDefinitions failed: %w", err) + } + return nil +} diff --git a/core/services/llo/orm_test.go b/core/services/llo/orm_test.go new file mode 100644 index 00000000000..63a6ac21e3b --- /dev/null +++ b/core/services/llo/orm_test.go @@ -0,0 +1,101 @@ +package llo + +import ( + "fmt" + "math/rand" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" + + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" +) + +func Test_ORM(t *testing.T) { + db := pgtest.NewSqlxDB(t) + orm := NewORM(db, testutils.FixtureChainID) + ctx := testutils.Context(t) + + addr1 := testutils.NewAddress() + addr2 := testutils.NewAddress() + addr3 := testutils.NewAddress() + + t.Run("LoadChannelDefinitions", func(t *testing.T) { + t.Run("returns zero values if nothing in database", func(t *testing.T) { + cd, blockNum, err := orm.LoadChannelDefinitions(ctx, addr1) + require.NoError(t, err) + + assert.Zero(t, cd) + assert.Zero(t, blockNum) + + }) + t.Run("loads channel definitions from database", func(t *testing.T) { + expectedBlockNum := rand.Int63() + expectedBlockNum2 := rand.Int63() + cid1 := rand.Uint32() + cid2 := rand.Uint32() + + channelDefsJSON := fmt.Sprintf(` +{ + "%d": { + "reportFormat": 42, + "chainSelector": 142, + "streamIds": [1, 2] + }, + "%d": { + "reportFormat": 42, + "chainSelector": 142, + "streamIds": [1, 3] + } +} + `, cid1, cid2) + pgtest.MustExec(t, db, ` + INSERT INTO channel_definitions(addr, evm_chain_id, definitions, block_num, updated_at) + VALUES ( $1, $2, $3, $4, NOW()) + `, addr1, testutils.FixtureChainID.String(), channelDefsJSON, expectedBlockNum) + + pgtest.MustExec(t, db, ` + INSERT INTO channel_definitions(addr, evm_chain_id, definitions, block_num, updated_at) + VALUES ( $1, $2, $3, $4, NOW()) + `, addr2, testutils.FixtureChainID.String(), `{}`, expectedBlockNum2) + + { + // alternative chain ID; we expect these ones to be ignored + pgtest.MustExec(t, db, ` + INSERT INTO channel_definitions(addr, evm_chain_id, definitions, block_num, updated_at) + VALUES ( $1, $2, $3, $4, NOW()) + `, addr1, testutils.SimulatedChainID.String(), channelDefsJSON, expectedBlockNum) + pgtest.MustExec(t, db, ` + INSERT INTO channel_definitions(addr, evm_chain_id, definitions, block_num, updated_at) + VALUES ( $1, $2, $3, $4, NOW()) + `, addr3, testutils.SimulatedChainID.String(), channelDefsJSON, expectedBlockNum) + } + + cd, blockNum, err := orm.LoadChannelDefinitions(ctx, addr1) + require.NoError(t, err) + + assert.Equal(t, llotypes.ChannelDefinitions{ + cid1: llotypes.ChannelDefinition{ + ReportFormat: 42, + ChainSelector: 142, + StreamIDs: []llotypes.StreamID{1, 2}, + }, + cid2: llotypes.ChannelDefinition{ + ReportFormat: 42, + ChainSelector: 142, + StreamIDs: []llotypes.StreamID{1, 3}, + }, + }, cd) + assert.Equal(t, expectedBlockNum, blockNum) + + cd, blockNum, err = orm.LoadChannelDefinitions(ctx, addr2) + require.NoError(t, err) + + assert.Equal(t, llotypes.ChannelDefinitions{}, cd) + assert.Equal(t, expectedBlockNum2, blockNum) + }) + }) +} diff --git a/core/services/llo/static_channel_definitions_cache.go b/core/services/llo/static_channel_definitions_cache.go new file mode 100644 index 00000000000..bf26dd781ee --- /dev/null +++ b/core/services/llo/static_channel_definitions_cache.go @@ -0,0 +1,56 @@ +package llo + +import ( + "context" + "encoding/json" + + "github.com/smartcontractkit/chainlink-common/pkg/services" + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" + + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +// A CDC that loads a static JSON of channel definitions; useful for +// benchmarking and testing + +var _ llotypes.ChannelDefinitionCache = &staticCDC{} + +type staticCDC struct { + services.StateMachine + lggr logger.Logger + + definitions llotypes.ChannelDefinitions +} + +func NewStaticChannelDefinitionCache(lggr logger.Logger, dfnstr string) (llotypes.ChannelDefinitionCache, error) { + var definitions llotypes.ChannelDefinitions + if err := json.Unmarshal([]byte(dfnstr), &definitions); err != nil { + return nil, err + } + return &staticCDC{services.StateMachine{}, lggr.Named("StaticChannelDefinitionCache"), definitions}, nil +} + +func (s *staticCDC) Start(context.Context) error { + return s.StartOnce("StaticChannelDefinitionCache", func() error { + return nil + }) +} + +func (s *staticCDC) Close() error { + return s.StopOnce("StaticChannelDefinitionCache", func() error { + return nil + }) +} + +func (s *staticCDC) Definitions() llotypes.ChannelDefinitions { + return s.definitions +} + +func (s *staticCDC) HealthReport() map[string]error { + report := map[string]error{s.Name(): s.Healthy()} + return report +} + +func (s *staticCDC) Name() string { + return s.lggr.Name() +} diff --git a/core/services/llo/transmitter.go b/core/services/llo/transmitter.go new file mode 100644 index 00000000000..eef211ab5d5 --- /dev/null +++ b/core/services/llo/transmitter.go @@ -0,0 +1,153 @@ +package llo + +import ( + "context" + "crypto/ed25519" + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/smartcontractkit/libocr/offchainreporting2/chains/evmutil" + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" + "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + "github.com/smartcontractkit/chainlink-common/pkg/services" + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" + + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ocr2key" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/pb" +) + +// LLO Transmitter implementation, based on +// core/services/relay/evm/mercury/transmitter.go + +// TODO: prom metrics (common with mercury/transmitter.go?) +// https://smartcontract-it.atlassian.net/browse/MERC-3659 + +const ( + // Mercury server error codes + DuplicateReport = 2 + // TODO: revisit these values in light of parallel composition + // https://smartcontract-it.atlassian.net/browse/MERC-3659 + // maxTransmitQueueSize = 10_000 + // maxDeleteQueueSize = 10_000 + // transmitTimeout = 5 * time.Second +) + +var PayloadTypes = getPayloadTypes() + +func getPayloadTypes() abi.Arguments { + mustNewType := func(t string) abi.Type { + result, err := abi.NewType(t, "", []abi.ArgumentMarshaling{}) + if err != nil { + panic(fmt.Sprintf("Unexpected error during abi.NewType: %s", err)) + } + return result + } + return abi.Arguments([]abi.Argument{ + {Name: "reportContext", Type: mustNewType("bytes32[2]")}, + {Name: "report", Type: mustNewType("bytes")}, + {Name: "rawRs", Type: mustNewType("bytes32[]")}, + {Name: "rawSs", Type: mustNewType("bytes32[]")}, + {Name: "rawVs", Type: mustNewType("bytes32")}, + }) +} + +type Transmitter interface { + llotypes.Transmitter + services.Service +} + +type transmitter struct { + services.StateMachine + lggr logger.Logger + rpcClient wsrpc.Client + fromAccount string +} + +func NewTransmitter(lggr logger.Logger, rpcClient wsrpc.Client, fromAccount ed25519.PublicKey) Transmitter { + return &transmitter{ + services.StateMachine{}, + lggr, + rpcClient, + fmt.Sprintf("%x", fromAccount), + } +} + +func (t *transmitter) Start(ctx context.Context) error { + return nil +} + +func (t *transmitter) Close() error { + return nil +} + +func (t *transmitter) HealthReport() map[string]error { + report := map[string]error{t.Name(): t.Healthy()} + services.CopyHealth(report, t.rpcClient.HealthReport()) + return report +} + +func (t *transmitter) Name() string { return t.lggr.Name() } + +func (t *transmitter) Transmit( + ctx context.Context, + digest types.ConfigDigest, + seqNr uint64, + report ocr3types.ReportWithInfo[llotypes.ReportInfo], + sigs []types.AttributedOnchainSignature, +) (err error) { + var payload []byte + + switch report.Info.ReportFormat { + case llotypes.ReportFormatJSON: + fallthrough + case llotypes.ReportFormatEVM: + payload, err = encodeEVM(digest, seqNr, report.Report, sigs) + default: + return fmt.Errorf("Transmit failed; unsupported report format: %q", report.Info.ReportFormat) + } + + if err != nil { + return fmt.Errorf("Transmit: encode failed; %w", err) + } + + req := &pb.TransmitRequest{ + Payload: payload, + ReportFormat: uint32(report.Info.ReportFormat), + } + + // TODO: persistenceManager and queueing, error handling, retry etc + // https://smartcontract-it.atlassian.net/browse/MERC-3659 + _, err = t.rpcClient.Transmit(ctx, req) + return err +} + +func encodeEVM(digest types.ConfigDigest, seqNr uint64, report ocr2types.Report, sigs []types.AttributedOnchainSignature) ([]byte, error) { + var rs [][32]byte + var ss [][32]byte + var vs [32]byte + for i, as := range sigs { + r, s, v, err := evmutil.SplitSignature(as.Signature) + if err != nil { + return nil, fmt.Errorf("eventTransmit(ev): error in SplitSignature: %w", err) + } + rs = append(rs, r) + ss = append(ss, s) + vs[i] = v + } + rawReportCtx := ocr2key.RawReportContext3(digest, seqNr) + + payload, err := PayloadTypes.Pack(rawReportCtx, []byte(report), rs, ss, vs) + if err != nil { + return nil, fmt.Errorf("abi.Pack failed; %w", err) + } + return payload, nil +} + +// FromAccount returns the stringified (hex) CSA public key +func (t *transmitter) FromAccount() (ocr2types.Account, error) { + return ocr2types.Account(t.fromAccount), nil +} diff --git a/core/services/llo/transmitter_test.go b/core/services/llo/transmitter_test.go new file mode 100644 index 00000000000..eb231494c0b --- /dev/null +++ b/core/services/llo/transmitter_test.go @@ -0,0 +1,7 @@ +package llo + +import "testing" + +func Test_Transmitter(t *testing.T) { + // TODO: https://smartcontract-it.atlassian.net/browse/MERC-3659 +} diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 754f48013bd..336d1ae3800 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -36,6 +36,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink-common/pkg/loop/reportingplugins" "github.com/smartcontractkit/chainlink-common/pkg/types" + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" "github.com/smartcontractkit/chainlink/v2/core/bridges" @@ -44,11 +45,14 @@ import ( "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/chaintype" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ocr2key" + "github.com/smartcontractkit/chainlink/v2/core/services/llo" "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" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/generic" + lloconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/llo/config" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/median" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/mercury" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper" @@ -70,6 +74,7 @@ import ( evmmercury "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury" mercuryutils "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" evmrelaytypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/services/streams" "github.com/smartcontractkit/chainlink/v2/core/services/synchronization" "github.com/smartcontractkit/chainlink/v2/core/services/telemetry" "github.com/smartcontractkit/chainlink/v2/plugins" @@ -107,6 +112,7 @@ type Delegate struct { bridgeORM bridges.ORM mercuryORM evmmercury.ORM pipelineRunner pipeline.Runner + streamRegistry streams.Getter peerWrapper *ocrcommon.SingletonPeerWrapper monitoringEndpointGen telemetry.MonitoringEndpointGenerator cfg DelegateConfig @@ -219,6 +225,7 @@ func NewDelegate( bridgeORM bridges.ORM, mercuryORM evmmercury.ORM, pipelineRunner pipeline.Runner, + streamRegistry streams.Getter, peerWrapper *ocrcommon.SingletonPeerWrapper, monitoringEndpointGen telemetry.MonitoringEndpointGenerator, legacyChains legacyevm.LegacyChainContainer, @@ -238,6 +245,7 @@ func NewDelegate( bridgeORM: bridgeORM, mercuryORM: mercuryORM, pipelineRunner: pipelineRunner, + streamRegistry: streamRegistry, peerWrapper: peerWrapper, monitoringEndpointGen: monitoringEndpointGen, legacyChains: legacyChains, @@ -435,6 +443,9 @@ func (d *Delegate) ServicesForSpec(jb job.Job) ([]job.ServiceCtx, error) { case types.Mercury: return d.newServicesMercury(ctx, lggr, jb, bootstrapPeers, kb, ocrDB, lc, ocrLogger) + case types.LLO: + return d.newServicesLLO(ctx, lggr, jb, bootstrapPeers, kb, ocrDB, lc, ocrLogger) + case types.Median: return d.newServicesMedian(ctx, lggr, jb, bootstrapPeers, kb, ocrDB, lc, ocrLogger) @@ -467,7 +478,7 @@ func (d *Delegate) ServicesForSpec(jb job.Job) ([]job.ServiceCtx, error) { func GetEVMEffectiveTransmitterID(jb *job.Job, chain legacyevm.Chain, lggr logger.SugaredLogger) (string, error) { spec := jb.OCR2OracleSpec - if spec.PluginType == types.Mercury { + if spec.PluginType == types.Mercury || spec.PluginType == types.LLO { return spec.TransmitterID.String, nil } @@ -775,6 +786,135 @@ func (d *Delegate) newServicesMercury( return mercuryServices, err2 } +func (d *Delegate) newServicesLLO( + ctx context.Context, + lggr logger.SugaredLogger, + jb job.Job, + bootstrapPeers []commontypes.BootstrapperLocator, + kb ocr2key.KeyBundle, + ocrDB *db, + lc ocrtypes.LocalConfig, + ocrLogger commontypes.Logger, +) ([]job.ServiceCtx, error) { + lggr = logger.Sugared(lggr.Named("LLO")) + spec := jb.OCR2OracleSpec + transmitterID := spec.TransmitterID.String + if len(transmitterID) != 64 { + return nil, errors.Errorf("ServicesForSpec: streams job type requires transmitter ID to be a 32-byte hex string, got: %q", transmitterID) + } + if _, err := hex.DecodeString(transmitterID); err != nil { + return nil, errors.Wrapf(err, "ServicesForSpec: streams job type requires transmitter ID to be a 32-byte hex string, got: %q", transmitterID) + } + + rid, err := spec.RelayID() + if err != nil { + return nil, ErrJobSpecNoRelayer{Err: err, PluginName: "streams"} + } + if rid.Network != relay.EVM { + return nil, fmt.Errorf("streams services: expected EVM relayer got %s", rid.Network) + } + relayer, err := d.RelayGetter.Get(rid) + if err != nil { + return nil, ErrRelayNotEnabled{Err: err, Relay: spec.Relay, PluginName: "streams"} + } + + provider, err2 := relayer.NewLLOProvider(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 err2 != nil { + return nil, err2 + } + + var pluginCfg lloconfig.PluginConfig + if err = json.Unmarshal(spec.PluginConfig.Bytes(), &pluginCfg); err != nil { + return nil, err + } + + kbm := make(map[llotypes.ReportFormat]llo.Key) + for rfStr, kbid := range pluginCfg.KeyBundleIDs { + k, err3 := d.ks.Get(kbid) + if err3 != nil { + return nil, fmt.Errorf("job %d (%s) specified key bundle ID %q for report format %s, but got error trying to load it: %w", jb.ID, jb.Name.ValueOrZero(), kbid, rfStr, err3) + } + rf, err4 := llotypes.ReportFormatFromString(rfStr) + if err4 != nil { + return nil, fmt.Errorf("job %d (%s) specified key bundle ID %q for report format %s, but it is not a recognized report format: %w", jb.ID, jb.Name.ValueOrZero(), kbid, rfStr, err4) + } + kbm[rf] = k + } + // NOTE: This is a bit messy because we assume chain type matches report + // format, and it may not in all cases. We don't yet know what report + // formats we need or how they correspond to chain types, so assume it's + // 1:1 for now but will change in future + // + // https://smartcontract-it.atlassian.net/browse/MERC-3722 + for _, s := range chaintype.SupportedChainTypes { + rf, err3 := llotypes.ReportFormatFromString(string(s)) + if err3 != nil { + return nil, fmt.Errorf("job %d (%s) has a chain type with no matching report format %s: %w", jb.ID, jb.Name.ValueOrZero(), s, err3) + } + if _, exists := kbm[rf]; !exists { + // Use the first if unspecified + kbs, err4 := d.ks.GetAllOfType(s) + if err4 != nil { + return nil, err4 + } + if len(kbs) == 0 { + // unsupported key type + continue + } else if len(kbs) > 1 { + lggr.Debugf("Multiple on-chain signing keys found for report format %s, using the first", rf.String()) + } + kbm[rf] = kbs[0] + } + } + + // FIXME: This is a bit confusing because the OCR2 key bundle actually + // includes an EVM on-chain key... but LLO only uses the key bundle for the + // offchain keys and the suppoprted onchain keys are defined in the plugin + // config on the job spec instead. + // https://smartcontract-it.atlassian.net/browse/MERC-3594 + lggr.Infof("Using on-chain signing keys for LLO job %d (%s): %v", jb.ID, jb.Name.ValueOrZero(), kbm) + kr := llo.NewOnchainKeyring(lggr, kbm) + + cfg := llo.DelegateConfig{ + Logger: lggr, + Queryer: pg.NewQ(d.db, lggr, d.cfg.Database()), + Runner: d.pipelineRunner, + Registry: d.streamRegistry, + + ChannelDefinitionCache: provider.ChannelDefinitionCache(), + + BinaryNetworkEndpointFactory: d.peerWrapper.Peer2, + V2Bootstrappers: bootstrapPeers, + ContractTransmitter: provider.ContractTransmitter(), + ContractConfigTracker: provider.ContractConfigTracker(), + Database: ocrDB, + LocalConfig: lc, + // TODO: Telemetry for llo + // https://smartcontract-it.atlassian.net/browse/MERC-3603 + MonitoringEndpoint: nil, + OffchainConfigDigester: provider.OffchainConfigDigester(), + OffchainKeyring: kb, + OnchainKeyring: kr, + OCRLogger: ocrLogger, + } + oracle, err := llo.NewDelegate(cfg) + if err != nil { + return nil, err + } + return []job.ServiceCtx{provider, oracle}, nil +} + func (d *Delegate) newServicesMedian( ctx context.Context, lggr logger.SugaredLogger, diff --git a/core/services/ocr2/plugins/llo/config/config.go b/core/services/ocr2/plugins/llo/config/config.go new file mode 100644 index 00000000000..15bb5e816a8 --- /dev/null +++ b/core/services/ocr2/plugins/llo/config/config.go @@ -0,0 +1,112 @@ +// config is a separate package so that we can validate +// the config in other packages, for example in job at job create time. + +package config + +import ( + "encoding/json" + "errors" + "fmt" + "net/url" + "regexp" + + "github.com/ethereum/go-ethereum/common" + + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" + + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +type PluginConfig struct { + RawServerURL string `json:"serverURL" toml:"serverURL"` + ServerPubKey utils.PlainHexBytes `json:"serverPubKey" toml:"serverPubKey"` + + ChannelDefinitionsContractAddress common.Address `json:"channelDefinitionsContractAddress" toml:"channelDefinitionsContractAddress"` + ChannelDefinitionsContractFromBlock int64 `json:"channelDefinitionsContractFromBlock" toml:"channelDefinitionsContractFromBlock"` + + // NOTE: ChannelDefinitions is an override. + // If Channe}lDefinitions is specified, values for + // ChannelDefinitionsContractAddress and + // ChannelDefinitionsContractFromBlock will be ignored + ChannelDefinitions string `json:"channelDefinitions" toml:"channelDefinitions"` + + // BenchmarkMode is a flag to enable benchmarking mode. In this mode, the + // transmitter will not transmit anything at all and instead emit + // logs/metrics. + BenchmarkMode bool `json:"benchmarkMode" toml:"benchmarkMode"` + + // KeyBundleIDs maps supported keys to their respective bundle IDs + // Key must match llo's ReportFormat + KeyBundleIDs map[string]string `json:"keyBundleIDs" toml:"keyBundleIDs"` +} + +func (p PluginConfig) Validate() (merr error) { + if p.RawServerURL == "" { + merr = errors.New("llo: ServerURL must be specified") + } else { + var normalizedURI string + if schemeRegexp.MatchString(p.RawServerURL) { + normalizedURI = p.RawServerURL + } else { + normalizedURI = fmt.Sprintf("wss://%s", p.RawServerURL) + } + uri, err := url.ParseRequestURI(normalizedURI) + if err != nil { + merr = fmt.Errorf("llo: invalid value for ServerURL: %w", err) + } else if uri.Scheme != "wss" { + merr = fmt.Errorf(`llo: invalid scheme specified for MercuryServer, got: %q (scheme: %q) but expected a websocket url e.g. "192.0.2.2:4242" or "wss://192.0.2.2:4242"`, p.RawServerURL, uri.Scheme) + } + } + + if p.ChannelDefinitions != "" { + if p.ChannelDefinitionsContractAddress != (common.Address{}) { + merr = errors.Join(merr, errors.New("llo: ChannelDefinitionsContractAddress is not allowed if ChannelDefinitions is specified")) + } + if p.ChannelDefinitionsContractFromBlock != 0 { + merr = errors.Join(merr, errors.New("llo: ChannelDefinitionsContractFromBlock is not allowed if ChannelDefinitions is specified")) + } + var cd llotypes.ChannelDefinitions + if err := json.Unmarshal([]byte(p.ChannelDefinitions), &cd); err != nil { + merr = errors.Join(merr, fmt.Errorf("channelDefinitions is invalid JSON: %w", err)) + } + } else { + if p.ChannelDefinitionsContractAddress == (common.Address{}) { + merr = errors.Join(merr, errors.New("llo: ChannelDefinitionsContractAddress is required if ChannelDefinitions is not specified")) + } + } + + if len(p.ServerPubKey) != 32 { + merr = errors.Join(merr, errors.New("llo: ServerPubKey is required and must be a 32-byte hex string")) + } + + merr = errors.Join(merr, validateKeyBundleIDs(p.KeyBundleIDs)) + + return merr +} + +func validateKeyBundleIDs(keyBundleIDs map[string]string) error { + for k, v := range keyBundleIDs { + if k == "" { + return errors.New("llo: KeyBundleIDs: key must not be empty") + } + if v == "" { + return errors.New("llo: KeyBundleIDs: value must not be empty") + } + if _, err := llotypes.ReportFormatFromString(k); err != nil { + return fmt.Errorf("llo: KeyBundleIDs: key must be a recognized report format, got: %s (err: %w)", k, err) + } + if !chaintype.IsSupportedChainType(chaintype.ChainType(k)) { + return fmt.Errorf("llo: KeyBundleIDs: key must be a supported chain type, got: %s", k) + } + + } + return nil +} + +var schemeRegexp = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9+.-]*://`) +var wssRegexp = regexp.MustCompile(`^wss://`) + +func (p PluginConfig) ServerURL() string { + return wssRegexp.ReplaceAllString(p.RawServerURL, "") +} diff --git a/core/services/ocr2/plugins/llo/config/config_test.go b/core/services/ocr2/plugins/llo/config/config_test.go new file mode 100644 index 00000000000..136fac87a56 --- /dev/null +++ b/core/services/ocr2/plugins/llo/config/config_test.go @@ -0,0 +1,142 @@ +package config + +import ( + "fmt" + "testing" + + "github.com/pelletier/go-toml/v2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_Config(t *testing.T) { + t.Run("unmarshals from toml", func(t *testing.T) { + cdjson := `{ + "42": { + "reportFormat": 42, + "chainSelector": 142, + "streamIds": [1, 2] + }, + "43": { + "reportFormat": 42, + "chainSelector": 142, + "streamIds": [1, 3] + }, + "44": { + "reportFormat": 42, + "chainSelector": 143, + "streamIds": [1, 4] + } +}` + + t.Run("with all possible values set", func(t *testing.T) { + rawToml := fmt.Sprintf(` + ServerURL = "example.com:80" + ServerPubKey = "724ff6eae9e900270edfff233e16322a70ec06e1a6e62a81ef13921f398f6c93" + BenchmarkMode = true + ChannelDefinitionsContractAddress = "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef" + ChannelDefinitionsContractFromBlock = 1234 + ChannelDefinitions = """ +%s +"""`, cdjson) + + var mc PluginConfig + err := toml.Unmarshal([]byte(rawToml), &mc) + require.NoError(t, err) + + assert.Equal(t, "example.com:80", mc.RawServerURL) + assert.Equal(t, "724ff6eae9e900270edfff233e16322a70ec06e1a6e62a81ef13921f398f6c93", mc.ServerPubKey.String()) + assert.Equal(t, "0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF", mc.ChannelDefinitionsContractAddress.Hex()) + assert.Equal(t, int64(1234), mc.ChannelDefinitionsContractFromBlock) + assert.JSONEq(t, cdjson, mc.ChannelDefinitions) + assert.True(t, mc.BenchmarkMode) + + err = mc.Validate() + require.Error(t, err) + + assert.Contains(t, err.Error(), "llo: ChannelDefinitionsContractAddress is not allowed if ChannelDefinitions is specified") + assert.Contains(t, err.Error(), "llo: ChannelDefinitionsContractFromBlock is not allowed if ChannelDefinitions is specified") + }) + + t.Run("with only channelDefinitions", func(t *testing.T) { + rawToml := fmt.Sprintf(` + ServerURL = "example.com:80" + ServerPubKey = "724ff6eae9e900270edfff233e16322a70ec06e1a6e62a81ef13921f398f6c93" + ChannelDefinitions = """ +%s +"""`, cdjson) + + var mc PluginConfig + err := toml.Unmarshal([]byte(rawToml), &mc) + require.NoError(t, err) + + assert.Equal(t, "example.com:80", mc.RawServerURL) + assert.Equal(t, "724ff6eae9e900270edfff233e16322a70ec06e1a6e62a81ef13921f398f6c93", mc.ServerPubKey.String()) + assert.JSONEq(t, cdjson, mc.ChannelDefinitions) + assert.False(t, mc.BenchmarkMode) + + err = mc.Validate() + require.NoError(t, err) + }) + t.Run("with only channelDefinitions contract details", func(t *testing.T) { + rawToml := ` + ServerURL = "example.com:80" + ServerPubKey = "724ff6eae9e900270edfff233e16322a70ec06e1a6e62a81ef13921f398f6c93" + ChannelDefinitionsContractAddress = "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"` + + var mc PluginConfig + err := toml.Unmarshal([]byte(rawToml), &mc) + require.NoError(t, err) + + assert.Equal(t, "example.com:80", mc.RawServerURL) + assert.Equal(t, "724ff6eae9e900270edfff233e16322a70ec06e1a6e62a81ef13921f398f6c93", mc.ServerPubKey.String()) + assert.Equal(t, "0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF", mc.ChannelDefinitionsContractAddress.Hex()) + assert.False(t, mc.BenchmarkMode) + + err = mc.Validate() + require.NoError(t, err) + }) + t.Run("with missing ChannelDefinitionsContractAddress", func(t *testing.T) { + rawToml := ` + ServerURL = "example.com:80" + ServerPubKey = "724ff6eae9e900270edfff233e16322a70ec06e1a6e62a81ef13921f398f6c93"` + + var mc PluginConfig + err := toml.Unmarshal([]byte(rawToml), &mc) + require.NoError(t, err) + + assert.Equal(t, "example.com:80", mc.RawServerURL) + assert.Equal(t, "724ff6eae9e900270edfff233e16322a70ec06e1a6e62a81ef13921f398f6c93", mc.ServerPubKey.String()) + assert.False(t, mc.BenchmarkMode) + + err = mc.Validate() + require.Error(t, err) + assert.EqualError(t, err, "llo: ChannelDefinitionsContractAddress is required if ChannelDefinitions is not specified") + }) + + t.Run("with invalid values", func(t *testing.T) { + rawToml := ` + ChannelDefinitionsContractFromBlock = "invalid" + ` + + var mc PluginConfig + err := toml.Unmarshal([]byte(rawToml), &mc) + require.Error(t, err) + assert.EqualError(t, err, `toml: cannot decode TOML string into struct field config.PluginConfig.ChannelDefinitionsContractFromBlock of type int64`) + assert.False(t, mc.BenchmarkMode) + + rawToml = ` + ServerURL = "http://example.com" + ServerPubKey = "4242" + ` + + err = toml.Unmarshal([]byte(rawToml), &mc) + require.NoError(t, err) + + err = mc.Validate() + require.Error(t, err) + assert.Contains(t, err.Error(), `invalid scheme specified for MercuryServer, got: "http://example.com" (scheme: "http") but expected a websocket url e.g. "192.0.2.2:4242" or "wss://192.0.2.2:4242"`) + assert.Contains(t, err.Error(), `ServerPubKey is required and must be a 32-byte hex string`) + }) + }) +} diff --git a/core/services/ocr2/plugins/llo/helpers_test.go b/core/services/ocr2/plugins/llo/helpers_test.go new file mode 100644 index 00000000000..ae9850134b9 --- /dev/null +++ b/core/services/ocr2/plugins/llo/helpers_test.go @@ -0,0 +1,356 @@ +package llo_test + +import ( + "context" + "crypto/ed25519" + "errors" + "fmt" + "io" + "math/big" + "net" + "net/http" + "net/http/httptest" + "net/url" + "testing" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" + "github.com/ethereum/go-ethereum/common" + "github.com/shopspring/decimal" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest/observer" + + "github.com/smartcontractkit/wsrpc" + "github.com/smartcontractkit/wsrpc/credentials" + "github.com/smartcontractkit/wsrpc/peer" + + "github.com/smartcontractkit/libocr/offchainreporting2/chains/evmutil" + ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + ocrtypes "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/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/keystest" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/csakey" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ocr2key" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/validate" + "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/services/streams" + "github.com/smartcontractkit/chainlink/v2/core/store/models" +) + +var _ pb.MercuryServer = &mercuryServer{} + +type request struct { + pk credentials.StaticSizedPublicKey + req *pb.TransmitRequest +} + +func (r request) TransmitterID() ocr2types.Account { + return ocr2types.Account(fmt.Sprintf("%x", r.pk)) +} + +type mercuryServer struct { + privKey ed25519.PrivateKey + reqsCh chan request + t *testing.T + buildReport func() []byte +} + +func NewMercuryServer(t *testing.T, privKey ed25519.PrivateKey, reqsCh chan request, buildReport func() []byte) *mercuryServer { + return &mercuryServer{privKey, reqsCh, t, buildReport} +} + +func (s *mercuryServer) Transmit(ctx context.Context, req *pb.TransmitRequest) (*pb.TransmitResponse, error) { + p, ok := peer.FromContext(ctx) + if !ok { + return nil, errors.New("could not extract public key") + } + r := request{p.PublicKey, req} + s.reqsCh <- r + + return &pb.TransmitResponse{ + Code: 1, + Error: "", + }, nil +} + +func (s *mercuryServer) LatestReport(ctx context.Context, lrr *pb.LatestReportRequest) (*pb.LatestReportResponse, error) { + p, ok := peer.FromContext(ctx) + if !ok { + return nil, errors.New("could not extract public key") + } + s.t.Logf("mercury server got latest report from %x for feed id 0x%x", p.PublicKey, lrr.FeedId) + + out := new(pb.LatestReportResponse) + out.Report = new(pb.Report) + out.Report.FeedId = lrr.FeedId + + report := s.buildReport() + payload, err := mercury.PayloadTypes.Pack(evmutil.RawReportContext(ocrtypes.ReportContext{}), report, [][32]byte{}, [][32]byte{}, [32]byte{}) + if err != nil { + require.NoError(s.t, err) + } + out.Report.Payload = payload + return out, nil +} + +func startMercuryServer(t *testing.T, srv *mercuryServer, pubKeys []ed25519.PublicKey) (serverURL string) { + // Set up the wsrpc server + lis, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatalf("[MAIN] failed to listen: %v", err) + } + serverURL = lis.Addr().String() + s := wsrpc.NewServer(wsrpc.Creds(srv.privKey, pubKeys)) + + // Register mercury implementation with the wsrpc server + pb.RegisterMercuryServer(s, srv) + + // Start serving + go s.Serve(lis) + t.Cleanup(s.Stop) + + return +} + +type Node struct { + App chainlink.Application + ClientPubKey credentials.StaticSizedPublicKey + KeyBundle ocr2key.KeyBundle +} + +func (node *Node) AddStreamJob(t *testing.T, spec string) { + job, err := streams.ValidatedStreamSpec(spec) + require.NoError(t, err) + err = node.App.AddJobV2(testutils.Context(t), &job) + require.NoError(t, err) +} + +func (node *Node) AddLLOJob(t *testing.T, spec string) { + c := node.App.GetConfig() + job, err := validate.ValidatedOracleSpecToml(c.OCR2(), c.Insecure(), spec) + require.NoError(t, err) + err = node.App.AddJobV2(testutils.Context(t), &job) + require.NoError(t, err) +} + +func (node *Node) AddBootstrapJob(t *testing.T, spec string) { + job, err := ocrbootstrap.ValidatedBootstrapSpecToml(spec) + require.NoError(t, err) + err = node.App.AddJobV2(testutils.Context(t), &job) + require.NoError(t, err) +} + +func setupNode( + t *testing.T, + port int, + dbName string, + backend *backends.SimulatedBackend, + csaKey csakey.KeyV2, +) (app chainlink.Application, peerID string, clientPubKey credentials.StaticSizedPublicKey, ocr2kb ocr2key.KeyBundle, observedLogs *observer.ObservedLogs) { + k := big.NewInt(int64(port)) // keys unique to port + p2pKey := p2pkey.MustNewV2XXXTestingOnly(k) + rdr := keystest.NewRandReaderFromSeed(int64(port)) + ocr2kb = ocr2key.MustNewInsecure(rdr, chaintype.EVM) + + p2paddresses := []string{fmt.Sprintf("127.0.0.1:%d", port)} + + config, _ := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { + // [JobPipeline] + c.JobPipeline.MaxSuccessfulRuns = ptr(uint64(0)) + + // [Feature] + c.Feature.UICSAKeys = ptr(true) + c.Feature.LogPoller = ptr(true) + c.Feature.FeedsManager = ptr(false) + + // [OCR] + c.OCR.Enabled = ptr(false) + + // [OCR2] + c.OCR2.Enabled = ptr(true) + c.OCR2.ContractPollInterval = commonconfig.MustNewDuration(1 * time.Second) + + // [P2P] + c.P2P.PeerID = ptr(p2pKey.PeerID()) + c.P2P.TraceLogging = ptr(true) + + // [P2P.V2] + c.P2P.V2.Enabled = ptr(true) + c.P2P.V2.AnnounceAddresses = &p2paddresses + c.P2P.V2.ListenAddresses = &p2paddresses + 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) + app = cltest.NewApplicationWithConfigV2OnSimulatedBlockchain(t, config, backend, p2pKey, ocr2kb, csaKey, lggr.Named(dbName)) + err := app.Start(testutils.Context(t)) + require.NoError(t, err) + + t.Cleanup(func() { + assert.NoError(t, app.Stop()) + }) + + return app, p2pKey.PeerID().Raw(), csaKey.StaticSizedPublicKey(), ocr2kb, observedLogs +} + +func ptr[T any](t T) *T { return &t } + +func addStreamJob( + t *testing.T, + node Node, + streamID uint32, + bridgeName string, +) { + node.AddStreamJob(t, fmt.Sprintf(` +type = "stream" +schemaVersion = 1 +name = "strm-spec-%d" +streamID = %d +observationSource = """ + // Benchmark Price + price1 [type=bridge name="%s" requestData="{\\"data\\":{\\"data\\":\\"foo\\"}}"]; + price1_parse [type=jsonparse path="result"]; + price1_multiply [type=multiply times=100000000 index=0]; + + price1 -> price1_parse -> price1_multiply; +""" + + `, + streamID, + streamID, + bridgeName, + )) +} +func addBootstrapJob(t *testing.T, bootstrapNode Node, chainID *big.Int, verifierAddress common.Address, name string) { + bootstrapNode.AddBootstrapJob(t, fmt.Sprintf(` +type = "bootstrap" +relay = "evm" +schemaVersion = 1 +name = "boot-%s" +contractID = "%s" +contractConfigTrackerPollInterval = "1s" + +[relayConfig] +chainID = %s +providerType = "llo" + `, name, verifierAddress.Hex(), chainID.String())) +} + +func addLLOJob( + t *testing.T, + node Node, + verifierAddress, + configStoreAddress common.Address, + bootstrapPeerID string, + bootstrapNodePort int, + serverURL string, + serverPubKey, + clientPubKey ed25519.PublicKey, + jobName string, + chainID *big.Int, + fromBlock int, +) { + node.AddLLOJob(t, fmt.Sprintf(` +type = "offchainreporting2" +schemaVersion = 1 +name = "%[1]s" +forwardingAllowed = false +maxTaskDuration = "1s" +contractID = "%[2]s" +contractConfigTrackerPollInterval = "1s" +ocrKeyBundleID = "%[3]s" +p2pv2Bootstrappers = [ + "%[4]s" +] +relay = "evm" +pluginType = "llo" +transmitterID = "%[5]x" + +[pluginConfig] +serverURL = "%[6]s" +serverPubKey = "%[7]x" +channelDefinitionsContractFromBlock = %[8]d +channelDefinitionsContractAddress = "%[9]s" + +[relayConfig] +chainID = %[10]s +fromBlock = 1`, + jobName, + verifierAddress.Hex(), + node.KeyBundle.ID(), + fmt.Sprintf("%s@127.0.0.1:%d", bootstrapPeerID, bootstrapNodePort), + clientPubKey, + serverURL, + serverPubKey, + fromBlock, + configStoreAddress.Hex(), + chainID.String(), + )) +} + +func addOCRJobs(t *testing.T, streams []Stream, serverPubKey ed25519.PublicKey, serverURL string, verifierAddress common.Address, bootstrapPeerID string, bootstrapNodePort int, nodes []Node, configStoreAddress common.Address, clientPubKeys []ed25519.PublicKey, chainID *big.Int, fromBlock int) { + createBridge := func(name string, i int, p *big.Int, borm bridges.ORM) (bridgeName string) { + bridge := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + b, err := io.ReadAll(req.Body) + require.NoError(t, err) + require.Equal(t, `{"data":{"data":"foo"}}`, string(b)) + + res.WriteHeader(http.StatusOK) + val := decimal.NewFromBigInt(p, 0).Div(decimal.NewFromInt(multiplier)).Add(decimal.NewFromInt(int64(i)).Div(decimal.NewFromInt(100))).String() + resp := fmt.Sprintf(`{"result": %s}`, val) + _, err = res.Write([]byte(resp)) + require.NoError(t, err) + })) + t.Cleanup(bridge.Close) + u, _ := url.Parse(bridge.URL) + bridgeName = fmt.Sprintf("bridge-%s-%d", name, i) + require.NoError(t, borm.CreateBridgeType(&bridges.BridgeType{ + Name: bridges.BridgeName(bridgeName), + URL: models.WebURL(*u), + })) + + return bridgeName + } + + // Add OCR jobs - one per feed on each node + for i, node := range nodes { + for j, strm := range streams { + bmBridge := createBridge(fmt.Sprintf("benchmarkprice-%d-%d", strm.id, j), i, strm.baseBenchmarkPrice, node.App.BridgeORM()) + addStreamJob( + t, + node, + strm.id, + bmBridge, + ) + } + addLLOJob( + t, + node, + verifierAddress, + configStoreAddress, + bootstrapPeerID, + bootstrapNodePort, + serverURL, + serverPubKey, + clientPubKeys[i], + "feed-1", + chainID, + fromBlock, + ) + } +} diff --git a/core/services/ocr2/plugins/llo/integration_test.go b/core/services/ocr2/plugins/llo/integration_test.go new file mode 100644 index 00000000000..df77316e4dd --- /dev/null +++ b/core/services/ocr2/plugins/llo/integration_test.go @@ -0,0 +1,370 @@ +package llo_test + +import ( + "crypto/ed25519" + "encoding/hex" + "fmt" + "math/big" + "math/rand" + "strings" + "testing" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/eth/ethconfig" + "github.com/hashicorp/consul/sdk/freeport" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + chainselectors "github.com/smartcontractkit/chain-selectors" + "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" + ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" + "github.com/smartcontractkit/chainlink-common/pkg/utils" + datastreamsllo "github.com/smartcontractkit/chainlink-data-streams/llo" + + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/channel_config_store" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/channel_verifier" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/verifier_proxy" + "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" + "github.com/smartcontractkit/chainlink/v2/core/services/llo" + lloevm "github.com/smartcontractkit/chainlink/v2/core/services/llo/evm" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" +) + +var ( + fNodes = uint8(1) + nNodes = 4 // number of nodes (not including bootstrap) + multiplier int64 = 100000000 +) + +func setupBlockchain(t *testing.T) (*bind.TransactOpts, *backends.SimulatedBackend, *channel_verifier.ChannelVerifier, common.Address, *channel_config_store.ChannelConfigStore, common.Address) { + steve := testutils.MustNewSimTransactor(t) // config contract deployer and owner + genesisData := core.GenesisAlloc{steve.From: {Balance: assets.Ether(1000).ToInt()}} + backend := cltest.NewSimulatedBackend(t, genesisData, uint32(ethconfig.Defaults.Miner.GasCeil)) + backend.Commit() + backend.Commit() // ensure starting block number at least 1 + + // Deploy contracts + verifierProxyAddr, _, _, err := verifier_proxy.DeployVerifierProxy(steve, backend, common.Address{}) // zero address for access controller disables access control + require.NoError(t, err) + + verifierAddress, _, verifierContract, err := channel_verifier.DeployChannelVerifier(steve, backend, verifierProxyAddr) + require.NoError(t, err) + configStoreAddress, _, configStoreContract, err := channel_config_store.DeployChannelConfigStore(steve, backend) + require.NoError(t, err) + + backend.Commit() + + return steve, backend, verifierContract, verifierAddress, configStoreContract, configStoreAddress +} + +type Stream struct { + id uint32 + baseBenchmarkPrice *big.Int +} + +func TestIntegration_LLO(t *testing.T) { + testStartTimeStamp := uint32(time.Now().Unix()) + + const fromBlock = 1 // cannot use zero, start from block 1 + + // streams + btcStream := Stream{ + id: 51, + baseBenchmarkPrice: big.NewInt(20_000 * multiplier), + } + ethStream := Stream{ + id: 52, + baseBenchmarkPrice: big.NewInt(1_568 * multiplier), + } + linkStream := Stream{ + id: 53, + baseBenchmarkPrice: big.NewInt(7150 * multiplier / 1000), + } + dogeStream := Stream{ + id: 54, + baseBenchmarkPrice: big.NewInt(2_020 * multiplier), + } + streams := []Stream{btcStream, ethStream, linkStream, dogeStream} + streamMap := make(map[uint32]Stream) + for _, strm := range streams { + streamMap[strm.id] = strm + } + + reqs := make(chan request) + serverKey := csakey.MustNewV2XXXTestingOnly(big.NewInt(-1)) + serverPubKey := serverKey.PublicKey + srv := NewMercuryServer(t, ed25519.PrivateKey(serverKey.Raw()), reqs, nil) + + clientCSAKeys := make([]csakey.KeyV2, nNodes) + clientPubKeys := make([]ed25519.PublicKey, nNodes) + for i := 0; i < nNodes; i++ { + k := big.NewInt(int64(i)) + key := csakey.MustNewV2XXXTestingOnly(k) + clientCSAKeys[i] = key + clientPubKeys[i] = key.PublicKey + } + serverURL := startMercuryServer(t, srv, clientPubKeys) + chainID := testutils.SimulatedChainID + + steve, backend, verifierContract, verifierAddress, configStoreContract, configStoreAddress := setupBlockchain(t) + + // Setup bootstrap + bootstrapCSAKey := csakey.MustNewV2XXXTestingOnly(big.NewInt(-1)) + bootstrapNodePort := freeport.GetOne(t) + appBootstrap, bootstrapPeerID, _, bootstrapKb, _ := setupNode(t, bootstrapNodePort, "bootstrap_mercury", backend, bootstrapCSAKey) + bootstrapNode := Node{App: appBootstrap, KeyBundle: bootstrapKb} + + // Setup oracle nodes + var ( + oracles []confighelper.OracleIdentityExtra + nodes []Node + ) + ports := freeport.GetN(t, nNodes) + for i := 0; i < nNodes; i++ { + app, peerID, transmitter, kb, _ := setupNode(t, ports[i], fmt.Sprintf("oracle_streams_%d", i), backend, clientCSAKeys[i]) + + nodes = append(nodes, Node{ + app, transmitter, kb, + }) + offchainPublicKey, _ := hex.DecodeString(strings.TrimPrefix(kb.OnChainPublicKey(), "0x")) + oracles = append(oracles, confighelper.OracleIdentityExtra{ + OracleIdentity: confighelper.OracleIdentity{ + OnchainPublicKey: offchainPublicKey, + TransmitAccount: ocr2types.Account(fmt.Sprintf("%x", transmitter[:])), + OffchainPublicKey: kb.OffchainPublicKey(), + PeerID: peerID, + }, + ConfigEncryptionPublicKey: kb.ConfigEncryptionPublicKey(), + }) + } + + configDigest := setConfig(t, steve, backend, verifierContract, verifierAddress, nodes, oracles) + channelDefinitions := setChannelDefinitions(t, steve, backend, configStoreContract, streams) + + // Bury everything with finality depth + ch, err := nodes[0].App.GetRelayers().LegacyEVMChains().Get(testutils.SimulatedChainID.String()) + require.NoError(t, err) + finalityDepth := ch.Config().EVM().FinalityDepth() + for i := 0; i < int(finalityDepth); i++ { + backend.Commit() + } + + addBootstrapJob(t, bootstrapNode, chainID, verifierAddress, "job-1") + addOCRJobs(t, streams, serverPubKey, serverURL, verifierAddress, bootstrapPeerID, bootstrapNodePort, nodes, configStoreAddress, clientPubKeys, chainID, fromBlock) + + t.Run("receives at least one report per feed from each oracle when EAs are at 100% reliability", func(t *testing.T) { + // Expect at least one report per channel from each oracle (keyed by transmitter ID) + seen := make(map[ocr2types.Account]map[llotypes.ChannelID]struct{}) + + for channelID, defn := range channelDefinitions { + t.Logf("Expect report for channel ID %x (definition: %#v)", channelID, defn) + } + for _, o := range oracles { + t.Logf("Expect report from oracle %s", o.OracleIdentity.TransmitAccount) + seen[o.OracleIdentity.TransmitAccount] = make(map[llotypes.ChannelID]struct{}) + } + + for req := range reqs { + if _, exists := seen[req.TransmitterID()]; !exists { + // oracle already reported on all channels; discard + continue + } + + v := make(map[string]interface{}) + err := llo.PayloadTypes.UnpackIntoMap(v, req.req.Payload) + require.NoError(t, err) + report, exists := v["report"] + if !exists { + t.Fatalf("FAIL: expected payload %#v to contain 'report'", v) + } + + t.Logf("Got report from oracle %x with format: %d", req.pk, req.req.ReportFormat) + + var r datastreamsllo.Report + + switch req.req.ReportFormat { + case uint32(llotypes.ReportFormatJSON): + t.Logf("Got report (JSON) from oracle %x: %s", req.pk, string(report.([]byte))) + var err error + r, err = (datastreamsllo.JSONReportCodec{}).Decode(report.([]byte)) + require.NoError(t, err, "expected valid JSON") + case uint32(llotypes.ReportFormatEVM): + t.Logf("Got report (EVM) from oracle %x: 0x%x", req.pk, report.([]byte)) + var err error + r, err = (lloevm.ReportCodec{}).Decode(report.([]byte)) + require.NoError(t, err, "expected valid EVM encoding") + default: + t.Fatalf("FAIL: unexpected report format: %q", req.req.ReportFormat) + } + + assert.Equal(t, configDigest, r.ConfigDigest) + assert.Equal(t, uint64(0x2ee634951ef71b46), r.ChainSelector) + assert.GreaterOrEqual(t, r.SeqNr, uint64(1)) + assert.GreaterOrEqual(t, r.ValidAfterSeconds, testStartTimeStamp) + assert.Equal(t, r.ValidAfterSeconds+1, r.ValidUntilSeconds) + + // values + defn, exists := channelDefinitions[r.ChannelID] + require.True(t, exists, "expected channel ID to be in channelDefinitions") + + require.Equal(t, len(defn.StreamIDs), len(r.Values)) + + for i, strmID := range defn.StreamIDs { + strm, exists := streamMap[strmID] + require.True(t, exists, "invariant violation: expected stream ID to be present") + assert.InDelta(t, strm.baseBenchmarkPrice.Int64(), r.Values[i].Int64(), 5000000) + } + + assert.False(t, r.Specimen) + + seen[req.TransmitterID()][r.ChannelID] = struct{}{} + t.Logf("Got report from oracle %s with channel: %x)", req.TransmitterID(), r.ChannelID) + + if _, exists := seen[req.TransmitterID()]; exists && len(seen[req.TransmitterID()]) == len(channelDefinitions) { + t.Logf("All channels reported for oracle with transmitterID %s", req.TransmitterID()) + delete(seen, req.TransmitterID()) + } + if len(seen) == 0 { + break // saw all oracles; success! + } + + // bit of a hack here but shouldn't hurt anything, we wanna dump + // `seen` before the test ends to aid in debugging test failures + if d, ok := t.Deadline(); ok { + select { + case <-time.After(time.Until(d.Add(-100 * time.Millisecond))): + if len(seen) > 0 { + t.Fatalf("FAILED: ERROR: missing expected reports: %#v\n", seen) + } + default: + } + } + } + }) + + // TODO: test verification +} + +func setConfig(t *testing.T, steve *bind.TransactOpts, backend *backends.SimulatedBackend, verifierContract *channel_verifier.ChannelVerifier, verifierAddress common.Address, nodes []Node, oracles []confighelper.OracleIdentityExtra) ocr2types.ConfigDigest { + // Setup config on contract + rawOnchainConfig := datastreamsllo.OnchainConfig{} + onchainConfig, err := (&datastreamsllo.JSONOnchainConfigCodec{}).Encode(rawOnchainConfig) + require.NoError(t, err) + + rawReportingPluginConfig := datastreamsllo.OffchainConfig{} + reportingPluginConfig, err := rawReportingPluginConfig.Encode() + require.NoError(t, err) + + signers, _, _, _, offchainConfigVersion, offchainConfig, err := ocr3confighelper.ContractSetConfigArgsForTests( + 2*time.Second, // DeltaProgress + 20*time.Second, // DeltaResend + 400*time.Millisecond, // DeltaInitial + 1000*time.Millisecond, // DeltaRound + 500*time.Millisecond, // DeltaGrace + 300*time.Millisecond, // DeltaCertifiedCommitRequest + 1*time.Minute, // DeltaStage + 100, // rMax + []int{len(nodes)}, // S + oracles, + reportingPluginConfig, // reportingPluginConfig []byte, + 0, // maxDurationQuery + 250*time.Millisecond, // maxDurationObservation + 0, // maxDurationShouldAcceptAttestedReport + 0, // maxDurationShouldTransmitAcceptedReport + int(fNodes), // f + onchainConfig, + ) + + require.NoError(t, err) + signerAddresses, err := evm.OnchainPublicKeyToAddress(signers) + require.NoError(t, err) + + offchainTransmitters := make([][32]byte, nNodes) + for i := 0; i < nNodes; i++ { + offchainTransmitters[i] = nodes[i].ClientPubKey + } + + _, err = verifierContract.SetConfig(steve, signerAddresses, offchainTransmitters, fNodes, offchainConfig, offchainConfigVersion, offchainConfig, nil) + require.NoError(t, err) + + backend.Commit() + + accounts := make([]ocr2types.Account, len(offchainTransmitters)) + for i := range offchainTransmitters { + accounts[i] = ocr2types.Account(fmt.Sprintf("%x", offchainTransmitters[i])) + } + + l, err := verifierContract.LatestConfigDigestAndEpoch(&bind.CallOpts{}) + require.NoError(t, err) + + return l.ConfigDigest +} + +func setChannelDefinitions(t *testing.T, steve *bind.TransactOpts, backend *backends.SimulatedBackend, configStoreContract *channel_config_store.ChannelConfigStore, streams []Stream) map[llotypes.ChannelID]channel_config_store.IChannelConfigStoreChannelDefinition { + channels := []llotypes.ChannelID{ + rand.Uint32(), + rand.Uint32(), + rand.Uint32(), + rand.Uint32(), + } + + chainSelector, err := chainselectors.SelectorFromChainId(testutils.SimulatedChainID.Uint64()) + require.NoError(t, err) + + streamIDs := make([]uint32, len(streams)) + for i := 0; i < len(streams); i++ { + streamIDs[i] = streams[i].id + } + + // First set contains [1,len(streams)] + channel0Def := channel_config_store.IChannelConfigStoreChannelDefinition{ + ReportFormat: uint32(llotypes.ReportFormatJSON), + ChainSelector: chainSelector, + StreamIDs: streamIDs[1:len(streams)], + } + channel1Def := channel_config_store.IChannelConfigStoreChannelDefinition{ + ReportFormat: uint32(llotypes.ReportFormatEVM), + ChainSelector: chainSelector, + StreamIDs: streamIDs[1:len(streams)], + } + + // Second set contains [0,len(streams)-1] + channel2Def := channel_config_store.IChannelConfigStoreChannelDefinition{ + ReportFormat: uint32(llotypes.ReportFormatJSON), + ChainSelector: chainSelector, + StreamIDs: streamIDs[0 : len(streams)-1], + } + channel3Def := channel_config_store.IChannelConfigStoreChannelDefinition{ + ReportFormat: uint32(llotypes.ReportFormatEVM), + ChainSelector: chainSelector, + StreamIDs: streamIDs[0 : len(streams)-1], + } + + require.NoError(t, utils.JustError(configStoreContract.AddChannel(steve, channels[0], channel0Def))) + require.NoError(t, utils.JustError(configStoreContract.AddChannel(steve, channels[1], channel1Def))) + require.NoError(t, utils.JustError(configStoreContract.AddChannel(steve, channels[2], channel2Def))) + require.NoError(t, utils.JustError(configStoreContract.AddChannel(steve, channels[3], channel3Def))) + + backend.Commit() + + channelDefinitions := make(map[llotypes.ChannelID]channel_config_store.IChannelConfigStoreChannelDefinition) + + channelDefinitions[channels[0]] = channel0Def + channelDefinitions[channels[1]] = channel1Def + channelDefinitions[channels[2]] = channel2Def + channelDefinitions[channels[3]] = channel3Def + + backend.Commit() + + return channelDefinitions +} diff --git a/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go b/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go new file mode 100644 index 00000000000..a9ba09d96bb --- /dev/null +++ b/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go @@ -0,0 +1,213 @@ +package llo_test + +import ( + "math/rand" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/eth/ethconfig" + chainselectors "github.com/smartcontractkit/chain-selectors" + "github.com/stretchr/testify/require" + "github.com/test-go/testify/assert" + "go.uber.org/zap/zapcore" + + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" + "github.com/smartcontractkit/chainlink-common/pkg/utils" + + "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/logpoller" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/channel_config_store" + "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/llo" + "github.com/smartcontractkit/chainlink/v2/core/services/pg" +) + +func Test_ChannelDefinitionCache_Integration(t *testing.T) { + lggr, observedLogs := logger.TestLoggerObserved(t, zapcore.InfoLevel) + db := pgtest.NewSqlxDB(t) + ctx := testutils.Context(t) + orm := llo.NewORM(db, testutils.SimulatedChainID) + + steve := testutils.MustNewSimTransactor(t) // config contract deployer and owner + genesisData := core.GenesisAlloc{steve.From: {Balance: assets.Ether(1000).ToInt()}} + backend := cltest.NewSimulatedBackend(t, genesisData, uint32(ethconfig.Defaults.Miner.GasCeil)) + backend.Commit() // ensure starting block number at least 1 + + ethClient := client.NewSimulatedBackendClient(t, backend, testutils.SimulatedChainID) + + configStoreAddress, _, configStoreContract, err := channel_config_store.DeployChannelConfigStore(steve, backend) + require.NoError(t, err) + + channel1 := rand.Uint32() + channel2 := rand.Uint32() + channel3 := rand.Uint32() + + chainSelector, err := chainselectors.SelectorFromChainId(testutils.SimulatedChainID.Uint64()) + require.NoError(t, err) + + streamIDs := []uint32{1, 2, 3} + channel1Def := channel_config_store.IChannelConfigStoreChannelDefinition{ + ReportFormat: uint32(llotypes.ReportFormatSolana), + ChainSelector: chainSelector, + StreamIDs: streamIDs, + } + channel2Def := channel_config_store.IChannelConfigStoreChannelDefinition{ + ReportFormat: uint32(llotypes.ReportFormatEVM), + ChainSelector: chainSelector, + StreamIDs: streamIDs, + } + channel3Def := channel_config_store.IChannelConfigStoreChannelDefinition{ + ReportFormat: uint32(llotypes.ReportFormatEVM), + ChainSelector: chainSelector, + StreamIDs: append(streamIDs, 4), + } + + require.NoError(t, utils.JustError(configStoreContract.AddChannel(steve, channel1, channel1Def))) + require.NoError(t, utils.JustError(configStoreContract.AddChannel(steve, channel2, channel2Def))) + + h := backend.Commit() + channel2Block, err := backend.BlockByHash(ctx, h) + require.NoError(t, err) + + t.Run("with zero fromblock", func(t *testing.T) { + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr, pgtest.NewQConfig(true)), ethClient, lggr, 100*time.Millisecond, false, 1, 3, 2, 1000) + servicetest.Run(t, lp) + cdc := llo.NewChannelDefinitionCache(lggr, orm, lp, configStoreAddress, 0) + + servicetest.Run(t, cdc) + + testutils.WaitForLogMessage(t, observedLogs, "Updated channel definitions") + + dfns := cdc.Definitions() + + require.Len(t, dfns, 2) + require.Contains(t, dfns, channel1) + require.Contains(t, dfns, channel2) + assert.Equal(t, llotypes.ChannelDefinition{ + ReportFormat: llotypes.ReportFormatSolana, + ChainSelector: chainSelector, + StreamIDs: []uint32{1, 2, 3}, + }, dfns[channel1]) + assert.Equal(t, llotypes.ChannelDefinition{ + ReportFormat: llotypes.ReportFormatEVM, + ChainSelector: chainSelector, + StreamIDs: []uint32{1, 2, 3}, + }, dfns[channel2]) + + // remove solana + require.NoError(t, utils.JustError(configStoreContract.RemoveChannel(steve, channel1))) + backend.Commit() + testutils.WaitForLogMessageCount(t, observedLogs, "Updated channel definitions", 2) + dfns = cdc.Definitions() + + require.Len(t, dfns, 1) + assert.NotContains(t, dfns, channel1) + require.Contains(t, dfns, channel2) + + assert.Equal(t, llotypes.ChannelDefinition{ + ReportFormat: llotypes.ReportFormatEVM, + ChainSelector: chainSelector, + StreamIDs: []uint32{1, 2, 3}, + }, dfns[channel2]) + + // add channel3 with additional stream + require.NoError(t, utils.JustError(configStoreContract.AddChannel(steve, channel3, channel3Def))) + backend.Commit() + testutils.WaitForLogMessageCount(t, observedLogs, "Updated channel definitions", 3) + dfns = cdc.Definitions() + + require.Len(t, dfns, 2) + require.Contains(t, dfns, channel2) + require.Contains(t, dfns, channel3) + + assert.Equal(t, llotypes.ChannelDefinition{ + ReportFormat: llotypes.ReportFormatEVM, + ChainSelector: chainSelector, + StreamIDs: []uint32{1, 2, 3}, + }, dfns[channel2]) + assert.Equal(t, llotypes.ChannelDefinition{ + ReportFormat: llotypes.ReportFormatEVM, + ChainSelector: chainSelector, + StreamIDs: []uint32{1, 2, 3, 4}, + }, dfns[channel3]) + }) + + t.Run("loads from ORM", func(t *testing.T) { + // Override logpoller to always return no logs + lp := &mockLogPoller{ + LogPoller: logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr, pgtest.NewQConfig(true)), ethClient, lggr, 100*time.Millisecond, false, 1, 3, 2, 1000), + LatestBlockFn: func(qopts ...pg.QOpt) (int64, error) { + return 0, nil + }, + LogsWithSigsFn: func(start, end int64, eventSigs []common.Hash, address common.Address, qopts ...pg.QOpt) ([]logpoller.Log, error) { + return []logpoller.Log{}, nil + }, + } + cdc := llo.NewChannelDefinitionCache(lggr, orm, lp, configStoreAddress, 0) + + servicetest.Run(t, cdc) + + dfns := cdc.Definitions() + + require.Len(t, dfns, 2) + require.Contains(t, dfns, channel2) + require.Contains(t, dfns, channel3) + + assert.Equal(t, llotypes.ChannelDefinition{ + ReportFormat: llotypes.ReportFormatEVM, + ChainSelector: chainSelector, + StreamIDs: []uint32{1, 2, 3}, + }, dfns[channel2]) + assert.Equal(t, llotypes.ChannelDefinition{ + ReportFormat: llotypes.ReportFormatEVM, + ChainSelector: chainSelector, + StreamIDs: []uint32{1, 2, 3, 4}, + }, dfns[channel3]) + }) + + // clear out DB for next test + pgtest.MustExec(t, db, `DELETE FROM channel_definitions`) + + t.Run("with non-zero fromBlock", func(t *testing.T) { + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr, pgtest.NewQConfig(true)), ethClient, lggr, 100*time.Millisecond, false, 1, 3, 2, 1000) + servicetest.Run(t, lp) + cdc := llo.NewChannelDefinitionCache(lggr, orm, lp, configStoreAddress, channel2Block.Number().Int64()+1) + + // should only detect events from AFTER channel 2 was added + servicetest.Run(t, cdc) + + testutils.WaitForLogMessageCount(t, observedLogs, "Updated channel definitions", 4) + + dfns := cdc.Definitions() + + require.Len(t, dfns, 1) + require.Contains(t, dfns, channel3) + + assert.Equal(t, llotypes.ChannelDefinition{ + ReportFormat: llotypes.ReportFormatEVM, + ChainSelector: chainSelector, + StreamIDs: []uint32{1, 2, 3, 4}, + }, dfns[channel3]) + }) +} + +type mockLogPoller struct { + logpoller.LogPoller + LatestBlockFn func(qopts ...pg.QOpt) (int64, error) + LogsWithSigsFn func(start, end int64, eventSigs []common.Hash, address common.Address, qopts ...pg.QOpt) ([]logpoller.Log, error) +} + +func (p *mockLogPoller) LogsWithSigs(start, end int64, eventSigs []common.Hash, address common.Address, qopts ...pg.QOpt) ([]logpoller.Log, error) { + return p.LogsWithSigsFn(start, end, eventSigs, address, qopts...) +} +func (p *mockLogPoller) LatestBlock(qopts ...pg.QOpt) (logpoller.LogPollerBlock, error) { + block, err := p.LatestBlockFn(qopts...) + return logpoller.LogPollerBlock{BlockNumber: block}, err +} diff --git a/core/services/ocr2/plugins/mercury/integration_test.go b/core/services/ocr2/plugins/mercury/integration_test.go index 0ebc6a5e354..36189475898 100644 --- a/core/services/ocr2/plugins/mercury/integration_test.go +++ b/core/services/ocr2/plugins/mercury/integration_test.go @@ -38,7 +38,7 @@ import ( 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" + datastreamsmercury "github.com/smartcontractkit/chainlink-data-streams/mercury" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" @@ -67,7 +67,7 @@ var ( Min: big.NewInt(0), Max: big.NewInt(math.MaxInt64), } - rawReportingPluginConfig = relaymercury.OffchainConfig{ + rawReportingPluginConfig = datastreamsmercury.OffchainConfig{ ExpirationWindow: 1, BaseUSDFee: decimal.NewFromInt(100), } @@ -273,7 +273,7 @@ func integration_MercuryV1(t *testing.T) { } // Setup config on contract - onchainConfig, err := (relaymercury.StandardOnchainConfigCodec{}).Encode(rawOnchainConfig) + onchainConfig, err := (datastreamsmercury.StandardOnchainConfigCodec{}).Encode(rawOnchainConfig) require.NoError(t, err) reportingPluginConfig, err := json.Marshal(rawReportingPluginConfig) @@ -623,7 +623,7 @@ func integration_MercuryV2(t *testing.T) { } // Setup config on contract - onchainConfig, err := (relaymercury.StandardOnchainConfigCodec{}).Encode(rawOnchainConfig) + onchainConfig, err := (datastreamsmercury.StandardOnchainConfigCodec{}).Encode(rawOnchainConfig) require.NoError(t, err) reportingPluginConfig, err := json.Marshal(rawReportingPluginConfig) @@ -707,7 +707,7 @@ func integration_MercuryV2(t *testing.T) { continue // already saw all oracles for this feed } - expectedFee := relaymercury.CalculateFee(big.NewInt(234567), rawReportingPluginConfig.BaseUSDFee) + expectedFee := datastreamsmercury.CalculateFee(big.NewInt(234567), rawReportingPluginConfig.BaseUSDFee) expectedExpiresAt := reportElems["observationsTimestamp"].(uint32) + rawReportingPluginConfig.ExpirationWindow assert.GreaterOrEqual(t, int(reportElems["observationsTimestamp"].(uint32)), int(testStartTimeStamp)) @@ -907,7 +907,7 @@ func integration_MercuryV3(t *testing.T) { } // Setup config on contract - onchainConfig, err := (relaymercury.StandardOnchainConfigCodec{}).Encode(rawOnchainConfig) + onchainConfig, err := (datastreamsmercury.StandardOnchainConfigCodec{}).Encode(rawOnchainConfig) require.NoError(t, err) reportingPluginConfig, err := json.Marshal(rawReportingPluginConfig) @@ -991,7 +991,7 @@ func integration_MercuryV3(t *testing.T) { continue // already saw all oracles for this feed } - expectedFee := relaymercury.CalculateFee(big.NewInt(234567), rawReportingPluginConfig.BaseUSDFee) + expectedFee := datastreamsmercury.CalculateFee(big.NewInt(234567), rawReportingPluginConfig.BaseUSDFee) expectedExpiresAt := reportElems["observationsTimestamp"].(uint32) + rawReportingPluginConfig.ExpirationWindow assert.GreaterOrEqual(t, int(reportElems["observationsTimestamp"].(uint32)), int(testStartTimeStamp)) diff --git a/core/services/ocr2/validate/validate.go b/core/services/ocr2/validate/validate.go index 9fe779b244f..5846eaa032f 100644 --- a/core/services/ocr2/validate/validate.go +++ b/core/services/ocr2/validate/validate.go @@ -14,6 +14,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/services/job" dkgconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/dkg/config" + lloconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/llo/config" mercuryconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/mercury/config" ocr2vrfconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2vrf/config" "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" @@ -113,6 +114,8 @@ func validateSpec(tree *toml.Tree, spec job.Job) error { return nil case types.Mercury: return validateOCR2MercurySpec(spec.OCR2OracleSpec.PluginConfig, *spec.OCR2OracleSpec.FeedID) + case types.LLO: + return validateOCR2LLOSpec(spec.OCR2OracleSpec.PluginConfig) case types.GenericPlugin: return validateOCR2GenericPluginSpec(spec.OCR2OracleSpec.PluginConfig) case "": @@ -256,3 +259,12 @@ func validateOCR2MercurySpec(jsonConfig job.JSONConfig, feedId [32]byte) error { } return pkgerrors.Wrap(mercuryconfig.ValidatePluginConfig(pluginConfig, feedId), "Mercury PluginConfig is invalid") } + +func validateOCR2LLOSpec(jsonConfig job.JSONConfig) error { + var pluginConfig lloconfig.PluginConfig + err := json.Unmarshal(jsonConfig.Bytes(), &pluginConfig) + if err != nil { + return pkgerrors.Wrap(err, "error while unmarshaling plugin config") + } + return pkgerrors.Wrap(pluginConfig.Validate(), "LLO PluginConfig is invalid") +} diff --git a/core/services/ocrbootstrap/delegate.go b/core/services/ocrbootstrap/delegate.go index 7912741802c..27ddd53bd52 100644 --- a/core/services/ocrbootstrap/delegate.go +++ b/core/services/ocrbootstrap/delegate.go @@ -39,8 +39,10 @@ type Delegate struct { isNewlyCreatedJob bool } -// Extra fields to enable router proxy contract support. Must match field names of functions' PluginConfig. -type relayConfigRouterContractFields struct { +type relayConfig struct { + // providerType used for determining which type of contract to track config on + ProviderType string `json:"providerType"` + // Extra fields to enable router proxy contract support. Must match field names of functions' PluginConfig. DONID string `json:"donID"` ContractVersion uint32 `json:"contractVersion"` ContractUpdateCheckFrequencySec uint32 `json:"contractUpdateCheckFrequencySec"` @@ -109,14 +111,14 @@ func (d *Delegate) ServicesForSpec(jb job.Job) (services []job.ServiceCtx, err e } ctx := ctxVals.ContextWithValues(context.Background()) - var routerFields relayConfigRouterContractFields - if err = json.Unmarshal(spec.RelayConfig.Bytes(), &routerFields); err != nil { + var relayCfg relayConfig + if err = json.Unmarshal(spec.RelayConfig.Bytes(), &relayCfg); err != nil { return nil, err } var configProvider types.ConfigProvider - if routerFields.DONID != "" { - if routerFields.ContractVersion != 1 || routerFields.ContractUpdateCheckFrequencySec == 0 { + if relayCfg.DONID != "" { + if relayCfg.ContractVersion != 1 || relayCfg.ContractUpdateCheckFrequencySec == 0 { return nil, errors.New("invalid router contract config") } configProvider, err = relayer.NewPluginProvider( @@ -140,6 +142,7 @@ func (d *Delegate) ServicesForSpec(jb job.Job) (services []job.ServiceCtx, err e ContractID: spec.ContractID, New: d.isNewlyCreatedJob, RelayConfig: spec.RelayConfig.Bytes(), + ProviderType: relayCfg.ProviderType, }) } diff --git a/core/services/pipeline/mocks/runner.go b/core/services/pipeline/mocks/runner.go index f6e5033eae9..1de72bbf4c0 100644 --- a/core/services/pipeline/mocks/runner.go +++ b/core/services/pipeline/mocks/runner.go @@ -132,6 +132,36 @@ func (_m *Runner) HealthReport() map[string]error { return r0 } +// InitializePipeline provides a mock function with given fields: spec +func (_m *Runner) InitializePipeline(spec pipeline.Spec) (*pipeline.Pipeline, error) { + ret := _m.Called(spec) + + if len(ret) == 0 { + panic("no return value specified for InitializePipeline") + } + + var r0 *pipeline.Pipeline + var r1 error + if rf, ok := ret.Get(0).(func(pipeline.Spec) (*pipeline.Pipeline, error)); ok { + return rf(spec) + } + if rf, ok := ret.Get(0).(func(pipeline.Spec) *pipeline.Pipeline); ok { + r0 = rf(spec) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pipeline.Pipeline) + } + } + + if rf, ok := ret.Get(1).(func(pipeline.Spec) error); ok { + r1 = rf(spec) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // InsertFinishedRun provides a mock function with given fields: run, saveSuccessfulTaskRuns, qopts func (_m *Runner) InsertFinishedRun(run *pipeline.Run, saveSuccessfulTaskRuns bool, qopts ...pg.QOpt) error { _va := make([]interface{}, len(qopts)) diff --git a/core/services/pipeline/runner.go b/core/services/pipeline/runner.go index 30a35598c3e..cc6214abf5a 100644 --- a/core/services/pipeline/runner.go +++ b/core/services/pipeline/runner.go @@ -51,6 +51,7 @@ type Runner interface { ExecuteAndInsertFinishedRun(ctx context.Context, spec Spec, vars Vars, l logger.Logger, saveSuccessfulTaskRuns bool) (runID int64, finalResult FinalResult, err error) OnRunFinished(func(*Run)) + InitializePipeline(spec Spec) (*Pipeline, error) } type runner struct { diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index 4de4e48bd90..dcccbb90c79 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -28,6 +28,9 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger" "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/llo" + "github.com/smartcontractkit/chainlink/v2/core/services/llo/bm" + lloconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/llo/config" 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" @@ -44,6 +47,7 @@ import ( var ( OCR2AggregatorTransmissionContractABI abi.ABI OCR2AggregatorLogDecoder LogDecoder + ChannelVerifierLogDecoder LogDecoder ) func init() { @@ -56,6 +60,10 @@ func init() { if err != nil { panic(err) } + ChannelVerifierLogDecoder, err = newChannelVerifierLogDecoder() + if err != nil { + panic(err) + } } var _ commontypes.Relayer = &Relayer{} //nolint:staticcheck @@ -69,6 +77,10 @@ type Relayer struct { pgCfg pg.QConfig chainReader commontypes.ChainReader codec commontypes.Codec + + // LLO/data streams + cdcFactory llo.ChannelDefinitionCacheFactory + orm llo.ORM } type CSAETHKeystore interface { @@ -107,6 +119,9 @@ func NewRelayer(lggr logger.Logger, chain legacyevm.Chain, opts RelayerOpts) (*R return nil, fmt.Errorf("cannot create evm relayer: %w", err) } lggr = lggr.Named("Relayer") + + orm := llo.NewORM(pg.NewQ(opts.DB, lggr, opts.QConfig), chain.ID()) + cdcFactory := llo.NewChannelDefinitionCacheFactory(lggr, orm, chain.LogPoller()) return &Relayer{ db: opts.DB, chain: chain, @@ -114,6 +129,8 @@ func NewRelayer(lggr logger.Logger, chain legacyevm.Chain, opts RelayerOpts) (*R ks: opts.CSAETHKeystore, mercuryPool: opts.MercuryPool, pgCfg: opts.QConfig, + cdcFactory: cdcFactory, + orm: orm, }, nil } @@ -226,7 +243,60 @@ func (r *Relayer) NewMercuryProvider(rargs commontypes.RelayArgs, pargs commonty } func (r *Relayer) NewLLOProvider(rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (commontypes.LLOProvider, error) { - return nil, errors.New("not implemented") + relayOpts := types.NewRelayOpts(rargs) + var relayConfig types.RelayConfig + { + var err error + relayConfig, err = relayOpts.RelayConfig() + if err != nil { + return nil, fmt.Errorf("failed to get relay config: %w", err) + } + } + + var lloCfg lloconfig.PluginConfig + if err := json.Unmarshal(pargs.PluginConfig, &lloCfg); err != nil { + return nil, pkgerrors.WithStack(err) + } + if err := lloCfg.Validate(); err != nil { + return nil, err + } + + 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()) + } + cp, err := newLLOConfigProvider(r.lggr, r.chain, relayOpts) + if err != nil { + return nil, pkgerrors.WithStack(err) + } + + if !relayConfig.EffectiveTransmitterID.Valid { + return nil, pkgerrors.New("EffectiveTransmitterID must be specified") + } + privKey, err := r.ks.CSA().Get(relayConfig.EffectiveTransmitterID.String) + if err != nil { + return nil, pkgerrors.Wrap(err, "failed to get CSA key for mercury connection") + } + + // FIXME: Remove after benchmarking is done + // https://smartcontract-it.atlassian.net/browse/MERC-3487 + var transmitter llo.Transmitter + if lloCfg.BenchmarkMode { + r.lggr.Info("Benchmark mode enabled, using dummy transmitter. NOTE: THIS WILL NOT TRANSMIT ANYTHING") + transmitter = bm.NewTransmitter(r.lggr, privKey.PublicKey) + } else { + var client wsrpc.Client + client, err = r.mercuryPool.Checkout(context.Background(), privKey, lloCfg.ServerPubKey, lloCfg.ServerURL()) + if err != nil { + return nil, err + } + transmitter = llo.NewTransmitter(r.lggr, client, privKey.PublicKey) + } + + cdc, err := r.cdcFactory.NewCache(lloCfg) + if err != nil { + return nil, err + } + return NewLLOProvider(cp, transmitter, r.lggr, cdc), nil } func (r *Relayer) NewFunctionsProvider(rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (commontypes.FunctionsProvider, error) { @@ -263,9 +333,12 @@ func (r *Relayer) NewConfigProvider(args commontypes.RelayArgs) (configProvider configProvider, err = newStandardConfigProvider(lggr, r.chain, relayOpts) case "mercury": configProvider, err = newMercuryConfigProvider(lggr, r.chain, relayOpts) + case "llo": + configProvider, err = newLLOConfigProvider(lggr, r.chain, relayOpts) default: return nil, fmt.Errorf("unrecognized provider type: %q", args.ProviderType) } + if err != nil { // Never return (*configProvider)(nil) return nil, err @@ -471,6 +544,7 @@ func (r *Relayer) NewMedianProvider(rargs commontypes.RelayArgs, pargs commontyp } reportCodec := evmreportcodec.ReportCodec{} + contractTransmitter, err := newOnChainContractTransmitter(lggr, rargs, pargs.TransmitterID, r.ks.Eth(), configWatcher, configTransmitterOpts{}, OCR2AggregatorTransmissionContractABI) if err != nil { return nil, err diff --git a/core/services/relay/evm/llo_config_provider.go b/core/services/relay/evm/llo_config_provider.go new file mode 100644 index 00000000000..bd8dbac8460 --- /dev/null +++ b/core/services/relay/evm/llo_config_provider.go @@ -0,0 +1,21 @@ +package evm + +import ( + "github.com/ethereum/go-ethereum/common" + pkgerrors "github.com/pkg/errors" + + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/llo" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" +) + +func newLLOConfigProvider(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") + } + + aggregatorAddress := common.HexToAddress(opts.ContractID) + configDigester := llo.NewOffchainConfigDigester(chain.Config().EVM().ChainID(), aggregatorAddress) + return newContractConfigProvider(lggr, chain, opts, aggregatorAddress, ChannelVerifierLogDecoder, configDigester) +} diff --git a/core/services/relay/evm/llo_provider.go b/core/services/relay/evm/llo_provider.go new file mode 100644 index 00000000000..0ab0773a160 --- /dev/null +++ b/core/services/relay/evm/llo_provider.go @@ -0,0 +1,90 @@ +package evm + +import ( + "context" + "errors" + + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + "github.com/smartcontractkit/chainlink-common/pkg/services" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + relaytypes "github.com/smartcontractkit/chainlink-common/pkg/types" + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" + datastreamsllo "github.com/smartcontractkit/chainlink-data-streams/llo" + + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/llo" +) + +var _ commontypes.LLOProvider = (*lloProvider)(nil) + +type lloProvider struct { + cp commontypes.ConfigProvider + transmitter llo.Transmitter + logger logger.Logger + channelDefinitionCache llotypes.ChannelDefinitionCache + + ms services.MultiStart +} + +func NewLLOProvider( + cp commontypes.ConfigProvider, + transmitter llo.Transmitter, + lggr logger.Logger, + channelDefinitionCache llotypes.ChannelDefinitionCache, +) relaytypes.LLOProvider { + return &lloProvider{ + cp, + transmitter, + lggr.Named("LLOProvider"), + channelDefinitionCache, + services.MultiStart{}, + } +} + +func (p *lloProvider) Start(ctx context.Context) error { + err := p.ms.Start(ctx, p.cp, p.transmitter, p.channelDefinitionCache) + return err +} + +func (p *lloProvider) Close() error { + return p.ms.Close() +} + +func (p *lloProvider) Ready() error { + return errors.Join(p.cp.Ready(), p.transmitter.Ready(), p.channelDefinitionCache.Ready()) +} + +func (p *lloProvider) Name() string { + return p.logger.Name() +} + +func (p *lloProvider) HealthReport() map[string]error { + report := map[string]error{} + services.CopyHealth(report, p.cp.HealthReport()) + services.CopyHealth(report, p.transmitter.HealthReport()) + services.CopyHealth(report, p.channelDefinitionCache.HealthReport()) + return report +} + +func (p *lloProvider) ContractConfigTracker() ocrtypes.ContractConfigTracker { + return p.cp.ContractConfigTracker() +} + +func (p *lloProvider) OffchainConfigDigester() ocrtypes.OffchainConfigDigester { + return p.cp.OffchainConfigDigester() +} + +func (p *lloProvider) OnchainConfigCodec() datastreamsllo.OnchainConfigCodec { + // TODO: This should probably be moved to core since its chain-specific + // https://smartcontract-it.atlassian.net/browse/MERC-3661 + return &datastreamsllo.JSONOnchainConfigCodec{} +} + +func (p *lloProvider) ContractTransmitter() llotypes.Transmitter { + return p.transmitter +} + +func (p *lloProvider) ChannelDefinitionCache() llotypes.ChannelDefinitionCache { + return p.channelDefinitionCache +} diff --git a/core/services/relay/evm/llo_verifier_decoder.go b/core/services/relay/evm/llo_verifier_decoder.go new file mode 100644 index 00000000000..922b83bec0d --- /dev/null +++ b/core/services/relay/evm/llo_verifier_decoder.go @@ -0,0 +1,67 @@ +package evm + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" + + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/channel_verifier" +) + +var _ LogDecoder = &channelVerifierLogDecoder{} + +type channelVerifierLogDecoder struct { + eventName string + eventSig common.Hash + abi *abi.ABI +} + +func newChannelVerifierLogDecoder() (*channelVerifierLogDecoder, error) { + const eventName = "ConfigSet" + abi, err := channel_verifier.ChannelVerifierMetaData.GetAbi() + if err != nil { + return nil, err + } + return &channelVerifierLogDecoder{ + eventName: eventName, + eventSig: abi.Events[eventName].ID, + abi: abi, + }, nil +} + +func (d *channelVerifierLogDecoder) Decode(rawLog []byte) (ocrtypes.ContractConfig, error) { + unpacked := new(channel_verifier.ChannelVerifierConfigSet) + err := d.abi.UnpackIntoInterface(unpacked, d.eventName, rawLog) + if err != nil { + return ocrtypes.ContractConfig{}, errors.Wrap(err, "failed to unpack log data") + } + + var transmitAccounts []ocrtypes.Account + for _, addr := range unpacked.OffchainTransmitters { + transmitAccounts = append(transmitAccounts, ocrtypes.Account(fmt.Sprintf("%x", addr))) + } + var signers []ocrtypes.OnchainPublicKey + for _, addr := range unpacked.Signers { + addr := addr + signers = append(signers, addr[:]) + } + + return ocrtypes.ContractConfig{ + ConfigDigest: unpacked.ConfigDigest, + ConfigCount: unpacked.ConfigCount, + Signers: signers, + Transmitters: transmitAccounts, + F: unpacked.F, + OnchainConfig: unpacked.OnchainConfig, + OffchainConfigVersion: unpacked.OffchainConfigVersion, + OffchainConfig: unpacked.OffchainConfig, + }, nil +} + +func (d *channelVerifierLogDecoder) EventSig() common.Hash { + return d.eventSig +} diff --git a/core/services/relay/evm/mercury/config_digest.go b/core/services/relay/evm/mercury/config_digest.go index b9431fe923f..291a723ee3a 100644 --- a/core/services/relay/evm/mercury/config_digest.go +++ b/core/services/relay/evm/mercury/config_digest.go @@ -61,7 +61,7 @@ func configDigest( panic("copy too little data") } binary.BigEndian.PutUint16(configDigest[:2], uint16(types.ConfigDigestPrefixMercuryV02)) - if !(configDigest[0] == 0 || configDigest[1] == 6) { + if !(configDigest[0] == 0 && configDigest[1] == 6) { // assertion panic("unexpected mismatch") } diff --git a/core/services/relay/evm/mercury/wsrpc/pb/mercury.pb.go b/core/services/relay/evm/mercury/wsrpc/pb/mercury.pb.go index ce4125bd579..ab4d2f68dad 100644 --- a/core/services/relay/evm/mercury/wsrpc/pb/mercury.pb.go +++ b/core/services/relay/evm/mercury/wsrpc/pb/mercury.pb.go @@ -26,7 +26,7 @@ type TransmitRequest struct { unknownFields protoimpl.UnknownFields Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` - ReportFormat string `protobuf:"bytes,2,opt,name=reportFormat,proto3" json:"reportFormat,omitempty"` + ReportFormat uint32 `protobuf:"varint,2,opt,name=reportFormat,proto3" json:"reportFormat,omitempty"` } func (x *TransmitRequest) Reset() { @@ -68,11 +68,11 @@ func (x *TransmitRequest) GetPayload() []byte { return nil } -func (x *TransmitRequest) GetReportFormat() string { +func (x *TransmitRequest) GetReportFormat() uint32 { if x != nil { return x.ReportFormat } - return "" + return 0 } type TransmitResponse struct { @@ -454,7 +454,7 @@ var file_mercury_proto_rawDesc = []byte{ 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x6f, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x22, 0x3c, 0x0a, 0x10, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, diff --git a/core/services/relay/evm/mercury/wsrpc/pb/mercury.proto b/core/services/relay/evm/mercury/wsrpc/pb/mercury.proto index 184b0572046..6b71404a6a6 100644 --- a/core/services/relay/evm/mercury/wsrpc/pb/mercury.proto +++ b/core/services/relay/evm/mercury/wsrpc/pb/mercury.proto @@ -11,7 +11,7 @@ service Mercury { message TransmitRequest { bytes payload = 1; - string reportFormat = 2; + uint32 reportFormat = 2; } message TransmitResponse { diff --git a/core/store/migrate/migrations/0224_create_channel_definition_caches.sql b/core/store/migrate/migrations/0224_create_channel_definition_caches.sql new file mode 100644 index 00000000000..9c46f1ceacf --- /dev/null +++ b/core/store/migrate/migrations/0224_create_channel_definition_caches.sql @@ -0,0 +1,12 @@ +-- +goose Up +CREATE TABLE channel_definitions ( + evm_chain_id NUMERIC(78) NOT NULL, + addr bytea CHECK (octet_length(addr) = 20), + definitions JSONB NOT NULL, + block_num BIGINT NOT NULL, + updated_at timestamp with time zone NOT NULL, + PRIMARY KEY (evm_chain_id, addr) +); + +-- +goose Down +DROP TABLE channel_definitions; diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 8fff0e36cad..6647dcc1efd 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 - Gas bumping logic to the `SuggestedPriceEstimator`. The bumping mechanism for this estimator refetches the price from the RPC and adds a buffer on top using the greater of `BumpPercent` and `BumpMin`. +- Add preliminary support for "llo" job type (Data Streams V1) ### Fixed diff --git a/go.mod b/go.mod index c0ef2b819de..2291388def2 100644 --- a/go.mod +++ b/go.mod @@ -71,7 +71,7 @@ require ( github.com/smartcontractkit/chainlink-automation v1.0.2-0.20240118014648-1ab6a88c9429 github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 - github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240214203158-47dae5de1336 + github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 github.com/smartcontractkit/chainlink-feeds v0.0.0-20240119021347-3c541a78cdb8 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240216142700-c5869534c19e github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240213121419-1272736c2ac0 @@ -82,6 +82,7 @@ require ( github.com/smartcontractkit/wsrpc v0.7.2 github.com/spf13/cast v1.6.0 github.com/stretchr/testify v1.8.4 + github.com/test-go/testify v1.1.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.12 @@ -342,4 +343,5 @@ 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 + ) diff --git a/go.sum b/go.sum index 88ea058b06e..13d684fd34d 100644 --- a/go.sum +++ b/go.sum @@ -1172,8 +1172,8 @@ github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6 github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 h1:I326nw5GwHQHsLKHwtu5Sb9EBLylC8CfUd7BFAS0jtg= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8/go.mod h1:a65NtrK4xZb01mf0dDNghPkN2wXgcqFQ55ADthVBgMc= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240214203158-47dae5de1336 h1:j00D0/EqE9HRu+63v7KwUOe4ZxLc4AN5SOJFiinkkH0= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240214203158-47dae5de1336/go.mod h1:umLyYLRGqyIuFfGpEREZP3So6+O8iL35cCCqW+OxX5w= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540/go.mod h1:sjAmX8K2kbQhvDarZE1ZZgDgmHJ50s0BBc/66vKY2ek= 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.20240216142700-c5869534c19e h1:k8HS3GsAFZnxXIW3141VsQP2+EL1XrTtOi/HDt7sdBE= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index ae2ac344c9b..955c0bbb1e5 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -370,7 +370,7 @@ require ( github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chain-selectors v1.0.10 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 // indirect - github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240214203158-47dae5de1336 // indirect + github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20240119021347-3c541a78cdb8 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240216142700-c5869534c19e // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240213121419-1272736c2ac0 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index fe81cc3ac68..e5f34a3d982 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1511,8 +1511,8 @@ github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6 github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 h1:I326nw5GwHQHsLKHwtu5Sb9EBLylC8CfUd7BFAS0jtg= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8/go.mod h1:a65NtrK4xZb01mf0dDNghPkN2wXgcqFQ55ADthVBgMc= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240214203158-47dae5de1336 h1:j00D0/EqE9HRu+63v7KwUOe4ZxLc4AN5SOJFiinkkH0= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240214203158-47dae5de1336/go.mod h1:umLyYLRGqyIuFfGpEREZP3So6+O8iL35cCCqW+OxX5w= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540/go.mod h1:sjAmX8K2kbQhvDarZE1ZZgDgmHJ50s0BBc/66vKY2ek= 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.20240216142700-c5869534c19e h1:k8HS3GsAFZnxXIW3141VsQP2+EL1XrTtOi/HDt7sdBE= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index 174ea743c23..de4360ea4e4 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -357,7 +357,7 @@ require ( github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chain-selectors v1.0.10 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 // indirect - github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240214203158-47dae5de1336 // indirect + github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20240119021347-3c541a78cdb8 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240216142700-c5869534c19e // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240213121419-1272736c2ac0 // indirect diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index 1aaac86bd00..f85521c2f7f 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1494,8 +1494,8 @@ github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6 github.com/smartcontractkit/chainlink-common v0.1.7-0.20240221153538-1ea85cf3dc6c/go.mod h1:6aXWSEQawX2oZXcPPOdxnEGufAhj7PqPKolXf6ijRGA= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8 h1:I326nw5GwHQHsLKHwtu5Sb9EBLylC8CfUd7BFAS0jtg= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240213120401-01a23955f9f8/go.mod h1:a65NtrK4xZb01mf0dDNghPkN2wXgcqFQ55ADthVBgMc= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240214203158-47dae5de1336 h1:j00D0/EqE9HRu+63v7KwUOe4ZxLc4AN5SOJFiinkkH0= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240214203158-47dae5de1336/go.mod h1:umLyYLRGqyIuFfGpEREZP3So6+O8iL35cCCqW+OxX5w= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540 h1:xFSv8561jsLtF6gYZr/zW2z5qUUAkcFkApin2mnbYTo= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240220203239-09be0ea34540/go.mod h1:sjAmX8K2kbQhvDarZE1ZZgDgmHJ50s0BBc/66vKY2ek= 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.20240216142700-c5869534c19e h1:k8HS3GsAFZnxXIW3141VsQP2+EL1XrTtOi/HDt7sdBE= From 0b9dc18fc7e6a223758af0703dfc82d50a99e940 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 22 Feb 2024 12:55:25 -0500 Subject: [PATCH 13/24] Ignore .venv (#12145) --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ae69535d352..e56774fbefc 100644 --- a/.gitignore +++ b/.gitignore @@ -91,4 +91,7 @@ tools/flakeytests/coverage.txt **/testdata/fuzz/* # Runtime test configuration that might contain secrets -overrides.toml \ No newline at end of file +overrides.toml + +# Pythin venv +.venv/ From 85cc5909de2427f9eb61a2dae27713e0ff585263 Mon Sep 17 00:00:00 2001 From: Gabriel Paradiso Date: Thu, 22 Feb 2024 19:23:17 +0100 Subject: [PATCH 14/24] [FUN-877] make persisting allowlist compatible with older contract (#11907) * feat: make persisting allowlist compatible with older contract * feat: check tos contract version instead of a config feature flag * extract contract version comparison to helper function * fix: use semver to compare versions --- .../handlers/functions/allowlist/allowlist.go | 58 ++- .../functions/allowlist/allowlist_test.go | 390 ++++++++++++++---- .../handlers/functions/allowlist/mocks/orm.go | 24 ++ .../handlers/functions/allowlist/orm.go | 24 ++ .../handlers/functions/allowlist/orm_test.go | 65 +++ go.mod | 2 +- 6 files changed, 467 insertions(+), 96 deletions(-) diff --git a/core/services/gateway/handlers/functions/allowlist/allowlist.go b/core/services/gateway/handlers/functions/allowlist/allowlist.go index 20dc92ced70..020de2359c2 100644 --- a/core/services/gateway/handlers/functions/allowlist/allowlist.go +++ b/core/services/gateway/handlers/functions/allowlist/allowlist.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "fmt" "math/big" + "regexp" "sync" "sync/atomic" "time" @@ -12,6 +13,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" + "golang.org/x/mod/semver" "github.com/smartcontractkit/chainlink-common/pkg/services" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" @@ -23,9 +25,10 @@ import ( ) const ( - defaultStoredAllowlistBatchSize = 1000 - defaultOnchainAllowlistBatchSize = 100 - defaultFetchingDelayInRangeSec = 1 + defaultStoredAllowlistBatchSize = 1000 + defaultOnchainAllowlistBatchSize = 100 + defaultFetchingDelayInRangeSec = 1 + tosContractMinBatchProcessingVersion = "v1.1.0" ) type OnchainAllowlistConfig struct { @@ -38,8 +41,6 @@ type OnchainAllowlistConfig struct { 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"` } @@ -210,7 +211,31 @@ func (a *onchainAllowlist) updateFromContractV1(ctx context.Context, blockNum *b } var allowedSenderList []common.Address - if !a.config.StoreAllowedSendersEnabled { + typeAndVersion, err := tosContract.TypeAndVersion(&bind.CallOpts{ + Pending: false, + BlockNumber: blockNum, + Context: ctx, + }) + if err != nil { + return errors.Wrap(err, "failed to fetch the tos contract type and version") + } + + currentVersion, err := ExtractContractVersion(typeAndVersion) + if err != nil { + return fmt.Errorf("failed to extract version: %w", err) + } + + if semver.Compare(tosContractMinBatchProcessingVersion, currentVersion) <= 0 { + 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") + } + } else { allowedSenderList, err = tosContract.GetAllAllowedSenders(&bind.CallOpts{ Pending: false, BlockNumber: blockNum, @@ -219,15 +244,15 @@ func (a *onchainAllowlist) updateFromContractV1(ctx context.Context, blockNum *b if err != nil { return errors.Wrap(err, "error calling GetAllAllowedSenders") } - } else { - err = a.syncBlockedSenders(ctx, tosContract, blockNum) + + err = a.orm.PurgeAllowedSenders() if err != nil { - return errors.Wrap(err, "failed to sync the stored allowed and blocked senders") + a.lggr.Errorf("failed to purge allowedSenderList: %w", err) } - allowedSenderList, err = a.getAllowedSendersBatched(ctx, tosContract, blockNum) + err = a.orm.CreateAllowedSenders(allowedSenderList) if err != nil { - return errors.Wrap(err, "failed to get allowed senders in rage") + a.lggr.Errorf("failed to update stored allowedSenderList: %w", err) } } @@ -344,3 +369,14 @@ func (a *onchainAllowlist) loadStoredAllowedSenderList() { a.update(allowedList) } + +func ExtractContractVersion(str string) (string, error) { + pattern := `v(\d+).(\d+).(\d+)` + re := regexp.MustCompile(pattern) + + match := re.FindStringSubmatch(str) + if len(match) != 4 { + return "", fmt.Errorf("version not found in string: %s", str) + } + return fmt.Sprintf("v%s.%s.%s", match[1], match[2], match[3]), nil +} diff --git a/core/services/gateway/handlers/functions/allowlist/allowlist_test.go b/core/services/gateway/handlers/functions/allowlist/allowlist_test.go index e8cbca80b94..735c0bff7dc 100644 --- a/core/services/gateway/handlers/functions/allowlist/allowlist_test.go +++ b/core/services/gateway/handlers/functions/allowlist/allowlist_test.go @@ -3,11 +3,14 @@ package allowlist_test import ( "context" "encoding/hex" + "fmt" "math/big" "testing" "time" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/onsi/gomega" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -18,55 +21,105 @@ import ( "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" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" ) const ( - addr1 = "9ed925d8206a4f88a2f643b28b3035b315753cd6" - addr2 = "ea6721ac65bced841b8ec3fc5fedea6141a0ade4" - addr3 = "84689acc87ff22841b8ec378300da5e141a99911" + addr1 = "9ed925d8206a4f88a2f643b28b3035b315753cd6" + addr2 = "ea6721ac65bced841b8ec3fc5fedea6141a0ade4" + addr3 = "84689acc87ff22841b8ec378300da5e141a99911" + ToSContractV100 = "Functions Terms of Service Allow List v1.0.0" + ToSContractV110 = "Functions Terms of Service Allow List v1.1.0" ) -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) { +func TestUpdateAndCheck(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, - } + t.Run("OK-with_ToS_V1.0.0", func(t *testing.T) { + client := mocks.NewClient(t) + client.On("LatestBlockHeight", mock.Anything).Return(big.NewInt(42), nil) - orm := amocks.NewORM(t) - allowlist, err := allowlist.NewOnchainAllowlist(client, config, orm, logger.TestLogger(t)) - require.NoError(t, err) + addr := common.HexToAddress("0x0000000000000000000000000000000000000020") + typeAndVersionResponse, err := encodeTypeAndVersionResponse(ToSContractV100) + require.NoError(t, err) - err = allowlist.Start(testutils.Context(t)) - require.NoError(t, err) - t.Cleanup(func() { - assert.NoError(t, allowlist.Close()) + client.On("CallContract", mock.Anything, ethereum.CallMsg{ // typeAndVersion + To: &addr, + Data: hexutil.MustDecode("0x181f5a77"), + }, mock.Anything).Return(typeAndVersionResponse, 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) + orm.On("PurgeAllowedSenders").Times(1).Return(nil) + orm.On("CreateAllowedSenders", []common.Address{common.HexToAddress(addr1), common.HexToAddress(addr2)}).Times(1).Return(nil) + + 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))) }) - 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))) + t.Run("OK-with_ToS_V1.1.0", func(t *testing.T) { + client := mocks.NewClient(t) + client.On("LatestBlockHeight", mock.Anything).Return(big.NewInt(42), nil) + + typeAndVersionResponse, err := encodeTypeAndVersionResponse(ToSContractV110) + require.NoError(t, err) + + addr := common.HexToAddress("0x0000000000000000000000000000000000000020") + client.On("CallContract", mock.Anything, ethereum.CallMsg{ // typeAndVersion + To: &addr, + Data: hexutil.MustDecode("0x181f5a77"), + }, mock.Anything).Return(typeAndVersionResponse, 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) + orm.On("DeleteAllowedSenders", []common.Address{common.HexToAddress(addr1), common.HexToAddress(addr2)}).Times(1).Return(nil) + orm.On("CreateAllowedSenders", []common.Address{common.HexToAddress(addr1), common.HexToAddress(addr2)}).Times(1).Return(nil) + + 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) { +func TestUnsupportedVersion(t *testing.T) { t.Parallel() client := mocks.NewClient(t) @@ -81,64 +134,132 @@ func TestAllowlist_UnsupportedVersion(t *testing.T) { require.Error(t, err) } -func TestAllowlist_UpdatePeriodically(t *testing.T) { +func TestUpdatePeriodically(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, - } + t.Run("OK-with_ToS_V1.0.0", 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) - orm := amocks.NewORM(t) - orm.On("GetAllowedSenders", uint(0), uint(1000)).Return([]common.Address{}, nil) + addr := common.HexToAddress("0x0000000000000000000000000000000000000020") + typeAndVersionResponse, err := encodeTypeAndVersionResponse(ToSContractV100) + require.NoError(t, err) - allowlist, err := allowlist.NewOnchainAllowlist(client, config, orm, logger.TestLogger(t)) - require.NoError(t, err) + client.On("CallContract", mock.Anything, ethereum.CallMsg{ // typeAndVersion + To: &addr, + Data: hexutil.MustDecode("0x181f5a77"), + }, mock.Anything).Return(typeAndVersionResponse, nil) - err = allowlist.Start(ctx) - require.NoError(t, err) - t.Cleanup(func() { - assert.NoError(t, allowlist.Close()) + 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("PurgeAllowedSenders").Times(1).Return(nil) + orm.On("GetAllowedSenders", uint(0), uint(1000)).Return([]common.Address{}, nil) + orm.On("CreateAllowedSenders", []common.Address{common.HexToAddress(addr1), common.HexToAddress(addr2)}).Times(1).Return(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()) }) - 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-with_ToS_V1.1.0", 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) + + addr := common.HexToAddress("0x0000000000000000000000000000000000000020") + typeAndVersionResponse, err := encodeTypeAndVersionResponse(ToSContractV110) + require.NoError(t, err) + + client.On("CallContract", mock.Anything, ethereum.CallMsg{ // typeAndVersion + To: &addr, + Data: hexutil.MustDecode("0x181f5a77"), + }, mock.Anything).Return(typeAndVersionResponse, 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("DeleteAllowedSenders", []common.Address{common.HexToAddress(addr1), common.HexToAddress(addr2)}).Times(1).Return(nil) + orm.On("GetAllowedSenders", uint(0), uint(1000)).Return([]common.Address{}, nil) + orm.On("CreateAllowedSenders", []common.Address{common.HexToAddress(addr1), common.HexToAddress(addr2)}).Times(1).Return(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) { + +func TestUpdateFromContract(t *testing.T) { t.Parallel() - t.Run("OK-iterate_over_list_of_allowed_senders", func(t *testing.T) { + t.Run("OK-fetch_complete_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) + + addr := common.HexToAddress("0x0000000000000000000000000000000000000020") + typeAndVersionResponse, err := encodeTypeAndVersionResponse(ToSContractV100) + require.NoError(t, err) + + client.On("CallContract", mock.Anything, ethereum.CallMsg{ // typeAndVersion + To: &addr, + Data: hexutil.MustDecode("0x181f5a77"), + }, mock.Anything).Return(typeAndVersionResponse, 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, + ContractAddress: common.HexToAddress(addr3), + ContractVersion: 1, + BlockConfirmations: 1, + UpdateFrequencySec: 2, + UpdateTimeoutSec: 1, + StoredAllowlistBatchSize: 2, + OnchainAllowlistBatchSize: 16, + 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) + orm.On("PurgeAllowedSenders").Times(1).Return(nil) + orm.On("CreateAllowedSenders", []common.Address{common.HexToAddress(addr1), common.HexToAddress(addr2)}).Times(1).Return(nil) allowlist, err := allowlist.NewOnchainAllowlist(client, config, orm, logger.TestLogger(t)) require.NoError(t, err) @@ -151,26 +272,38 @@ func TestAllowlist_UpdateFromContract(t *testing.T) { }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeTrue()) }) - t.Run("OK-fetch_complete_list_of_allowed_senders_without_storing", func(t *testing.T) { + 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) + + addr := common.HexToAddress("0x0000000000000000000000000000000000000020") + typeAndVersionResponse, err := encodeTypeAndVersionResponse(ToSContractV110) + require.NoError(t, err) + + client.On("CallContract", mock.Anything, ethereum.CallMsg{ // typeAndVersion + To: &addr, + Data: hexutil.MustDecode("0x181f5a77"), + }, mock.Anything).Return(typeAndVersionResponse, 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, + ContractAddress: common.HexToAddress(addr3), + ContractVersion: 1, + BlockConfirmations: 1, + UpdateFrequencySec: 2, + UpdateTimeoutSec: 1, + StoredAllowlistBatchSize: 2, + OnchainAllowlistBatchSize: 16, + 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) @@ -181,4 +314,93 @@ func TestAllowlist_UpdateFromContract(t *testing.T) { return allowlist.Allow(common.HexToAddress(addr1)) && !allowlist.Allow(common.HexToAddress(addr3)) }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeTrue()) }) + +} + +func TestExtractContractVersion(t *testing.T) { + + type tc struct { + name string + versionStr string + expectedResult string + expectedError *string + } + + var errInvalidVersion = func(v string) *string { + ev := fmt.Sprintf("version not found in string: %s", v) + return &ev + } + + tcs := []tc{ + { + name: "OK-Tos_type_and_version", + versionStr: "Functions Terms of Service Allow List v1.1.0", + expectedResult: "v1.1.0", + expectedError: nil, + }, + { + name: "OK-double_digits_minor", + versionStr: "Functions Terms of Service Allow List v1.20.0", + expectedResult: "v1.20.0", + expectedError: nil, + }, + { + name: "NOK-invalid_version", + versionStr: "invalid_version", + expectedResult: "", + expectedError: errInvalidVersion("invalid_version"), + }, + { + name: "NOK-incomplete_version", + versionStr: "v2.0", + expectedResult: "", + expectedError: errInvalidVersion("v2.0"), + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + actualResult, actualError := allowlist.ExtractContractVersion(tc.versionStr) + require.Equal(t, tc.expectedResult, actualResult) + + if tc.expectedError != nil { + require.EqualError(t, actualError, *tc.expectedError) + } else { + require.NoError(t, actualError) + } + }) + } +} + +func encodeTypeAndVersionResponse(typeAndVersion string) ([]byte, error) { + codecName := "my_codec" + evmEncoderConfig := `[{"Name":"typeAndVersion","Type":"string"}]` + codecConfig := types.CodecConfig{Configs: map[string]types.ChainCodecConfig{ + codecName: {TypeABI: evmEncoderConfig}, + }} + encoder, err := evm.NewCodec(codecConfig) + if err != nil { + return nil, err + } + + input := map[string]any{ + "typeAndVersion": typeAndVersion, + } + typeAndVersionResponse, err := encoder.Encode(context.Background(), input, codecName) + if err != nil { + return nil, err + } + + return typeAndVersionResponse, nil +} + +func sampleEncodedAllowlist(t *testing.T) []byte { + abiEncodedAddresses := + "0000000000000000000000000000000000000000000000000000000000000020" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "000000000000000000000000" + addr1 + + "000000000000000000000000" + addr2 + rawData, err := hex.DecodeString(abiEncodedAddresses) + require.NoError(t, err) + return rawData } diff --git a/core/services/gateway/handlers/functions/allowlist/mocks/orm.go b/core/services/gateway/handlers/functions/allowlist/mocks/orm.go index c2ba27c3a24..daff33d8902 100644 --- a/core/services/gateway/handlers/functions/allowlist/mocks/orm.go +++ b/core/services/gateway/handlers/functions/allowlist/mocks/orm.go @@ -101,6 +101,30 @@ func (_m *ORM) GetAllowedSenders(offset uint, limit uint, qopts ...pg.QOpt) ([]c return r0, r1 } +// PurgeAllowedSenders provides a mock function with given fields: qopts +func (_m *ORM) PurgeAllowedSenders(qopts ...pg.QOpt) error { + _va := make([]interface{}, len(qopts)) + for _i := range qopts { + _va[_i] = qopts[_i] + } + var _ca []interface{} + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for PurgeAllowedSenders") + } + + var r0 error + if rf, ok := ret.Get(0).(func(...pg.QOpt) error); ok { + r0 = rf(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 { diff --git a/core/services/gateway/handlers/functions/allowlist/orm.go b/core/services/gateway/handlers/functions/allowlist/orm.go index 07ee1ea3b3b..ccacec81a43 100644 --- a/core/services/gateway/handlers/functions/allowlist/orm.go +++ b/core/services/gateway/handlers/functions/allowlist/orm.go @@ -18,6 +18,7 @@ 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 + PurgeAllowedSenders(qopts ...pg.QOpt) error } type orm struct { @@ -91,6 +92,8 @@ func (o *orm) CreateAllowedSenders(allowedSenders []common.Address, qopts ...pg. return nil } +// DeleteAllowedSenders is used to remove blocked senders from the functions_allowlist table. +// This is achieved by specifying a list of blockedSenders to remove. func (o *orm) DeleteAllowedSenders(blockedSenders []common.Address, qopts ...pg.QOpt) error { var valuesPlaceholder []string for i := 1; i <= len(blockedSenders); i++ { @@ -121,3 +124,24 @@ func (o *orm) DeleteAllowedSenders(blockedSenders []common.Address, qopts ...pg. return nil } + +// PurgeAllowedSenders will remove all the allowed senders for the configured orm routerContractAddress +func (o *orm) PurgeAllowedSenders(qopts ...pg.QOpt) error { + stmt := fmt.Sprintf(` + DELETE FROM %s + WHERE router_contract_address = $1;`, tableName) + + res, err := o.q.WithOpts(qopts...).Exec(stmt, o.routerContractAddress) + if err != nil { + return err + } + + rowsAffected, err := res.RowsAffected() + if err != nil { + return err + } + + o.lggr.Debugf("Successfully purged allowed senders for routerContractAddress: %s. rowsAffected: %d", 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 index 0f63e83cd5f..1d357616fab 100644 --- a/core/services/gateway/handlers/functions/allowlist/orm_test.go +++ b/core/services/gateway/handlers/functions/allowlist/orm_test.go @@ -174,6 +174,71 @@ func TestORM_DeleteAllowedSenders(t *testing.T) { }) } +func TestORM_PurgeAllowedSenders(t *testing.T) { + t.Parallel() + + t.Run("OK-purge_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.PurgeAllowedSenders() + require.NoError(t, err) + + results, err = orm.GetAllowedSenders(0, 10) + require.NoError(t, err) + require.Equal(t, 0, len(results), "incorrect results length") + }) + + t.Run("OK-purge_allowed_list_for_contract_address", func(t *testing.T) { + orm1, err := setupORM(t) + require.NoError(t, err) + add1 := testutils.NewAddress() + add2 := testutils.NewAddress() + err = orm1.CreateAllowedSenders([]common.Address{add1, add2}) + require.NoError(t, err) + + results, err := orm1.GetAllowedSenders(0, 10) + require.NoError(t, err) + require.Equal(t, 2, len(results), "incorrect results length") + require.Equal(t, add1, results[0]) + + orm2, err := setupORM(t) + require.NoError(t, err) + add3 := testutils.NewAddress() + add4 := testutils.NewAddress() + err = orm2.CreateAllowedSenders([]common.Address{add3, add4}) + require.NoError(t, err) + + results, err = orm2.GetAllowedSenders(0, 10) + require.NoError(t, err) + require.Equal(t, 2, len(results), "incorrect results length") + require.Equal(t, add3, results[0]) + + err = orm2.PurgeAllowedSenders() + require.NoError(t, err) + + results, err = orm2.GetAllowedSenders(0, 10) + require.NoError(t, err) + require.Equal(t, 0, len(results), "incorrect results length") + + results, err = orm1.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()) diff --git a/go.mod b/go.mod index 2291388def2..1e0c09b06bf 100644 --- a/go.mod +++ b/go.mod @@ -98,6 +98,7 @@ require ( go.uber.org/zap v1.26.0 golang.org/x/crypto v0.19.0 golang.org/x/exp v0.0.0-20240213143201-ec583247a57a + golang.org/x/mod v0.15.0 golang.org/x/sync v0.6.0 golang.org/x/term v0.17.0 golang.org/x/text v0.14.0 @@ -316,7 +317,6 @@ require ( go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.uber.org/ratelimit v0.2.0 // indirect golang.org/x/arch v0.7.0 // indirect - golang.org/x/mod v0.15.0 // indirect golang.org/x/net v0.21.0 // indirect golang.org/x/oauth2 v0.17.0 // indirect golang.org/x/sys v0.17.0 // indirect From 63c286d81d9c5032a334c629616e6fab0ca6597a Mon Sep 17 00:00:00 2001 From: Domino Valdano <2644901+reductionista@users.noreply.github.com> Date: Thu, 22 Feb 2024 10:46:14 -0800 Subject: [PATCH 15/24] Add Topic1, Topic2, Topic3, LogsPerBlock fields to logpoller.Filter and log_poller_filters schema (#11949) * Add Topic1, Topic2, Topic3, LogsPerBlock fields to logpoller.Filter And update evm.log_poller_filters schema to match * Improve SQL QUERY logging []BYTEA was panicing for nil or empty array, which then gets trapped and ignored... making it very difficult to figure out which query the error is coming from. While fixing that bug, updated formating of []BYTEA and TEXT to be closer an actual SQL query you can run by cutting and pasting from the log * Add MaxLogsKept * Address PR comments - Add new index before removing old index to be safer - Change MaxLogsKept & LogsPerBlock from big.Int to uint64 * Update migration # after rebase --- core/chains/evm/logpoller/log_poller.go | 13 ++- .../evm/logpoller/log_poller_internal_test.go | 10 +- core/chains/evm/logpoller/log_poller_test.go | 74 +++++++++----- core/chains/evm/logpoller/orm.go | 37 +++++-- core/chains/evm/logpoller/orm_test.go | 96 +++++++++++++++++++ core/chains/evm/logpoller/query.go | 23 +++++ core/chains/evm/types/types.go | 14 ++- core/services/pg/q.go | 17 +++- core/services/pg/q_test.go | 12 +-- ...ller_filters_add_topics_logs_per_block.sql | 29 ++++++ 10 files changed, 271 insertions(+), 54 deletions(-) create mode 100644 core/store/migrate/migrations/0225_log_poller_filters_add_topics_logs_per_block.sql diff --git a/core/chains/evm/logpoller/log_poller.go b/core/chains/evm/logpoller/log_poller.go index 7006c1762ef..a2c35bec59f 100644 --- a/core/chains/evm/logpoller/log_poller.go +++ b/core/chains/evm/logpoller/log_poller.go @@ -153,10 +153,15 @@ func NewLogPoller(orm ORM, ec Client, lggr logger.Logger, pollPeriod time.Durati } type Filter struct { - Name string // see FilterName(id, args) below - EventSigs evmtypes.HashArray - Addresses evmtypes.AddressArray - Retention time.Duration + Name string // see FilterName(id, args) below + Addresses evmtypes.AddressArray + EventSigs evmtypes.HashArray // list of possible values for eventsig (aka topic1) + Topic2 evmtypes.HashArray // list of possible values for topic2 + Topic3 evmtypes.HashArray // list of possible values for topic3 + Topic4 evmtypes.HashArray // list of possible values for topic4 + Retention time.Duration // maximum amount of time to retain logs + MaxLogsKept uint64 // maximum number of logs to retain ( 0 = unlimited ) + LogsPerBlock uint64 // rate limit ( maximum # of logs per block, 0 = unlimited ) } // FilterName is a suggested convenience function for clients to construct unique filter names diff --git a/core/chains/evm/logpoller/log_poller_internal_test.go b/core/chains/evm/logpoller/log_poller_internal_test.go index 863ab0fddea..899efebe42c 100644 --- a/core/chains/evm/logpoller/log_poller_internal_test.go +++ b/core/chains/evm/logpoller/log_poller_internal_test.go @@ -71,31 +71,31 @@ func TestLogPoller_RegisterFilter(t *testing.T) { require.Equal(t, 1, len(f.Addresses)) assert.Equal(t, common.HexToAddress("0x0000000000000000000000000000000000000000"), f.Addresses[0]) - err := lp.RegisterFilter(Filter{"Emitter Log 1", []common.Hash{EmitterABI.Events["Log1"].ID}, []common.Address{a1}, 0}) + err := lp.RegisterFilter(Filter{Name: "Emitter Log 1", EventSigs: []common.Hash{EmitterABI.Events["Log1"].ID}, Addresses: []common.Address{a1}}) require.NoError(t, err) assert.Equal(t, []common.Address{a1}, lp.Filter(nil, nil, nil).Addresses) assert.Equal(t, [][]common.Hash{{EmitterABI.Events["Log1"].ID}}, lp.Filter(nil, nil, nil).Topics) validateFiltersTable(t, lp, orm) // Should de-dupe EventSigs - err = lp.RegisterFilter(Filter{"Emitter Log 1 + 2", []common.Hash{EmitterABI.Events["Log1"].ID, EmitterABI.Events["Log2"].ID}, []common.Address{a2}, 0}) + err = lp.RegisterFilter(Filter{Name: "Emitter Log 1 + 2", EventSigs: []common.Hash{EmitterABI.Events["Log1"].ID, EmitterABI.Events["Log2"].ID}, Addresses: []common.Address{a2}}) require.NoError(t, err) assert.Equal(t, []common.Address{a1, a2}, lp.Filter(nil, nil, nil).Addresses) assert.Equal(t, [][]common.Hash{{EmitterABI.Events["Log1"].ID, EmitterABI.Events["Log2"].ID}}, lp.Filter(nil, nil, nil).Topics) validateFiltersTable(t, lp, orm) // Should de-dupe Addresses - err = lp.RegisterFilter(Filter{"Emitter Log 1 + 2 dupe", []common.Hash{EmitterABI.Events["Log1"].ID, EmitterABI.Events["Log2"].ID}, []common.Address{a2}, 0}) + err = lp.RegisterFilter(Filter{Name: "Emitter Log 1 + 2 dupe", EventSigs: []common.Hash{EmitterABI.Events["Log1"].ID, EmitterABI.Events["Log2"].ID}, Addresses: []common.Address{a2}}) require.NoError(t, err) assert.Equal(t, []common.Address{a1, a2}, lp.Filter(nil, nil, nil).Addresses) assert.Equal(t, [][]common.Hash{{EmitterABI.Events["Log1"].ID, EmitterABI.Events["Log2"].ID}}, lp.Filter(nil, nil, nil).Topics) validateFiltersTable(t, lp, orm) // Address required. - err = lp.RegisterFilter(Filter{"no address", []common.Hash{EmitterABI.Events["Log1"].ID}, []common.Address{}, 0}) + err = lp.RegisterFilter(Filter{Name: "no address", EventSigs: []common.Hash{EmitterABI.Events["Log1"].ID}}) require.Error(t, err) // Event required - err = lp.RegisterFilter(Filter{"No event", []common.Hash{}, []common.Address{a1}, 0}) + err = lp.RegisterFilter(Filter{Name: "No event", Addresses: []common.Address{a1}}) require.Error(t, err) validateFiltersTable(t, lp, orm) diff --git a/core/chains/evm/logpoller/log_poller_test.go b/core/chains/evm/logpoller/log_poller_test.go index 2508e676e6c..ab02e783a28 100644 --- a/core/chains/evm/logpoller/log_poller_test.go +++ b/core/chains/evm/logpoller/log_poller_test.go @@ -150,7 +150,7 @@ func TestLogPoller_Integration(t *testing.T) { th := SetupTH(t, false, 2, 3, 2, 1000) th.Client.Commit() // Block 2. Ensure we have finality number of blocks - require.NoError(t, th.LogPoller.RegisterFilter(logpoller.Filter{"Integration test", []common.Hash{EmitterABI.Events["Log1"].ID}, []common.Address{th.EmitterAddress1}, 0})) + require.NoError(t, th.LogPoller.RegisterFilter(logpoller.Filter{Name: "Integration test", EventSigs: []common.Hash{EmitterABI.Events["Log1"].ID}, Addresses: []common.Address{th.EmitterAddress1}})) require.Len(t, th.LogPoller.Filter(nil, nil, nil).Addresses, 1) require.Len(t, th.LogPoller.Filter(nil, nil, nil).Topics, 1) @@ -188,8 +188,9 @@ func TestLogPoller_Integration(t *testing.T) { // Now let's update the Filter and replay to get Log2 logs. err = th.LogPoller.RegisterFilter(logpoller.Filter{ - "Emitter - log2", []common.Hash{EmitterABI.Events["Log2"].ID}, - []common.Address{th.EmitterAddress1}, 0, + Name: "Emitter - log2", + EventSigs: []common.Hash{EmitterABI.Events["Log2"].ID}, + Addresses: []common.Address{th.EmitterAddress1}, }) require.NoError(t, err) // Replay an invalid block should error @@ -254,11 +255,13 @@ func Test_BackupLogPoller(t *testing.T) { ctx := testutils.Context(t) - filter1 := logpoller.Filter{"filter1", []common.Hash{ - EmitterABI.Events["Log1"].ID, - EmitterABI.Events["Log2"].ID}, - []common.Address{th.EmitterAddress1}, - 0} + filter1 := logpoller.Filter{ + Name: "filter1", + EventSigs: []common.Hash{ + EmitterABI.Events["Log1"].ID, + EmitterABI.Events["Log2"].ID}, + Addresses: []common.Address{th.EmitterAddress1}, + } err := th.LogPoller.RegisterFilter(filter1) require.NoError(t, err) @@ -268,9 +271,11 @@ func Test_BackupLogPoller(t *testing.T) { require.Equal(t, filter1, filters["filter1"]) err = th.LogPoller.RegisterFilter( - logpoller.Filter{"filter2", - []common.Hash{EmitterABI.Events["Log1"].ID}, - []common.Address{th.EmitterAddress2}, 0}) + logpoller.Filter{ + Name: "filter2", + EventSigs: []common.Hash{EmitterABI.Events["Log1"].ID}, + Addresses: []common.Address{th.EmitterAddress2}, + }) require.NoError(t, err) defer func() { @@ -569,9 +574,9 @@ func TestLogPoller_BlockTimestamps(t *testing.T) { th := SetupTH(t, false, 2, 3, 2, 1000) addresses := []common.Address{th.EmitterAddress1, th.EmitterAddress2} - topics := []common.Hash{EmitterABI.Events["Log1"].ID, EmitterABI.Events["Log2"].ID} + events := []common.Hash{EmitterABI.Events["Log1"].ID, EmitterABI.Events["Log2"].ID} - err := th.LogPoller.RegisterFilter(logpoller.Filter{"convertLogs", topics, addresses, 0}) + err := th.LogPoller.RegisterFilter(logpoller.Filter{Name: "convertLogs", EventSigs: events, Addresses: addresses}) require.NoError(t, err) blk, err := th.Client.BlockByNumber(ctx, nil) @@ -619,7 +624,7 @@ func TestLogPoller_BlockTimestamps(t *testing.T) { query := ethereum.FilterQuery{ FromBlock: big.NewInt(2), ToBlock: big.NewInt(5), - Topics: [][]common.Hash{topics}, + Topics: [][]common.Hash{events}, Addresses: []common.Address{th.EmitterAddress1, th.EmitterAddress2}} gethLogs, err := th.Client.FilterLogs(ctx, query) @@ -762,8 +767,9 @@ func TestLogPoller_PollAndSaveLogs(t *testing.T) { // Set up a log poller listening for log emitter logs. err := th.LogPoller.RegisterFilter(logpoller.Filter{ - "Test Emitter 1 & 2", []common.Hash{EmitterABI.Events["Log1"].ID, EmitterABI.Events["Log2"].ID}, - []common.Address{th.EmitterAddress1, th.EmitterAddress2}, 0, + Name: "Test Emitter 1 & 2", + EventSigs: []common.Hash{EmitterABI.Events["Log1"].ID, EmitterABI.Events["Log2"].ID}, + Addresses: []common.Address{th.EmitterAddress1, th.EmitterAddress2}, }) require.NoError(t, err) @@ -1068,12 +1074,22 @@ func TestLogPoller_LoadFilters(t *testing.T) { t.Parallel() th := SetupTH(t, false, 2, 3, 2, 1000) - filter1 := logpoller.Filter{"first Filter", []common.Hash{ - EmitterABI.Events["Log1"].ID, EmitterABI.Events["Log2"].ID}, []common.Address{th.EmitterAddress1, th.EmitterAddress2}, 0} - filter2 := logpoller.Filter{"second Filter", []common.Hash{ - EmitterABI.Events["Log2"].ID, EmitterABI.Events["Log3"].ID}, []common.Address{th.EmitterAddress2}, 0} - filter3 := logpoller.Filter{"third Filter", []common.Hash{ - EmitterABI.Events["Log1"].ID}, []common.Address{th.EmitterAddress1, th.EmitterAddress2}, 0} + filter1 := logpoller.Filter{ + Name: "first Filter", + EventSigs: []common.Hash{ + EmitterABI.Events["Log1"].ID, EmitterABI.Events["Log2"].ID}, + Addresses: []common.Address{th.EmitterAddress1, th.EmitterAddress2}, + } + filter2 := logpoller.Filter{ + Name: "second Filter", + EventSigs: []common.Hash{EmitterABI.Events["Log2"].ID, EmitterABI.Events["Log3"].ID}, + Addresses: []common.Address{th.EmitterAddress2}, + } + filter3 := logpoller.Filter{ + Name: "third Filter", + EventSigs: []common.Hash{EmitterABI.Events["Log1"].ID}, + Addresses: []common.Address{th.EmitterAddress1, th.EmitterAddress2}, + } assert.True(t, filter1.Contains(nil)) assert.False(t, filter1.Contains(&filter2)) @@ -1119,9 +1135,11 @@ func TestLogPoller_GetBlocks_Range(t *testing.T) { t.Parallel() th := SetupTH(t, false, 2, 3, 2, 1000) - err := th.LogPoller.RegisterFilter(logpoller.Filter{"GetBlocks Test", []common.Hash{ - EmitterABI.Events["Log1"].ID, EmitterABI.Events["Log2"].ID}, []common.Address{th.EmitterAddress1, th.EmitterAddress2}, 0}, - ) + err := th.LogPoller.RegisterFilter(logpoller.Filter{ + Name: "GetBlocks Test", + EventSigs: []common.Hash{EmitterABI.Events["Log1"].ID, EmitterABI.Events["Log2"].ID}, + Addresses: []common.Address{th.EmitterAddress1, th.EmitterAddress2}, + }) require.NoError(t, err) // LP retrieves 0 blocks @@ -1365,7 +1383,11 @@ func TestTooManyLogResults(t *testing.T) { }) addr := testutils.NewAddress() - err := lp.RegisterFilter(logpoller.Filter{"Integration test", []common.Hash{EmitterABI.Events["Log1"].ID}, []common.Address{addr}, 0}) + err := lp.RegisterFilter(logpoller.Filter{ + Name: "Integration test", + EventSigs: []common.Hash{EmitterABI.Events["Log1"].ID}, + Addresses: []common.Address{addr}, + }) require.NoError(t, err) lp.PollAndSaveLogs(ctx, 5) block, err2 := o.SelectLatestBlock() diff --git a/core/chains/evm/logpoller/orm.go b/core/chains/evm/logpoller/orm.go index 1db8271ccb6..fe814e8a43c 100644 --- a/core/chains/evm/logpoller/orm.go +++ b/core/chains/evm/logpoller/orm.go @@ -5,6 +5,7 @@ import ( "database/sql" "fmt" "math/big" + "strings" "time" "github.com/ethereum/go-ethereum/common" @@ -13,6 +14,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" + "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" ) @@ -95,26 +97,42 @@ func (o *DbORM) InsertBlock(blockHash common.Hash, blockNumber int64, blockTimes // Each address/event pair must have a unique job id, so it may be removed when the job is deleted. // If a second job tries to overwrite the same pair, this should fail. func (o *DbORM) InsertFilter(filter Filter, qopts ...pg.QOpt) (err error) { + topicArrays := []types.HashArray{filter.Topic2, filter.Topic3, filter.Topic4} args, err := newQueryArgs(o.chainID). withCustomArg("name", filter.Name). - withCustomArg("retention", filter.Retention). + withRetention(filter.Retention). + withMaxLogsKept(filter.MaxLogsKept). + withLogsPerBlock(filter.LogsPerBlock). withAddressArray(filter.Addresses). withEventSigArray(filter.EventSigs). + withTopicArrays(filter.Topic2, filter.Topic3, filter.Topic4). toArgs() if err != nil { return err } // '::' has to be escaped in the query string // https://github.com/jmoiron/sqlx/issues/91, https://github.com/jmoiron/sqlx/issues/428 - return o.q.WithOpts(qopts...).ExecQNamed(` + var topicsColumns, topicsSql strings.Builder + for n, topicValues := range topicArrays { + if len(topicValues) != 0 { + topicCol := fmt.Sprintf("topic%d", n+2) + fmt.Fprintf(&topicsColumns, ", %s", topicCol) + fmt.Fprintf(&topicsSql, ",\n(SELECT unnest(:%s ::::BYTEA[]) %s) t%d", topicCol, topicCol, n+2) + } + } + query := fmt.Sprintf(` INSERT INTO evm.log_poller_filters - (name, evm_chain_id, retention, created_at, address, event) + (name, evm_chain_id, retention, max_logs_kept, logs_per_block, created_at, address, event %s) SELECT * FROM - (SELECT :name, :evm_chain_id ::::NUMERIC, :retention ::::BIGINT, NOW()) x, + (SELECT :name, :evm_chain_id ::::NUMERIC, :retention ::::BIGINT, :max_logs_kept ::::NUMERIC, :logs_per_block ::::NUMERIC, NOW()) x, (SELECT unnest(:address_array ::::BYTEA[]) addr) a, (SELECT unnest(:event_sig_array ::::BYTEA[]) ev) e - ON CONFLICT (name, evm_chain_id, address, event) - DO UPDATE SET retention=:retention ::::BIGINT`, args) + %s + ON CONFLICT (hash_record_extended((name, evm_chain_id, address, event, topic2, topic3, topic4), 0)) + DO UPDATE SET retention=:retention ::::BIGINT, max_logs_kept=:max_logs_kept ::::NUMERIC, logs_per_block=:logs_per_block ::::NUMERIC`, + topicsColumns.String(), + topicsSql.String()) + return o.q.WithOpts(qopts...).ExecQNamed(query, args) } // DeleteFilter removes all events,address pairs associated with the Filter @@ -130,7 +148,12 @@ func (o *DbORM) LoadFilters(qopts ...pg.QOpt) (map[string]Filter, error) { err := q.Select(&rows, `SELECT name, ARRAY_AGG(DISTINCT address)::BYTEA[] AS addresses, ARRAY_AGG(DISTINCT event)::BYTEA[] AS event_sigs, - MAX(retention) AS retention + ARRAY_AGG(DISTINCT topic2 ORDER BY topic2) FILTER(WHERE topic2 IS NOT NULL) AS topic2, + ARRAY_AGG(DISTINCT topic3 ORDER BY topic3) FILTER(WHERE topic3 IS NOT NULL) AS topic3, + ARRAY_AGG(DISTINCT topic4 ORDER BY topic4) FILTER(WHERE topic4 IS NOT NULL) AS topic4, + MAX(logs_per_block) AS logs_per_block, + MAX(retention) AS retention, + MAX(max_logs_kept) AS max_logs_kept FROM evm.log_poller_filters WHERE evm_chain_id = $1 GROUP BY name`, ubig.New(o.chainID)) filters := make(map[string]Filter) diff --git a/core/chains/evm/logpoller/orm_test.go b/core/chains/evm/logpoller/orm_test.go index bcaa6f72fa0..9824d0e9426 100644 --- a/core/chains/evm/logpoller/orm_test.go +++ b/core/chains/evm/logpoller/orm_test.go @@ -2,6 +2,7 @@ package logpoller_test import ( "bytes" + "context" "database/sql" "fmt" "math" @@ -10,6 +11,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/jackc/pgx/v4" "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -448,6 +450,100 @@ func TestORM(t *testing.T) { require.Zero(t, len(logs)) } +type PgxLogger struct { + lggr logger.Logger +} + +func NewPgxLogger(lggr logger.Logger) PgxLogger { + return PgxLogger{lggr} +} + +func (l PgxLogger) Log(ctx context.Context, log pgx.LogLevel, msg string, data map[string]interface{}) { + +} + +func TestLogPollerFilters(t *testing.T) { + lggr := logger.Test(t) + chainID := testutils.NewRandomEVMChainID() + + dbx := pgtest.NewSqlxDB(t) + orm := logpoller.NewORM(chainID, dbx, lggr, pgtest.NewQConfig(true)) + + event1 := EmitterABI.Events["Log1"].ID + event2 := EmitterABI.Events["Log2"].ID + address := common.HexToAddress("0x1234") + topicA := common.HexToHash("0x1111") + topicB := common.HexToHash("0x2222") + topicC := common.HexToHash("0x3333") + topicD := common.HexToHash("0x4444") + + filters := []logpoller.Filter{{ + Name: "filter by topic2", + EventSigs: types.HashArray{event1, event2}, + Addresses: types.AddressArray{address}, + Topic2: types.HashArray{topicA, topicB}, + }, { + Name: "filter by topic3", + Addresses: types.AddressArray{address}, + EventSigs: types.HashArray{event1}, + Topic3: types.HashArray{topicB, topicC, topicD}, + }, { + Name: "filter by topic4", + Addresses: types.AddressArray{address}, + EventSigs: types.HashArray{event1}, + Topic4: types.HashArray{topicC}, + }, { + Name: "filter by topics 2 and 4", + Addresses: types.AddressArray{address}, + EventSigs: types.HashArray{event2}, + Topic2: types.HashArray{topicA}, + Topic4: types.HashArray{topicC, topicD}, + }, { + Name: "10 lpb rate limit, 1M max logs", + Addresses: types.AddressArray{address}, + EventSigs: types.HashArray{event1}, + MaxLogsKept: 1000000, + LogsPerBlock: 10, + }, { // ensure that the UNIQUE CONSTRAINT isn't too strict (should only error if all fields are identical) + Name: "duplicate of filter by topic4", + Addresses: types.AddressArray{address}, + EventSigs: types.HashArray{event1}, + Topic3: types.HashArray{topicC}, + }} + + for _, filter := range filters { + t.Run("Save filter: "+filter.Name, func(t *testing.T) { + var count int + err := orm.InsertFilter(filter) + require.NoError(t, err) + err = dbx.Get(&count, `SELECT COUNT(*) FROM evm.log_poller_filters WHERE evm_chain_id = $1 AND name = $2`, ubig.New(chainID), filter.Name) + require.NoError(t, err) + expectedCount := len(filter.Addresses) * len(filter.EventSigs) + if len(filter.Topic2) > 0 { + expectedCount *= len(filter.Topic2) + } + if len(filter.Topic3) > 0 { + expectedCount *= len(filter.Topic3) + } + if len(filter.Topic4) > 0 { + expectedCount *= len(filter.Topic4) + } + assert.Equal(t, count, expectedCount) + }) + } + + // Make sure they all come back the same when we reload them + t.Run("Load filters", func(t *testing.T) { + loadedFilters, err := orm.LoadFilters() + require.NoError(t, err) + for _, filter := range filters { + loadedFilter, ok := loadedFilters[filter.Name] + require.True(t, ok, `Failed to reload filter "%s"`, filter.Name) + assert.Equal(t, filter, loadedFilter) + } + }) +} + func insertLogsTopicValueRange(t *testing.T, chainID *big.Int, o *logpoller.DbORM, addr common.Address, blockNumber int, eventSig common.Hash, start, stop int) { var lgs []logpoller.Log for i := start; i <= stop; i++ { diff --git a/core/chains/evm/logpoller/query.go b/core/chains/evm/logpoller/query.go index a37b15b2b2d..d8112459743 100644 --- a/core/chains/evm/logpoller/query.go +++ b/core/chains/evm/logpoller/query.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/lib/pq" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" ) @@ -54,6 +55,16 @@ func (q *queryArgs) withEventSigArray(eventSigs []common.Hash) *queryArgs { return q.withCustomArg("event_sig_array", concatBytes(eventSigs)) } +func (q *queryArgs) withTopicArray(topicValues types.HashArray, topicNum uint64) *queryArgs { + return q.withCustomArg(fmt.Sprintf("topic%d", topicNum), concatBytes(topicValues)) +} + +func (q *queryArgs) withTopicArrays(topic2Vals types.HashArray, topic3Vals types.HashArray, topic4Vals types.HashArray) *queryArgs { + return q.withTopicArray(topic2Vals, 2). + withTopicArray(topic3Vals, 3). + withTopicArray(topic4Vals, 4) +} + func (q *queryArgs) withAddress(address common.Address) *queryArgs { return q.withCustomArg("address", address) } @@ -127,6 +138,18 @@ func (q *queryArgs) withTxHash(hash common.Hash) *queryArgs { return q.withCustomHashArg("tx_hash", hash) } +func (q *queryArgs) withRetention(retention time.Duration) *queryArgs { + return q.withCustomArg("retention", retention) +} + +func (q *queryArgs) withLogsPerBlock(logsPerBlock uint64) *queryArgs { + return q.withCustomArg("logs_per_block", logsPerBlock) +} + +func (q *queryArgs) withMaxLogsKept(maxLogsKept uint64) *queryArgs { + return q.withCustomArg("max_logs_kept", maxLogsKept) +} + func (q *queryArgs) withCustomHashArg(name string, arg common.Hash) *queryArgs { return q.withCustomArg(name, arg.Bytes()) } diff --git a/core/chains/evm/types/types.go b/core/chains/evm/types/types.go index 987fd987d3f..c3ad584ebbd 100644 --- a/core/chains/evm/types/types.go +++ b/core/chains/evm/types/types.go @@ -332,7 +332,11 @@ func (a *AddressArray) Scan(src interface{}) error { if err != nil { return errors.Wrap(err, "Expected BYTEA[] column for AddressArray") } - if baArray.Status != pgtype.Present || len(baArray.Dimensions) > 1 { + if baArray.Status != pgtype.Present { + *a = nil + return nil + } + if len(baArray.Dimensions) > 1 { return errors.Errorf("Expected AddressArray to be 1-dimensional. Dimensions = %v", baArray.Dimensions) } @@ -359,14 +363,18 @@ func (h *HashArray) Scan(src interface{}) error { if err != nil { return errors.Wrap(err, "Expected BYTEA[] column for HashArray") } - if baArray.Status != pgtype.Present || len(baArray.Dimensions) > 1 { + if baArray.Status != pgtype.Present { + *h = nil + return nil + } + if len(baArray.Dimensions) > 1 { return errors.Errorf("Expected HashArray to be 1-dimensional. Dimensions = %v", baArray.Dimensions) } for i, ba := range baArray.Elements { hash := common.Hash{} if ba.Status != pgtype.Present { - return errors.Errorf("Expected all addresses in HashArray to be non-NULL. Got HashArray[%d] = NULL", i) + return errors.Errorf("Expected all hashes in HashArray to be non-NULL. Got HashArray[%d] = NULL", i) } err = hash.Scan(ba.Bytes) if err != nil { diff --git a/core/services/pg/q.go b/core/services/pg/q.go index ba2627fa745..49a18817de9 100644 --- a/core/services/pg/q.go +++ b/core/services/pg/q.go @@ -296,14 +296,25 @@ func sprintQ(query string, args []interface{}) string { case common.Hash: pairs = append(pairs, fmt.Sprintf("$%d", i+1), fmt.Sprintf("'\\x%x'", v.Bytes())) case pq.ByteaArray: + pairs = append(pairs, fmt.Sprintf("$%d", i+1)) + if v == nil { + pairs = append(pairs, "NULL") + continue + } + if len(v) == 0 { + pairs = append(pairs, "ARRAY[]") + continue + } var s strings.Builder - fmt.Fprintf(&s, "('\\x%x'", v[0]) + fmt.Fprintf(&s, "ARRAY['\\x%x'", v[0]) for j := 1; j < len(v); j++ { fmt.Fprintf(&s, ",'\\x%x'", v[j]) } - pairs = append(pairs, fmt.Sprintf("$%d", i+1), fmt.Sprintf("%s)", s.String())) + pairs = append(pairs, fmt.Sprintf("%s]", s.String())) + case string: + pairs = append(pairs, fmt.Sprintf("$%d", i+1), fmt.Sprintf("'%s'", v)) default: - pairs = append(pairs, fmt.Sprintf("$%d", i+1), fmt.Sprintf("%v", arg)) + pairs = append(pairs, fmt.Sprintf("$%d", i+1), fmt.Sprintf("%v", v)) } } replacer := strings.NewReplacer(pairs...) diff --git a/core/services/pg/q_test.go b/core/services/pg/q_test.go index 7692fb792bd..6e59e499887 100644 --- a/core/services/pg/q_test.go +++ b/core/services/pg/q_test.go @@ -21,27 +21,27 @@ func Test_sprintQ(t *testing.T) { {"one", "SELECT $1 FROM table;", []interface{}{"foo"}, - "SELECT foo FROM table;"}, + "SELECT 'foo' FROM table;"}, {"two", "SELECT $1 FROM table WHERE bar = $2;", []interface{}{"foo", 1}, - "SELECT foo FROM table WHERE bar = 1;"}, + "SELECT 'foo' FROM table WHERE bar = 1;"}, {"limit", "SELECT $1 FROM table LIMIT $2;", []interface{}{"foo", Limit(10)}, - "SELECT foo FROM table LIMIT 10;"}, + "SELECT 'foo' FROM table LIMIT 10;"}, {"limit-all", "SELECT $1 FROM table LIMIT $2;", []interface{}{"foo", Limit(-1)}, - "SELECT foo FROM table LIMIT NULL;"}, + "SELECT 'foo' FROM table LIMIT NULL;"}, {"bytea", "SELECT $1 FROM table WHERE b = $2;", []interface{}{"foo", []byte{0x0a}}, - "SELECT foo FROM table WHERE b = '\\x0a';"}, + "SELECT 'foo' FROM table WHERE b = '\\x0a';"}, {"bytea[]", "SELECT $1 FROM table WHERE b = $2;", []interface{}{"foo", pq.ByteaArray([][]byte{{0xa}, {0xb}})}, - "SELECT foo FROM table WHERE b = ('\\x0a','\\x0b');"}, + "SELECT 'foo' FROM table WHERE b = ARRAY['\\x0a','\\x0b'];"}, } { t.Run(tt.name, func(t *testing.T) { got := sprintQ(tt.query, tt.args) diff --git a/core/store/migrate/migrations/0225_log_poller_filters_add_topics_logs_per_block.sql b/core/store/migrate/migrations/0225_log_poller_filters_add_topics_logs_per_block.sql new file mode 100644 index 00000000000..77e3d5fbd51 --- /dev/null +++ b/core/store/migrate/migrations/0225_log_poller_filters_add_topics_logs_per_block.sql @@ -0,0 +1,29 @@ +-- +goose Up + +ALTER TABLE evm.log_poller_filters + ADD COLUMN topic2 BYTEA CHECK (octet_length(topic2) = 32), + ADD COLUMN topic3 BYTEA CHECK (octet_length(topic3) = 32), + ADD COLUMN topic4 BYTEA CHECK (octet_length(topic4) = 32), + ADD COLUMN max_logs_kept BIGINT, + ADD COLUMN logs_per_block BIGINT; + +-- Ordinary UNIQUE CONSTRAINT can't work for topics because they can be NULL. Any row with any column being NULL automatically satisfies the unique constraint (NULL != NULL) +-- Using a hash of all the columns treats NULL's as the same as any other field. If we ever get to a point where we can require postgresql >= 15 then this can +-- be fixed by using UNIQUE CONSTRAINT NULLS NOT DISTINCT which treats NULL's as if they were ordinary values (NULL == NULL) +CREATE UNIQUE INDEX evm_log_poller_filters_name_chain_address_event_topics_key ON evm.log_poller_filters (hash_record_extended((name, evm_chain_id, address, event, topic2, topic3, topic4), 0)); + +ALTER TABLE evm.log_poller_filters + DROP CONSTRAINT evm_log_poller_filters_name_evm_chain_id_address_event_key; + +-- +goose Down + +ALTER TABLE evm.log_poller_filters + ADD CONSTRAINT evm_log_poller_filters_name_evm_chain_id_address_event_key UNIQUE (name, evm_chain_id, address, event); +DROP INDEX IF EXISTS evm_log_poller_filters_name_chain_address_event_topics_key; + +ALTER TABLE evm.log_poller_filters + DROP COLUMN topic2, + DROP COLUMN topic3, + DROP COLUMN topic4, + DROP COLUMN max_logs_kept, + DROP COLUMN logs_per_block; From 18a004c55738198c8eb5fa828fd4fcacf86a4411 Mon Sep 17 00:00:00 2001 From: Jim W Date: Thu, 22 Feb 2024 15:42:31 -0500 Subject: [PATCH 16/24] change method signature so that it returns a transaction rather than replacing inplace (#12124) --- common/txmgr/broadcaster.go | 4 +-- common/txmgr/types/mocks/tx_store.go | 28 +++++++++++++++------ common/txmgr/types/tx_store.go | 2 +- core/chains/evm/txmgr/evm_tx_store.go | 9 +++++-- core/chains/evm/txmgr/evm_tx_store_test.go | 8 +++--- core/chains/evm/txmgr/mocks/evm_tx_store.go | 28 +++++++++++++++------ 6 files changed, 54 insertions(+), 25 deletions(-) diff --git a/common/txmgr/broadcaster.go b/common/txmgr/broadcaster.go index 9f2204f37e2..4c59a950ac1 100644 --- a/common/txmgr/broadcaster.go +++ b/common/txmgr/broadcaster.go @@ -707,8 +707,8 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) nextUnstartedTransactionWithSequence(fromAddress ADDR) (*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ctx, cancel := eb.chStop.NewCtx() defer cancel() - etx := &txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]{} - if err := eb.txStore.FindNextUnstartedTransactionFromAddress(ctx, etx, fromAddress, eb.chainID); err != nil { + etx, err := eb.txStore.FindNextUnstartedTransactionFromAddress(ctx, fromAddress, eb.chainID) + if err != nil { if errors.Is(err, sql.ErrNoRows) { // Finish. No more transactions left to process. Hoorah! return nil, nil diff --git a/common/txmgr/types/mocks/tx_store.go b/common/txmgr/types/mocks/tx_store.go index 353f398316d..814207d3986 100644 --- a/common/txmgr/types/mocks/tx_store.go +++ b/common/txmgr/types/mocks/tx_store.go @@ -280,22 +280,34 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindLatestS return r0, r1 } -// FindNextUnstartedTransactionFromAddress provides a mock function with given fields: ctx, etx, fromAddress, chainID -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) +// FindNextUnstartedTransactionFromAddress provides a mock function with given fields: ctx, fromAddress, chainID +func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindNextUnstartedTransactionFromAddress(ctx context.Context, fromAddress ADDR, chainID CHAIN_ID) (*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { + ret := _m.Called(ctx, 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) + 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 { + return rf(ctx, fromAddress, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, ADDR, CHAIN_ID) *txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]); ok { + r0 = rf(ctx, fromAddress, chainID) } else { - r0 = ret.Error(0) + if ret.Get(0) != nil { + r0 = ret.Get(0).(*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) + } } - return r0 + if rf, ok := ret.Get(1).(func(context.Context, ADDR, CHAIN_ID) error); ok { + r1 = rf(ctx, fromAddress, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 } // FindTransactionsConfirmedInBlockRange provides a mock function with given fields: ctx, highBlockNumber, lowBlockNumber, chainID diff --git a/common/txmgr/types/tx_store.go b/common/txmgr/types/tx_store.go index 742a1740033..f061f0ea628 100644 --- a/common/txmgr/types/tx_store.go +++ b/common/txmgr/types/tx_store.go @@ -79,7 +79,7 @@ type TransactionStore[ FindTxWithIdempotencyKey(ctx context.Context, idempotencyKey string, chainID CHAIN_ID) (tx *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) // Search for Tx using the fromAddress and sequence FindTxWithSequence(ctx context.Context, fromAddress ADDR, seq SEQ) (etx *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) - FindNextUnstartedTransactionFromAddress(ctx context.Context, etx *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], fromAddress ADDR, chainID CHAIN_ID) error + FindNextUnstartedTransactionFromAddress(ctx context.Context, fromAddress ADDR, chainID CHAIN_ID) (*Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) FindTransactionsConfirmedInBlockRange(ctx context.Context, highBlockNumber, lowBlockNumber int64, chainID CHAIN_ID) (etxs []*Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) FindEarliestUnconfirmedBroadcastTime(ctx context.Context, chainID CHAIN_ID) (null.Time, error) FindEarliestUnconfirmedTxAttemptBlock(ctx context.Context, chainID CHAIN_ID) (null.Int, error) diff --git a/core/chains/evm/txmgr/evm_tx_store.go b/core/chains/evm/txmgr/evm_tx_store.go index ae986acee27..364ee3f04d1 100644 --- a/core/chains/evm/txmgr/evm_tx_store.go +++ b/core/chains/evm/txmgr/evm_tx_store.go @@ -1545,15 +1545,20 @@ func (o *evmTxStore) SaveReplacementInProgressAttempt(ctx context.Context, oldAt } // Finds earliest saved transaction that has yet to be broadcast from the given address -func (o *evmTxStore) FindNextUnstartedTransactionFromAddress(ctx context.Context, etx *Tx, fromAddress common.Address, chainID *big.Int) error { +func (o *evmTxStore) FindNextUnstartedTransactionFromAddress(ctx context.Context, fromAddress common.Address, chainID *big.Int) (*Tx, error) { var cancel context.CancelFunc ctx, cancel = o.mergeContexts(ctx) defer cancel() qq := o.q.WithOpts(pg.WithParentCtx(ctx)) var dbEtx DbEthTx + etx := new(Tx) err := qq.Get(&dbEtx, `SELECT * FROM evm.txes WHERE from_address = $1 AND state = 'unstarted' AND evm_chain_id = $2 ORDER BY value ASC, created_at ASC, id ASC`, fromAddress, chainID.String()) dbEtx.ToTx(etx) - return pkgerrors.Wrap(err, "failed to FindNextUnstartedTransactionFromAddress") + if err != nil { + return nil, pkgerrors.Wrap(err, "failed to FindNextUnstartedTransactionFromAddress") + } + + return etx, nil } func (o *evmTxStore) UpdateTxFatalError(ctx context.Context, etx *Tx) error { diff --git a/core/chains/evm/txmgr/evm_tx_store_test.go b/core/chains/evm/txmgr/evm_tx_store_test.go index 35d684727d1..1e478e09b58 100644 --- a/core/chains/evm/txmgr/evm_tx_store_test.go +++ b/core/chains/evm/txmgr/evm_tx_store_test.go @@ -1261,16 +1261,16 @@ func TestORM_FindNextUnstartedTransactionFromAddress(t *testing.T) { t.Run("cannot find unstarted tx", func(t *testing.T) { mustInsertInProgressEthTxWithAttempt(t, txStore, 13, fromAddress) - resultEtx := new(txmgr.Tx) - err := txStore.FindNextUnstartedTransactionFromAddress(testutils.Context(t), resultEtx, fromAddress, ethClient.ConfiguredChainID()) + resultEtx, err := txStore.FindNextUnstartedTransactionFromAddress(testutils.Context(t), fromAddress, ethClient.ConfiguredChainID()) assert.ErrorIs(t, err, sql.ErrNoRows) + assert.Nil(t, resultEtx) }) t.Run("finds unstarted tx", func(t *testing.T) { mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID) - resultEtx := new(txmgr.Tx) - err := txStore.FindNextUnstartedTransactionFromAddress(testutils.Context(t), resultEtx, fromAddress, ethClient.ConfiguredChainID()) + resultEtx, err := txStore.FindNextUnstartedTransactionFromAddress(testutils.Context(t), fromAddress, ethClient.ConfiguredChainID()) require.NoError(t, err) + assert.NotNil(t, resultEtx) }) } diff --git a/core/chains/evm/txmgr/mocks/evm_tx_store.go b/core/chains/evm/txmgr/mocks/evm_tx_store.go index 9690bf9728d..9f1af016fea 100644 --- a/core/chains/evm/txmgr/mocks/evm_tx_store.go +++ b/core/chains/evm/txmgr/mocks/evm_tx_store.go @@ -283,22 +283,34 @@ func (_m *EvmTxStore) FindLatestSequence(ctx context.Context, fromAddress common return r0, r1 } -// FindNextUnstartedTransactionFromAddress provides a mock function with given fields: ctx, etx, fromAddress, chainID -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) +// FindNextUnstartedTransactionFromAddress provides a mock function with given fields: ctx, fromAddress, chainID +func (_m *EvmTxStore) FindNextUnstartedTransactionFromAddress(ctx context.Context, fromAddress common.Address, chainID *big.Int) (*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { + ret := _m.Called(ctx, 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) + 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 { + return rf(ctx, fromAddress, chainID) + } + 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]); ok { + r0 = rf(ctx, fromAddress, chainID) } else { - r0 = ret.Error(0) + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) + } } - return r0 + if rf, ok := ret.Get(1).(func(context.Context, common.Address, *big.Int) error); ok { + r1 = rf(ctx, fromAddress, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 } // FindTransactionsConfirmedInBlockRange provides a mock function with given fields: ctx, highBlockNumber, lowBlockNumber, chainID From 16f1b785ca6dfa81ddacfb871e92a1dfa9823040 Mon Sep 17 00:00:00 2001 From: Chris Cushman <104409744+vreff@users.noreply.github.com> Date: Thu, 22 Feb 2024 16:24:50 -0500 Subject: [PATCH 17/24] [VRF-887] Add NativePayment() to VRFCoordinator_2x_Interface and integ tests (#12133) --- core/services/vrf/v2/coordinator_v2x_interface.go | 1 + core/services/vrf/v2/integration_v2_test.go | 1 + 2 files changed, 2 insertions(+) diff --git a/core/services/vrf/v2/coordinator_v2x_interface.go b/core/services/vrf/v2/coordinator_v2x_interface.go index 05a9e5d8918..c99576d7558 100644 --- a/core/services/vrf/v2/coordinator_v2x_interface.go +++ b/core/services/vrf/v2/coordinator_v2x_interface.go @@ -656,6 +656,7 @@ type RandomWordsFulfilled interface { SubID() *big.Int Payment() *big.Int Raw() types.Log + NativePayment() bool } func NewV2RandomWordsFulfilled(event *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled) RandomWordsFulfilled { diff --git a/core/services/vrf/v2/integration_v2_test.go b/core/services/vrf/v2/integration_v2_test.go index 39acc3da3e5..5114015c008 100644 --- a/core/services/vrf/v2/integration_v2_test.go +++ b/core/services/vrf/v2/integration_v2_test.go @@ -751,6 +751,7 @@ func assertRandomWordsFulfilled( for filter.Next() { require.Equal(t, expectedSuccess, filter.Event().Success(), "fulfillment event success not correct, expected: %+v, actual: %+v", expectedSuccess, filter.Event().Success()) require.Equal(t, requestID, filter.Event().RequestID()) + require.Equal(t, nativePayment, filter.Event().NativePayment()) found = true rwfe = filter.Event() } From 5f212bbc8485fc1fb20076ef5df7842f7bbfc6c1 Mon Sep 17 00:00:00 2001 From: Mateusz Sekara Date: Fri, 23 Feb 2024 08:22:18 +0100 Subject: [PATCH 18/24] CCIP-1496 Moving pruning to a separate loop and respecting paging there (#12060) * Moving pruning to a separate loop and respecting paging there * Separate parameter to configure paging during logs pruning * Fixing specs * Config fixes * Post review fixes * Post review fixes * Tests * Switching to index instead of primary key, because of the performance difference * Minor performance improvement to blocks deletion * Minor fix * Minor fix --- core/chains/evm/config/chain_scoped.go | 4 ++ core/chains/evm/config/config.go | 2 + core/chains/evm/config/toml/config.go | 1 + core/chains/evm/config/toml/defaults.go | 4 ++ .../evm/config/toml/defaults/fallback.toml | 1 + .../evm/forwarders/forwarder_manager_test.go | 4 +- core/chains/evm/logpoller/helper_test.go | 3 +- core/chains/evm/logpoller/log_poller.go | 64 ++++++++++++++---- .../evm/logpoller/log_poller_internal_test.go | 16 ++--- core/chains/evm/logpoller/log_poller_test.go | 13 ++-- core/chains/evm/logpoller/observability.go | 28 ++++++-- core/chains/evm/logpoller/orm.go | 52 +++++++++++--- core/chains/evm/logpoller/orm_test.go | 67 +++++++++++++++++-- core/chains/evm/txmgr/txmgr_test.go | 2 +- core/chains/legacyevm/chain.go | 3 +- core/config/docs/chains-evm.toml | 3 + core/services/chainlink/config_test.go | 2 + .../chainlink/testdata/config-full.toml | 1 + .../config-multi-chain-effective.toml | 3 + ...annel_definition_cache_integration_test.go | 6 +- .../v21/logprovider/integration_test.go | 2 +- core/services/pg/q.go | 10 +++ core/services/pg/q_test.go | 30 +++++++++ .../promreporter/prom_reporter_test.go | 2 +- core/services/relay/evm/chain_reader_test.go | 2 +- core/services/relay/evm/config_poller_test.go | 3 +- .../relay/evm/functions/config_poller_test.go | 2 +- .../relay/evm/mercury/helpers_test.go | 2 +- .../vrf/v2/listener_v2_log_listener_test.go | 2 +- core/web/resolver/chain_test.go | 3 + core/web/resolver/testdata/config-full.toml | 1 + .../config-multi-chain-effective.toml | 3 + docs/CHANGELOG.md | 1 + docs/CONFIG.md | 58 ++++++++++++++++ .../disk-based-logging-disabled.txtar | 1 + .../validate/disk-based-logging-no-dir.txtar | 1 + .../node/validate/disk-based-logging.txtar | 1 + testdata/scripts/node/validate/invalid.txtar | 1 + testdata/scripts/node/validate/valid.txtar | 1 + 39 files changed, 345 insertions(+), 60 deletions(-) diff --git a/core/chains/evm/config/chain_scoped.go b/core/chains/evm/config/chain_scoped.go index c579da86c8c..69cc4c0a6ad 100644 --- a/core/chains/evm/config/chain_scoped.go +++ b/core/chains/evm/config/chain_scoped.go @@ -193,3 +193,7 @@ func (e *evmConfig) OperatorFactoryAddress() string { } return e.c.OperatorFactoryAddress.String() } + +func (e *evmConfig) LogPrunePageSize() uint32 { + return *e.c.LogPrunePageSize +} diff --git a/core/chains/evm/config/config.go b/core/chains/evm/config/config.go index 5b397ddd574..2cfc497f462 100644 --- a/core/chains/evm/config/config.go +++ b/core/chains/evm/config/config.go @@ -7,6 +7,7 @@ import ( gethcommon "github.com/ethereum/go-ethereum/common" commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" + commonconfig "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/config" @@ -36,6 +37,7 @@ type EVM interface { LogBackfillBatchSize() uint32 LogKeepBlocksDepth() uint32 LogPollInterval() time.Duration + LogPrunePageSize() uint32 MinContractPayment() *commonassets.Link MinIncomingConfirmations() uint32 NonceAutoSync() bool diff --git a/core/chains/evm/config/toml/config.go b/core/chains/evm/config/toml/config.go index 6ebf3ed0a94..0ffe3549613 100644 --- a/core/chains/evm/config/toml/config.go +++ b/core/chains/evm/config/toml/config.go @@ -352,6 +352,7 @@ type Chain struct { LogBackfillBatchSize *uint32 LogPollInterval *commonconfig.Duration LogKeepBlocksDepth *uint32 + LogPrunePageSize *uint32 MinIncomingConfirmations *uint32 MinContractPayment *commonassets.Link NonceAutoSync *bool diff --git a/core/chains/evm/config/toml/defaults.go b/core/chains/evm/config/toml/defaults.go index adb91b3a1bc..242373fd4af 100644 --- a/core/chains/evm/config/toml/defaults.go +++ b/core/chains/evm/config/toml/defaults.go @@ -9,6 +9,7 @@ import ( "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" ) @@ -136,6 +137,9 @@ func (c *Chain) SetFrom(f *Chain) { if v := f.LogKeepBlocksDepth; v != nil { c.LogKeepBlocksDepth = v } + if v := f.LogPrunePageSize; v != nil { + c.LogPrunePageSize = v + } if v := f.MinIncomingConfirmations; v != nil { c.MinIncomingConfirmations = v } diff --git a/core/chains/evm/config/toml/defaults/fallback.toml b/core/chains/evm/config/toml/defaults/fallback.toml index 94fb83849bf..7b369142133 100644 --- a/core/chains/evm/config/toml/defaults/fallback.toml +++ b/core/chains/evm/config/toml/defaults/fallback.toml @@ -6,6 +6,7 @@ FinalityTagEnabled = false LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinContractPayment = '.00001 link' MinIncomingConfirmations = 3 NonceAutoSync = true diff --git a/core/chains/evm/forwarders/forwarder_manager_test.go b/core/chains/evm/forwarders/forwarder_manager_test.go index 5ef150aa5c3..4480e533525 100644 --- a/core/chains/evm/forwarders/forwarder_manager_test.go +++ b/core/chains/evm/forwarders/forwarder_manager_test.go @@ -60,7 +60,7 @@ func TestFwdMgr_MaybeForwardTransaction(t *testing.T) { t.Log(authorized) evmClient := client.NewSimulatedBackendClient(t, ec, testutils.FixtureChainID) - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr, pgtest.NewQConfig(true)), evmClient, lggr, 100*time.Millisecond, false, 2, 3, 2, 1000) + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr, pgtest.NewQConfig(true)), evmClient, lggr, 100*time.Millisecond, false, 2, 3, 2, 1000, 0) fwdMgr := forwarders.NewFwdMgr(db, evmClient, lp, lggr, evmcfg.EVM(), evmcfg.Database()) fwdMgr.ORM = forwarders.NewORM(db, logger.Test(t), cfg.Database()) @@ -113,7 +113,7 @@ func TestFwdMgr_AccountUnauthorizedToForward_SkipsForwarding(t *testing.T) { ec.Commit() evmClient := client.NewSimulatedBackendClient(t, ec, testutils.FixtureChainID) - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr, pgtest.NewQConfig(true)), evmClient, lggr, 100*time.Millisecond, false, 2, 3, 2, 1000) + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr, pgtest.NewQConfig(true)), evmClient, lggr, 100*time.Millisecond, false, 2, 3, 2, 1000, 0) fwdMgr := forwarders.NewFwdMgr(db, evmClient, lp, lggr, evmcfg.EVM(), evmcfg.Database()) fwdMgr.ORM = forwarders.NewORM(db, logger.Test(t), cfg.Database()) diff --git a/core/chains/evm/logpoller/helper_test.go b/core/chains/evm/logpoller/helper_test.go index 9e48690a249..cb0fbd247fc 100644 --- a/core/chains/evm/logpoller/helper_test.go +++ b/core/chains/evm/logpoller/helper_test.go @@ -20,6 +20,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "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/generated/log_emitter" @@ -66,7 +67,7 @@ func SetupTH(t testing.TB, useFinalityTag bool, finalityDepth, backfillBatchSize // Mark genesis block as finalized to avoid any nulls in the tests head := esc.Backend().Blockchain().CurrentHeader() esc.Backend().Blockchain().SetFinalized(head) - lp := logpoller.NewLogPoller(o, esc, lggr, 1*time.Hour, useFinalityTag, finalityDepth, backfillBatchSize, rpcBatchSize, keepFinalizedBlocksDepth) + lp := logpoller.NewLogPoller(o, esc, lggr, 1*time.Hour, useFinalityTag, finalityDepth, backfillBatchSize, rpcBatchSize, keepFinalizedBlocksDepth, 0) emitterAddress1, _, emitter1, err := log_emitter.DeployLogEmitter(owner, ec) require.NoError(t, err) emitterAddress2, _, emitter2, err := log_emitter.DeployLogEmitter(owner, ec) diff --git a/core/chains/evm/logpoller/log_poller.go b/core/chains/evm/logpoller/log_poller.go index a2c35bec59f..ba617a2178b 100644 --- a/core/chains/evm/logpoller/log_poller.go +++ b/core/chains/evm/logpoller/log_poller.go @@ -76,7 +76,7 @@ type LogPollerTest interface { BackupPollAndSaveLogs(ctx context.Context, backupPollerBlockDelay int64) Filter(from, to *big.Int, bh *common.Hash) ethereum.FilterQuery GetReplayFromBlock(ctx context.Context, requested int64) (int64, error) - PruneOldBlocks(ctx context.Context) error + PruneOldBlocks(ctx context.Context) (bool, error) } type Client interface { @@ -106,6 +106,7 @@ type logPoller struct { backfillBatchSize int64 // batch size to use when backfilling finalized logs rpcBatchSize int64 // batch size to use for fallback RPC calls made in GetBlocks backupPollerNextBlock int64 + logPrunePageSize int64 filterMu sync.RWMutex filters map[string]Filter @@ -130,8 +131,7 @@ type logPoller struct { // // How fast that can be done depends largely on network speed and DB, but even for the fastest // support chain, polygon, which has 2s block times, we need RPCs roughly with <= 500ms latency -func NewLogPoller(orm ORM, ec Client, lggr logger.Logger, pollPeriod time.Duration, - useFinalityTag bool, finalityDepth int64, backfillBatchSize int64, rpcBatchSize int64, keepFinalizedBlocksDepth int64) *logPoller { +func NewLogPoller(orm ORM, ec Client, lggr logger.Logger, pollPeriod time.Duration, useFinalityTag bool, finalityDepth int64, backfillBatchSize int64, rpcBatchSize int64, keepFinalizedBlocksDepth int64, logsPrunePageSize int64) *logPoller { ctx, cancel := context.WithCancel(context.Background()) return &logPoller{ ctx: ctx, @@ -147,6 +147,7 @@ func NewLogPoller(orm ORM, ec Client, lggr logger.Logger, pollPeriod time.Durati backfillBatchSize: backfillBatchSize, rpcBatchSize: rpcBatchSize, keepFinalizedBlocksDepth: keepFinalizedBlocksDepth, + logPrunePageSize: logsPrunePageSize, filters: make(map[string]Filter), filterDirty: true, // Always build Filter on first call to cache an empty filter if nothing registered yet. } @@ -392,8 +393,9 @@ func (lp *logPoller) ReplayAsync(fromBlock int64) { func (lp *logPoller) Start(context.Context) error { return lp.StartOnce("LogPoller", func() error { - lp.wg.Add(1) + lp.wg.Add(2) go lp.run() + go lp.backgroundWorkerRun() return nil }) } @@ -439,8 +441,6 @@ func (lp *logPoller) run() { logPollTick := time.After(0) // stagger these somewhat, so they don't all run back-to-back backupLogPollTick := time.After(100 * time.Millisecond) - blockPruneTick := time.After(3 * time.Second) - logPruneTick := time.After(5 * time.Second) filtersLoaded := false loadFilters := func() error { @@ -545,15 +545,38 @@ func (lp *logPoller) run() { continue } lp.BackupPollAndSaveLogs(lp.ctx, backupPollerBlockDelay) + } + } +} + +func (lp *logPoller) backgroundWorkerRun() { + defer lp.wg.Done() + + // Avoid putting too much pressure on the database by staggering the pruning of old blocks and logs. + // Usually, node after restart will have some work to boot the plugins and other services. + // Deferring first prune by minutes reduces risk of putting too much pressure on the database. + blockPruneTick := time.After(5 * time.Minute) + logPruneTick := time.After(10 * time.Minute) + + for { + select { + case <-lp.ctx.Done(): + return case <-blockPruneTick: blockPruneTick = time.After(utils.WithJitter(lp.pollPeriod * 1000)) - if err := lp.PruneOldBlocks(lp.ctx); err != nil { + if allRemoved, err := lp.PruneOldBlocks(lp.ctx); err != nil { lp.lggr.Errorw("Unable to prune old blocks", "err", err) + } else if !allRemoved { + // Tick faster when cleanup can't keep up with the pace of new blocks + blockPruneTick = time.After(utils.WithJitter(lp.pollPeriod * 100)) } case <-logPruneTick: logPruneTick = time.After(utils.WithJitter(lp.pollPeriod * 2401)) // = 7^5 avoids common factors with 1000 - if err := lp.orm.DeleteExpiredLogs(pg.WithParentCtx(lp.ctx)); err != nil { - lp.lggr.Error(err) + if allRemoved, err := lp.PruneExpiredLogs(lp.ctx); err != nil { + lp.lggr.Errorw("Unable to prune expired logs", "err", err) + } else if !allRemoved { + // Tick faster when cleanup can't keep up with the pace of new logs + logPruneTick = time.After(utils.WithJitter(lp.pollPeriod * 241)) } } } @@ -933,22 +956,35 @@ func (lp *logPoller) findBlockAfterLCA(ctx context.Context, current *evmtypes.He } // PruneOldBlocks removes blocks that are > lp.keepFinalizedBlocksDepth behind the latest finalized block. -func (lp *logPoller) PruneOldBlocks(ctx context.Context) error { +// Returns whether all blocks eligible for pruning were removed. If logPrunePageSize is set to 0, it will always return true. +func (lp *logPoller) PruneOldBlocks(ctx context.Context) (bool, error) { latestBlock, err := lp.orm.SelectLatestBlock(pg.WithParentCtx(ctx)) if err != nil { - return err + return false, err } if latestBlock == nil { // No blocks saved yet. - return nil + return true, nil } if latestBlock.FinalizedBlockNumber <= lp.keepFinalizedBlocksDepth { // No-op, keep all blocks - return nil + return true, nil } // 1-2-3-4-5(finalized)-6-7(latest), keepFinalizedBlocksDepth=3 // Remove <= 2 - return lp.orm.DeleteBlocksBefore(latestBlock.FinalizedBlockNumber-lp.keepFinalizedBlocksDepth, pg.WithParentCtx(ctx)) + rowsRemoved, err := lp.orm.DeleteBlocksBefore( + latestBlock.FinalizedBlockNumber-lp.keepFinalizedBlocksDepth, + lp.logPrunePageSize, + pg.WithParentCtx(ctx), + ) + return lp.logPrunePageSize == 0 || rowsRemoved < lp.logPrunePageSize, err +} + +// PruneExpiredLogs logs that are older than their retention period defined in Filter. +// Returns whether all logs eligible for pruning were removed. If logPrunePageSize is set to 0, it will always return true. +func (lp *logPoller) PruneExpiredLogs(ctx context.Context) (bool, error) { + rowsRemoved, err := lp.orm.DeleteExpiredLogs(lp.logPrunePageSize, pg.WithParentCtx(ctx)) + return lp.logPrunePageSize == 0 || rowsRemoved < lp.logPrunePageSize, err } // Logs returns logs matching topics and address (exactly) in the given block range, diff --git a/core/chains/evm/logpoller/log_poller_internal_test.go b/core/chains/evm/logpoller/log_poller_internal_test.go index 899efebe42c..124c16d26f3 100644 --- a/core/chains/evm/logpoller/log_poller_internal_test.go +++ b/core/chains/evm/logpoller/log_poller_internal_test.go @@ -64,7 +64,7 @@ func TestLogPoller_RegisterFilter(t *testing.T) { orm := NewORM(chainID, db, lggr, pgtest.NewQConfig(true)) // Set up a test chain with a log emitting contract deployed. - lp := NewLogPoller(orm, nil, lggr, time.Hour, false, 1, 1, 2, 1000) + lp := NewLogPoller(orm, nil, lggr, time.Hour, false, 1, 1, 2, 1000, 0) // We expect a zero Filter if nothing registered yet. f := lp.Filter(nil, nil, nil) @@ -218,7 +218,7 @@ func TestLogPoller_BackupPollerStartup(t *testing.T) { ctx := testutils.Context(t) - lp := NewLogPoller(orm, ec, lggr, 1*time.Hour, false, 2, 3, 2, 1000) + lp := NewLogPoller(orm, ec, lggr, 1*time.Hour, false, 2, 3, 2, 1000, 0) lp.BackupPollAndSaveLogs(ctx, 100) assert.Equal(t, int64(0), lp.backupPollerNextBlock) assert.Equal(t, 1, observedLogs.FilterMessageSnippet("ran before first successful log poller run").Len()) @@ -258,7 +258,7 @@ func TestLogPoller_Replay(t *testing.T) { ec.On("HeadByNumber", mock.Anything, mock.Anything).Return(&head, nil) ec.On("FilterLogs", mock.Anything, mock.Anything).Return([]types.Log{log1}, nil).Once() ec.On("ConfiguredChainID").Return(chainID, nil) - lp := NewLogPoller(orm, ec, lggr, time.Hour, false, 3, 3, 3, 20) + lp := NewLogPoller(orm, ec, lggr, time.Hour, false, 3, 3, 3, 20, 0) // process 1 log in block 3 lp.PollAndSaveLogs(testutils.Context(t), 4) @@ -446,7 +446,7 @@ func Test_latestBlockAndFinalityDepth(t *testing.T) { ec := evmclimocks.NewClient(t) ec.On("HeadByNumber", mock.Anything, mock.Anything).Return(&head, nil) - lp := NewLogPoller(orm, ec, lggr, time.Hour, false, finalityDepth, 3, 3, 20) + lp := NewLogPoller(orm, ec, lggr, time.Hour, false, finalityDepth, 3, 3, 20, 0) latestBlock, lastFinalizedBlockNumber, err := lp.latestBlocks(testutils.Context(t)) require.NoError(t, err) require.Equal(t, latestBlock.Number, head.Number) @@ -470,7 +470,7 @@ func Test_latestBlockAndFinalityDepth(t *testing.T) { *(elems[1].Result.(*evmtypes.Head)) = evmtypes.Head{Number: expectedLastFinalizedBlockNumber, Hash: utils.RandomBytes32()} }) - lp := NewLogPoller(orm, ec, lggr, time.Hour, true, 3, 3, 3, 20) + lp := NewLogPoller(orm, ec, lggr, time.Hour, true, 3, 3, 3, 20, 0) latestBlock, lastFinalizedBlockNumber, err := lp.latestBlocks(testutils.Context(t)) require.NoError(t, err) @@ -488,7 +488,7 @@ func Test_latestBlockAndFinalityDepth(t *testing.T) { elems[1].Error = fmt.Errorf("some error") }) - lp := NewLogPoller(orm, ec, lggr, time.Hour, true, 3, 3, 3, 20) + lp := NewLogPoller(orm, ec, lggr, time.Hour, true, 3, 3, 3, 20, 0) _, _, err := lp.latestBlocks(testutils.Context(t)) require.Error(t, err) }) @@ -497,7 +497,7 @@ func Test_latestBlockAndFinalityDepth(t *testing.T) { ec := evmclimocks.NewClient(t) ec.On("BatchCallContext", mock.Anything, mock.Anything).Return(fmt.Errorf("some error")) - lp := NewLogPoller(orm, ec, lggr, time.Hour, true, 3, 3, 3, 20) + lp := NewLogPoller(orm, ec, lggr, time.Hour, true, 3, 3, 3, 20, 0) _, _, err := lp.latestBlocks(testutils.Context(t)) require.Error(t, err) }) @@ -506,7 +506,7 @@ func Test_latestBlockAndFinalityDepth(t *testing.T) { func benchmarkFilter(b *testing.B, nFilters, nAddresses, nEvents int) { lggr := logger.Test(b) - lp := NewLogPoller(nil, nil, lggr, 1*time.Hour, false, 2, 3, 2, 1000) + lp := NewLogPoller(nil, nil, lggr, 1*time.Hour, false, 2, 3, 2, 1000, 0) for i := 0; i < nFilters; i++ { var addresses []common.Address var events []common.Hash diff --git a/core/chains/evm/logpoller/log_poller_test.go b/core/chains/evm/logpoller/log_poller_test.go index ab02e783a28..5b894b8a19a 100644 --- a/core/chains/evm/logpoller/log_poller_test.go +++ b/core/chains/evm/logpoller/log_poller_test.go @@ -676,7 +676,7 @@ func TestLogPoller_SynchronizedWithGeth(t *testing.T) { }, 10e6) _, _, emitter1, err := log_emitter.DeployLogEmitter(owner, ec) require.NoError(t, err) - lp := logpoller.NewLogPoller(orm, client.NewSimulatedBackendClient(t, ec, chainID), lggr, 15*time.Second, false, int64(finalityDepth), 3, 2, 1000) + lp := logpoller.NewLogPoller(orm, client.NewSimulatedBackendClient(t, ec, chainID), lggr, 15*time.Second, false, int64(finalityDepth), 3, 2, 1000, 0) for i := 0; i < finalityDepth; i++ { // Have enough blocks that we could reorg the full finalityDepth-1. ec.Commit() } @@ -1306,7 +1306,7 @@ func TestLogPoller_DBErrorHandling(t *testing.T) { ec.Commit() ec.Commit() - lp := logpoller.NewLogPoller(o, client.NewSimulatedBackendClient(t, ec, chainID2), lggr, 1*time.Hour, false, 2, 3, 2, 1000) + lp := logpoller.NewLogPoller(o, client.NewSimulatedBackendClient(t, ec, chainID2), lggr, 1*time.Hour, false, 2, 3, 2, 1000, 0) err = lp.Replay(ctx, 5) // block number too high require.ErrorContains(t, err, "Invalid replay block number") @@ -1354,7 +1354,7 @@ func TestTooManyLogResults(t *testing.T) { chainID := testutils.NewRandomEVMChainID() db := pgtest.NewSqlxDB(t) o := logpoller.NewORM(chainID, db, lggr, pgtest.NewQConfig(true)) - lp := logpoller.NewLogPoller(o, ec, lggr, 1*time.Hour, false, 2, 20, 10, 1000) + lp := logpoller.NewLogPoller(o, ec, lggr, 1*time.Hour, false, 2, 20, 10, 1000, 0) expected := []int64{10, 5, 2, 1} clientErr := client.JsonError{ @@ -1671,11 +1671,14 @@ func Test_PruneOldBlocks(t *testing.T) { } if tt.wantErr { - require.Error(t, th.LogPoller.PruneOldBlocks(ctx)) + _, err := th.LogPoller.PruneOldBlocks(ctx) + require.Error(t, err) return } - require.NoError(t, th.LogPoller.PruneOldBlocks(ctx)) + allDeleted, err := th.LogPoller.PruneOldBlocks(ctx) + require.NoError(t, err) + assert.True(t, allDeleted) blocks, err := th.ORM.GetBlocksRange(0, math.MaxInt64, pg.WithParentCtx(ctx)) require.NoError(t, err) assert.Len(t, blocks, tt.blocksLeft) diff --git a/core/chains/evm/logpoller/observability.go b/core/chains/evm/logpoller/observability.go index a7a0d3c03d5..abb3246585b 100644 --- a/core/chains/evm/logpoller/observability.go +++ b/core/chains/evm/logpoller/observability.go @@ -122,9 +122,9 @@ func (o *ObservedORM) DeleteFilter(name string, qopts ...pg.QOpt) error { }) } -func (o *ObservedORM) DeleteBlocksBefore(end int64, qopts ...pg.QOpt) error { - return withObservedExec(o, "DeleteBlocksBefore", del, func() error { - return o.ORM.DeleteBlocksBefore(end, qopts...) +func (o *ObservedORM) DeleteBlocksBefore(end int64, limit int64, qopts ...pg.QOpt) (int64, error) { + return withObservedExecAndRowsAffected(o, "DeleteBlocksBefore", del, func() (int64, error) { + return o.ORM.DeleteBlocksBefore(end, limit, qopts...) }) } @@ -134,9 +134,9 @@ func (o *ObservedORM) DeleteLogsAndBlocksAfter(start int64, qopts ...pg.QOpt) er }) } -func (o *ObservedORM) DeleteExpiredLogs(qopts ...pg.QOpt) error { - return withObservedExec(o, "DeleteExpiredLogs", del, func() error { - return o.ORM.DeleteExpiredLogs(qopts...) +func (o *ObservedORM) DeleteExpiredLogs(limit int64, qopts ...pg.QOpt) (int64, error) { + return withObservedExecAndRowsAffected(o, "DeleteExpiredLogs", del, func() (int64, error) { + return o.ORM.DeleteExpiredLogs(limit, qopts...) }) } @@ -264,6 +264,22 @@ func withObservedQueryAndResults[T any](o *ObservedORM, queryName string, query return results, err } +func withObservedExecAndRowsAffected(o *ObservedORM, queryName string, queryType queryType, exec func() (int64, error)) (int64, error) { + queryStarted := time.Now() + rowsAffected, err := exec() + o.queryDuration. + WithLabelValues(o.chainId, queryName, string(queryType)). + Observe(float64(time.Since(queryStarted))) + + if err != nil { + o.datasetSize. + WithLabelValues(o.chainId, queryName, string(queryType)). + Set(float64(rowsAffected)) + } + + return rowsAffected, err +} + func withObservedQuery[T any](o *ObservedORM, queryName string, query func() (T, error)) (T, error) { queryStarted := time.Now() defer func() { diff --git a/core/chains/evm/logpoller/orm.go b/core/chains/evm/logpoller/orm.go index fe814e8a43c..c0e870870e6 100644 --- a/core/chains/evm/logpoller/orm.go +++ b/core/chains/evm/logpoller/orm.go @@ -30,9 +30,9 @@ type ORM interface { LoadFilters(qopts ...pg.QOpt) (map[string]Filter, error) DeleteFilter(name string, qopts ...pg.QOpt) error - DeleteBlocksBefore(end int64, qopts ...pg.QOpt) error + DeleteBlocksBefore(end int64, limit int64, qopts ...pg.QOpt) (int64, error) DeleteLogsAndBlocksAfter(start int64, qopts ...pg.QOpt) error - DeleteExpiredLogs(qopts ...pg.QOpt) error + DeleteExpiredLogs(limit int64, qopts ...pg.QOpt) (int64, error) GetBlocksRange(start int64, end int64, qopts ...pg.QOpt) ([]LogPollerBlock, error) SelectBlockByNumber(blockNumber int64, qopts ...pg.QOpt) (*LogPollerBlock, error) @@ -212,11 +212,28 @@ func (o *DbORM) SelectLatestLogByEventSigWithConfs(eventSig common.Hash, address return &l, nil } -// DeleteBlocksBefore delete all blocks before and including end. -func (o *DbORM) DeleteBlocksBefore(end int64, qopts ...pg.QOpt) error { +// DeleteBlocksBefore delete blocks before and including end. When limit is set, it will delete at most limit blocks. +// Otherwise, it will delete all blocks at once. +func (o *DbORM) DeleteBlocksBefore(end int64, limit int64, qopts ...pg.QOpt) (int64, 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, ubig.New(o.chainID)) - return err + if limit > 0 { + return q.ExecQWithRowsAffected( + `DELETE FROM evm.log_poller_blocks + WHERE block_number IN ( + SELECT block_number FROM evm.log_poller_blocks + WHERE block_number <= $1 + AND evm_chain_id = $2 + LIMIT $3 + ) + AND evm_chain_id = $2`, + end, ubig.New(o.chainID), limit, + ) + } + return q.ExecQWithRowsAffected( + `DELETE FROM evm.log_poller_blocks + WHERE block_number <= $1 AND evm_chain_id = $2`, + end, ubig.New(o.chainID), + ) } func (o *DbORM) DeleteLogsAndBlocksAfter(start int64, qopts ...pg.QOpt) error { @@ -263,11 +280,30 @@ type Exp struct { ShouldDelete bool } -func (o *DbORM) DeleteExpiredLogs(qopts ...pg.QOpt) error { +func (o *DbORM) DeleteExpiredLogs(limit int64, qopts ...pg.QOpt) (int64, error) { qopts = append(qopts, pg.WithLongQueryTimeout()) q := o.q.WithOpts(qopts...) - return q.ExecQ(`WITH r AS + if limit > 0 { + return q.ExecQWithRowsAffected(` + DELETE FROM evm.logs + WHERE (evm_chain_id, address, event_sig, block_number) IN ( + SELECT l.evm_chain_id, l.address, l.event_sig, l.block_number + FROM evm.logs l + INNER JOIN ( + SELECT address, event, MAX(retention) AS retention + FROM evm.log_poller_filters + WHERE evm_chain_id = $1 + GROUP BY evm_chain_id, address, event + HAVING NOT 0 = ANY(ARRAY_AGG(retention)) + ) r ON l.evm_chain_id = $1 AND l.address = r.address AND l.event_sig = r.event + AND l.block_timestamp <= STATEMENT_TIMESTAMP() - (r.retention / 10^9 * interval '1 second') + LIMIT $2 + )`, + ubig.New(o.chainID), limit) + } + + return q.ExecQWithRowsAffected(`WITH r AS ( SELECT address, event, MAX(retention) AS retention FROM evm.log_poller_filters WHERE evm_chain_id=$1 GROUP BY evm_chain_id,address, event HAVING NOT 0 = ANY(ARRAY_AGG(retention)) diff --git a/core/chains/evm/logpoller/orm_test.go b/core/chains/evm/logpoller/orm_test.go index 9824d0e9426..8f89a237fd4 100644 --- a/core/chains/evm/logpoller/orm_test.go +++ b/core/chains/evm/logpoller/orm_test.go @@ -433,8 +433,9 @@ func TestORM(t *testing.T) { // Delete expired logs time.Sleep(2 * time.Millisecond) // just in case we haven't reached the end of the 1ms retention period - err = o1.DeleteExpiredLogs(pg.WithParentCtx(testutils.Context(t))) + deleted, err := o1.DeleteExpiredLogs(0, pg.WithParentCtx(testutils.Context(t))) require.NoError(t, err) + assert.Equal(t, int64(1), deleted) logs, err = o1.SelectLogsByBlockRange(1, latest.BlockNumber) require.NoError(t, err) // The only log which should be deleted is the one which matches filter1 (ret=1ms) but not filter12 (ret=1 hour) @@ -851,9 +852,11 @@ func TestORM_DeleteBlocksBefore(t *testing.T) { o1 := th.ORM require.NoError(t, o1.InsertBlock(common.HexToHash("0x1234"), 1, time.Now(), 0)) require.NoError(t, o1.InsertBlock(common.HexToHash("0x1235"), 2, time.Now(), 0)) - require.NoError(t, o1.DeleteBlocksBefore(1)) + deleted, err := o1.DeleteBlocksBefore(1, 0) + require.NoError(t, err) + assert.Equal(t, int64(1), deleted) // 1 should be gone. - _, err := o1.SelectBlockByNumber(1) + _, err = o1.SelectBlockByNumber(1) require.Equal(t, err, sql.ErrNoRows) b, err := o1.SelectBlockByNumber(2) require.NoError(t, err) @@ -861,7 +864,9 @@ func TestORM_DeleteBlocksBefore(t *testing.T) { // Clear multiple require.NoError(t, o1.InsertBlock(common.HexToHash("0x1236"), 3, time.Now(), 0)) require.NoError(t, o1.InsertBlock(common.HexToHash("0x1237"), 4, time.Now(), 0)) - require.NoError(t, o1.DeleteBlocksBefore(3)) + deleted, err = o1.DeleteBlocksBefore(3, 0) + require.NoError(t, err) + assert.Equal(t, int64(2), deleted) _, err = o1.SelectBlockByNumber(2) require.Equal(t, err, sql.ErrNoRows) _, err = o1.SelectBlockByNumber(3) @@ -1661,3 +1666,57 @@ func Benchmark_LogsDataWordBetween(b *testing.B) { assert.Len(b, logs, 1) } } + +func Benchmark_DeleteExpiredLogs(b *testing.B) { + chainId := big.NewInt(137) + _, db := heavyweight.FullTestDBV2(b, nil) + o := logpoller.NewORM(chainId, db, logger.Test(b), pgtest.NewQConfig(false)) + + numberOfReports := 200_000 + commitStoreAddress := utils.RandomAddress() + commitReportAccepted := utils.RandomBytes32() + + past := time.Now().Add(-1 * time.Hour) + + err := o.InsertFilter(logpoller.Filter{ + Name: "test filter", + EventSigs: []common.Hash{commitReportAccepted}, + Addresses: []common.Address{commitStoreAddress}, + Retention: 1 * time.Millisecond, + }) + require.NoError(b, err) + + for j := 0; j < 5; j++ { + var dbLogs []logpoller.Log + for i := 0; i < numberOfReports; i++ { + + dbLogs = append(dbLogs, logpoller.Log{ + EvmChainId: ubig.New(chainId), + LogIndex: int64(i + 1), + BlockHash: utils.RandomBytes32(), + BlockNumber: int64(i + 1), + BlockTimestamp: past, + EventSig: commitReportAccepted, + Topics: [][]byte{}, + Address: commitStoreAddress, + TxHash: utils.RandomHash(), + Data: []byte{}, + CreatedAt: past, + }) + } + require.NoError(b, o.InsertLogs(dbLogs)) + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + tx, err1 := db.Beginx() + assert.NoError(b, err1) + + _, err1 = o.DeleteExpiredLogs(0, pg.WithQueryer(tx)) + assert.NoError(b, err1) + + err1 = tx.Rollback() + assert.NoError(b, err1) + } +} diff --git a/core/chains/evm/txmgr/txmgr_test.go b/core/chains/evm/txmgr/txmgr_test.go index 0e28f2948ee..85e37571b5a 100644 --- a/core/chains/evm/txmgr/txmgr_test.go +++ b/core/chains/evm/txmgr/txmgr_test.go @@ -50,7 +50,7 @@ import ( func makeTestEvmTxm( t *testing.T, db *sqlx.DB, ethClient evmclient.Client, estimator gas.EvmFeeEstimator, ccfg txmgr.ChainConfig, fcfg txmgr.FeeConfig, txConfig evmconfig.Transactions, dbConfig txmgr.DatabaseConfig, listenerConfig txmgr.ListenerConfig, keyStore keystore.Eth) (txmgr.TxManager, error) { lggr := logger.Test(t) - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr, pgtest.NewQConfig(true)), ethClient, lggr, 100*time.Millisecond, false, 2, 3, 2, 1000) + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr, pgtest.NewQConfig(true)), ethClient, lggr, 100*time.Millisecond, false, 2, 3, 2, 1000, 0) // logic for building components (from evm/evm_txm.go) ------- lggr.Infow("Initializing EVM transaction manager", diff --git a/core/chains/legacyevm/chain.go b/core/chains/legacyevm/chain.go index 92936299cdb..66907b8352f 100644 --- a/core/chains/legacyevm/chain.go +++ b/core/chains/legacyevm/chain.go @@ -251,7 +251,8 @@ func newChain(ctx context.Context, cfg *evmconfig.ChainScoped, nodes []*toml.Nod int64(cfg.EVM().FinalityDepth()), int64(cfg.EVM().LogBackfillBatchSize()), int64(cfg.EVM().RPCDefaultBatchSize()), - int64(cfg.EVM().LogKeepBlocksDepth())) + int64(cfg.EVM().LogKeepBlocksDepth()), + int64(cfg.EVM().LogPrunePageSize())) } } diff --git a/core/config/docs/chains-evm.toml b/core/config/docs/chains-evm.toml index 6f2322fd6db..eec580a8e69 100644 --- a/core/config/docs/chains-evm.toml +++ b/core/config/docs/chains-evm.toml @@ -54,6 +54,9 @@ LogPollInterval = '15s' # Default # **ADVANCED** # LogKeepBlocksDepth works in conjunction with Feature.LogPoller. Controls how many blocks the poller will keep, must be greater than FinalityDepth+1. LogKeepBlocksDepth = 100000 # Default +# **ADVANCED** +# LogPrunePageSize defines size of the page for pruning logs. Controls how many logs/blocks (at most) are deleted in a single prune tick. Default value 0 means no paging, delete everything at once. +LogPrunePageSize = 0 # Default # MinContractPayment is the minimum payment in LINK required to execute a direct request job. This can be overridden on a per-job basis. MinContractPayment = '10000000000000 juels' # Default # MinIncomingConfirmations is the minimum required confirmations before a log event will be consumed. diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 552a91d6e2f..13e18145e73 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -529,6 +529,7 @@ func TestConfig_Marshal(t *testing.T) { LogBackfillBatchSize: ptr[uint32](17), LogPollInterval: &minute, LogKeepBlocksDepth: ptr[uint32](100000), + LogPrunePageSize: ptr[uint32](0), MinContractPayment: commonassets.NewLinkFromJuels(math.MaxInt64), MinIncomingConfirmations: ptr[uint32](13), NonceAutoSync: ptr(true), @@ -923,6 +924,7 @@ LinkContractAddress = '0x538aAaB4ea120b2bC2fe5D296852D948F07D849e' LogBackfillBatchSize = 17 LogPollInterval = '1m0s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 13 MinContractPayment = '9.223372036854775807 link' NonceAutoSync = true diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index bc1b124ccb6..fc47602ef3b 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -253,6 +253,7 @@ LinkContractAddress = '0x538aAaB4ea120b2bC2fe5D296852D948F07D849e' LogBackfillBatchSize = 17 LogPollInterval = '1m0s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 13 MinContractPayment = '9.223372036854775807 link' NonceAutoSync = true diff --git a/core/services/chainlink/testdata/config-multi-chain-effective.toml b/core/services/chainlink/testdata/config-multi-chain-effective.toml index 03990b02a50..2ad6bf30c50 100644 --- a/core/services/chainlink/testdata/config-multi-chain-effective.toml +++ b/core/services/chainlink/testdata/config-multi-chain-effective.toml @@ -240,6 +240,7 @@ LinkContractAddress = '0x514910771AF9Ca656af840dff83E8264EcF986CA' LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true @@ -327,6 +328,7 @@ LinkContractAddress = '0xa36085F69e2889c224210F603D836748e7dC0088' LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true @@ -409,6 +411,7 @@ LinkContractAddress = '0xb0897686c545045aFc77CF20eC7A532E3120E0F1' LogBackfillBatchSize = 1000 LogPollInterval = '1s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 5 MinContractPayment = '0.00001 link' NonceAutoSync = true diff --git a/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go b/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go index a9ba09d96bb..427dd6b32c2 100644 --- a/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go +++ b/core/services/ocr2/plugins/llo/onchain_channel_definition_cache_integration_test.go @@ -77,7 +77,7 @@ func Test_ChannelDefinitionCache_Integration(t *testing.T) { require.NoError(t, err) t.Run("with zero fromblock", func(t *testing.T) { - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr, pgtest.NewQConfig(true)), ethClient, lggr, 100*time.Millisecond, false, 1, 3, 2, 1000) + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr, pgtest.NewQConfig(true)), ethClient, lggr, 100*time.Millisecond, false, 1, 3, 2, 1000, 0) servicetest.Run(t, lp) cdc := llo.NewChannelDefinitionCache(lggr, orm, lp, configStoreAddress, 0) @@ -142,7 +142,7 @@ func Test_ChannelDefinitionCache_Integration(t *testing.T) { t.Run("loads from ORM", func(t *testing.T) { // Override logpoller to always return no logs lp := &mockLogPoller{ - LogPoller: logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr, pgtest.NewQConfig(true)), ethClient, lggr, 100*time.Millisecond, false, 1, 3, 2, 1000), + LogPoller: logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr, pgtest.NewQConfig(true)), ethClient, lggr, 100*time.Millisecond, false, 1, 3, 2, 1000, 0), LatestBlockFn: func(qopts ...pg.QOpt) (int64, error) { return 0, nil }, @@ -176,7 +176,7 @@ func Test_ChannelDefinitionCache_Integration(t *testing.T) { pgtest.MustExec(t, db, `DELETE FROM channel_definitions`) t.Run("with non-zero fromBlock", func(t *testing.T) { - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr, pgtest.NewQConfig(true)), ethClient, lggr, 100*time.Millisecond, false, 1, 3, 2, 1000) + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr, pgtest.NewQConfig(true)), ethClient, lggr, 100*time.Millisecond, false, 1, 3, 2, 1000, 0) servicetest.Run(t, lp) cdc := llo.NewChannelDefinitionCache(lggr, orm, lp, configStoreAddress, channel2Block.Number().Int64()+1) 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 5ef06f1bd08..ba89c52c2ef 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 @@ -661,7 +661,7 @@ func setupDependencies(t *testing.T, db *sqlx.DB, backend *backends.SimulatedBac pollerLggr := logger.TestLogger(t) pollerLggr.SetLogLevel(zapcore.WarnLevel) lorm := logpoller.NewORM(big.NewInt(1337), db, pollerLggr, pgtest.NewQConfig(false)) - lp := logpoller.NewLogPoller(lorm, ethClient, pollerLggr, 100*time.Millisecond, false, 1, 2, 2, 1000) + lp := logpoller.NewLogPoller(lorm, ethClient, pollerLggr, 100*time.Millisecond, false, 1, 2, 2, 1000, 0) return lp, ethClient } diff --git a/core/services/pg/q.go b/core/services/pg/q.go index 49a18817de9..52225ac6168 100644 --- a/core/services/pg/q.go +++ b/core/services/pg/q.go @@ -199,6 +199,16 @@ func (q Q) ExecQIter(query string, args ...interface{}) (sql.Result, context.Can res, err := q.Queryer.ExecContext(ctx, query, args...) return res, cancel, ql.withLogError(err) } +func (q Q) ExecQWithRowsAffected(query string, args ...interface{}) (int64, error) { + res, cancel, err := q.ExecQIter(query, args...) + defer cancel() + if err != nil { + return 0, err + } + + rowsDeleted, err := res.RowsAffected() + return rowsDeleted, err +} func (q Q) ExecQ(query string, args ...interface{}) error { ctx, cancel := q.Context() defer cancel() diff --git a/core/services/pg/q_test.go b/core/services/pg/q_test.go index 6e59e499887..66258fabff5 100644 --- a/core/services/pg/q_test.go +++ b/core/services/pg/q_test.go @@ -3,8 +3,14 @@ package pg import ( "testing" + "github.com/google/uuid" + "github.com/jmoiron/sqlx" "github.com/lib/pq" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/store/dialects" ) func Test_sprintQ(t *testing.T) { @@ -51,3 +57,27 @@ func Test_sprintQ(t *testing.T) { }) } } + +func Test_ExecQWithRowsAffected(t *testing.T) { + db, err := sqlx.Open(string(dialects.TransactionWrappedPostgres), uuid.New().String()) + require.NoError(t, err) + q := NewQ(db, logger.NullLogger, NewQConfig(false)) + + require.NoError(t, q.ExecQ("CREATE TABLE testtable (a TEXT, b TEXT)")) + + rows, err := q.ExecQWithRowsAffected("INSERT INTO testtable (a, b) VALUES ($1, $2)", "foo", "bar") + require.NoError(t, err) + assert.Equal(t, int64(1), rows) + + rows, err = q.ExecQWithRowsAffected("INSERT INTO testtable (a, b) VALUES ($1, $1), ($2, $2), ($1, $2)", "foo", "bar") + require.NoError(t, err) + assert.Equal(t, int64(3), rows) + + rows, err = q.ExecQWithRowsAffected("delete from testtable") + require.NoError(t, err) + assert.Equal(t, int64(4), rows) + + rows, err = q.ExecQWithRowsAffected("delete from testtable") + require.NoError(t, err) + assert.Equal(t, int64(0), rows) +} diff --git a/core/services/promreporter/prom_reporter_test.go b/core/services/promreporter/prom_reporter_test.go index 60d6d9388fa..d283cb8f873 100644 --- a/core/services/promreporter/prom_reporter_test.go +++ b/core/services/promreporter/prom_reporter_test.go @@ -38,7 +38,7 @@ func newLegacyChainContainer(t *testing.T, db *sqlx.DB) legacyevm.LegacyChainCon ethClient := evmtest.NewEthClientMockWithDefaultChain(t) estimator := gas.NewEstimator(logger.TestLogger(t), ethClient, config, evmConfig.GasEstimator()) lggr := logger.TestLogger(t) - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr, pgtest.NewQConfig(true)), ethClient, lggr, 100*time.Millisecond, false, 2, 3, 2, 1000) + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr, pgtest.NewQConfig(true)), ethClient, lggr, 100*time.Millisecond, false, 2, 3, 2, 1000, 0) txm, err := txmgr.NewTxm( db, diff --git a/core/services/relay/evm/chain_reader_test.go b/core/services/relay/evm/chain_reader_test.go index c5fe0a16ed4..64d9f9f1cac 100644 --- a/core/services/relay/evm/chain_reader_test.go +++ b/core/services/relay/evm/chain_reader_test.go @@ -263,7 +263,7 @@ func (it *chainReaderInterfaceTester) GetChainReader(t *testing.T) clcommontypes 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) + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr, pgtest.NewQConfig(true)), it.chain.Client(), lggr, time.Millisecond, false, 0, 1, 1, 10000, 0) require.NoError(t, lp.Start(ctx)) it.chain.On("LogPoller").Return(lp) cr, err := evm.NewChainReaderService(lggr, lp, it.chain, it.chainConfig) diff --git a/core/services/relay/evm/config_poller_test.go b/core/services/relay/evm/config_poller_test.go index cd66e5479bf..089db6decd5 100644 --- a/core/services/relay/evm/config_poller_test.go +++ b/core/services/relay/evm/config_poller_test.go @@ -28,6 +28,7 @@ import ( 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" @@ -89,7 +90,7 @@ func TestConfigPoller(t *testing.T) { cfg := pgtest.NewQConfig(false) ethClient = evmclient.NewSimulatedBackendClient(t, b, testutils.SimulatedChainID) lorm := logpoller.NewORM(testutils.SimulatedChainID, db, lggr, cfg) - lp = logpoller.NewLogPoller(lorm, ethClient, lggr, 100*time.Millisecond, false, 1, 2, 2, 1000) + lp = logpoller.NewLogPoller(lorm, ethClient, lggr, 100*time.Millisecond, false, 1, 2, 2, 1000, 0) servicetest.Run(t, lp) } diff --git a/core/services/relay/evm/functions/config_poller_test.go b/core/services/relay/evm/functions/config_poller_test.go index 2cf373d2e86..6a8c682a81b 100644 --- a/core/services/relay/evm/functions/config_poller_test.go +++ b/core/services/relay/evm/functions/config_poller_test.go @@ -81,7 +81,7 @@ func runTest(t *testing.T, pluginType functions.FunctionsPluginType, expectedDig defer ethClient.Close() 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) + lp := logpoller.NewLogPoller(lorm, ethClient, lggr, 100*time.Millisecond, false, 1, 2, 2, 1000, 0) servicetest.Run(t, lp) configPoller, err := functions.NewFunctionsConfigPoller(pluginType, lp, lggr) require.NoError(t, err) diff --git a/core/services/relay/evm/mercury/helpers_test.go b/core/services/relay/evm/mercury/helpers_test.go index f1686ee00c8..8283e80916e 100644 --- a/core/services/relay/evm/mercury/helpers_test.go +++ b/core/services/relay/evm/mercury/helpers_test.go @@ -167,7 +167,7 @@ func SetupTH(t *testing.T, feedID common.Hash) TestHarness { ethClient := evmclient.NewSimulatedBackendClient(t, b, big.NewInt(1337)) 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) + lp := logpoller.NewLogPoller(lorm, ethClient, lggr, 100*time.Millisecond, false, 1, 2, 2, 1000, 0) servicetest.Run(t, lp) configPoller, err := NewConfigPoller(lggr, lp, verifierAddress, feedID) 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 6f5177c230a..c92795f55a6 100644 --- a/core/services/vrf/v2/listener_v2_log_listener_test.go +++ b/core/services/vrf/v2/listener_v2_log_listener_test.go @@ -92,7 +92,7 @@ func setupVRFLogPollerListenerTH(t *testing.T, // 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) + lp := logpoller.NewLogPoller(o, esc, lggr, 1*time.Hour, useFinalityTag, finalityDepth, backfillBatchSize, rpcBatchSize, keepFinalizedBlocksDepth, 0) emitterAddress1, _, emitter1, err := log_emitter.DeployLogEmitter(owner, ec) require.NoError(t, err) diff --git a/core/web/resolver/chain_test.go b/core/web/resolver/chain_test.go index b7a4b7c8386..e2663af561f 100644 --- a/core/web/resolver/chain_test.go +++ b/core/web/resolver/chain_test.go @@ -10,6 +10,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/loop" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + evmtoml "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" chainlinkmocks "github.com/smartcontractkit/chainlink/v2/core/services/chainlink/mocks" @@ -44,6 +45,7 @@ LinkContractAddress = '0x538aAaB4ea120b2bC2fe5D296852D948F07D849e' LogBackfillBatchSize = 17 LogPollInterval = '1m0s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 13 MinContractPayment = '9.223372036854775807 link' NonceAutoSync = true @@ -161,6 +163,7 @@ LinkContractAddress = '0x538aAaB4ea120b2bC2fe5D296852D948F07D849e' LogBackfillBatchSize = 17 LogPollInterval = '1m0s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 13 MinContractPayment = '9.223372036854775807 link' NonceAutoSync = true diff --git a/core/web/resolver/testdata/config-full.toml b/core/web/resolver/testdata/config-full.toml index 67ddbb33efd..2c84024cde6 100644 --- a/core/web/resolver/testdata/config-full.toml +++ b/core/web/resolver/testdata/config-full.toml @@ -253,6 +253,7 @@ LinkContractAddress = '0x538aAaB4ea120b2bC2fe5D296852D948F07D849e' LogBackfillBatchSize = 17 LogPollInterval = '1m0s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 13 MinContractPayment = '9.223372036854775807 link' NonceAutoSync = true diff --git a/core/web/resolver/testdata/config-multi-chain-effective.toml b/core/web/resolver/testdata/config-multi-chain-effective.toml index 03990b02a50..2ad6bf30c50 100644 --- a/core/web/resolver/testdata/config-multi-chain-effective.toml +++ b/core/web/resolver/testdata/config-multi-chain-effective.toml @@ -240,6 +240,7 @@ LinkContractAddress = '0x514910771AF9Ca656af840dff83E8264EcF986CA' LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true @@ -327,6 +328,7 @@ LinkContractAddress = '0xa36085F69e2889c224210F603D836748e7dC0088' LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true @@ -409,6 +411,7 @@ LinkContractAddress = '0xb0897686c545045aFc77CF20eC7A532E3120E0F1' LogBackfillBatchSize = 1000 LogPollInterval = '1s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 5 MinContractPayment = '0.00001 link' NonceAutoSync = true diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 6647dcc1efd..7ef27305809 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Gas bumping logic to the `SuggestedPriceEstimator`. The bumping mechanism for this estimator refetches the price from the RPC and adds a buffer on top using the greater of `BumpPercent` and `BumpMin`. - Add preliminary support for "llo" job type (Data Streams V1) +- Add `LogPrunePageSize` parameter to the EVM configuration. This parameter controls the number of logs removed during prune phase in LogPoller. Default value is 0, which deletes all logs at once - exactly how it used to work, so it doesn't require any changes on the product's side. ### Fixed diff --git a/docs/CONFIG.md b/docs/CONFIG.md index cb1653c1020..baafab0d347 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -1603,6 +1603,7 @@ LinkContractAddress = '0x514910771AF9Ca656af840dff83E8264EcF986CA' LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true @@ -1685,6 +1686,7 @@ LinkContractAddress = '0x20fE562d797A42Dcb3399062AE9546cd06f63280' LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true @@ -1766,6 +1768,7 @@ LinkContractAddress = '0x01BE23585060835E02B77ef475b0Cc51aA1e0709' LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true @@ -1847,6 +1850,7 @@ LinkContractAddress = '0x326C977E6efc84E512bB9C30f76E30c160eD06FB' LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true @@ -1929,6 +1933,7 @@ LinkContractAddress = '0x350a791Bfc2C21F9Ed5d10980Dad2e2638ffa7f6' LogBackfillBatchSize = 1000 LogPollInterval = '2s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -2010,6 +2015,7 @@ LinkContractAddress = '0x14AdaE34beF7ca957Ce2dDe5ADD97ea050123827' LogBackfillBatchSize = 1000 LogPollInterval = '30s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.001 link' NonceAutoSync = true @@ -2091,6 +2097,7 @@ LinkContractAddress = '0x8bBbd80981FE76d44854D8DF305e8985c19f0e78' LogBackfillBatchSize = 1000 LogPollInterval = '30s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.001 link' NonceAutoSync = true @@ -2172,6 +2179,7 @@ LinkContractAddress = '0xa36085F69e2889c224210F603D836748e7dC0088' LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true @@ -2254,6 +2262,7 @@ LinkContractAddress = '0x404460C6A5EdE2D891e8297795264fDe62ADBB75' LogBackfillBatchSize = 1000 LogPollInterval = '3s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -2334,6 +2343,7 @@ FinalityTagEnabled = false LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -2414,6 +2424,7 @@ FinalityTagEnabled = false LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -2495,6 +2506,7 @@ LinkContractAddress = '0x84b9B910527Ad5C03A9Ca831909E21e236EA7b06' LogBackfillBatchSize = 1000 LogPollInterval = '3s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -2577,6 +2589,7 @@ LinkContractAddress = '0xE2e73A1c69ecF83F464EFCE6A5be353a37cA09b2' LogBackfillBatchSize = 1000 LogPollInterval = '5s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -2658,6 +2671,7 @@ LinkContractAddress = '0x404460C6A5EdE2D891e8297795264fDe62ADBB75' LogBackfillBatchSize = 1000 LogPollInterval = '3s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -2739,6 +2753,7 @@ LinkContractAddress = '0xb0897686c545045aFc77CF20eC7A532E3120E0F1' LogBackfillBatchSize = 1000 LogPollInterval = '1s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 5 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -2820,6 +2835,7 @@ LinkContractAddress = '0x6F43FF82CCA38001B6699a8AC47A2d0E66939407' LogBackfillBatchSize = 1000 LogPollInterval = '1s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -2901,6 +2917,7 @@ FinalityTagEnabled = false LogBackfillBatchSize = 1000 LogPollInterval = '2s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -2982,6 +2999,7 @@ FinalityTagEnabled = false LogBackfillBatchSize = 1000 LogPollInterval = '5s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -3063,6 +3081,7 @@ FinalityTagEnabled = false LogBackfillBatchSize = 1000 LogPollInterval = '5s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -3145,6 +3164,7 @@ LinkContractAddress = '0xdc2CC710e42857672E7907CF474a69B63B93089f' LogBackfillBatchSize = 1000 LogPollInterval = '2s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -3226,6 +3246,7 @@ FinalityTagEnabled = false LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -3306,6 +3327,7 @@ FinalityTagEnabled = false LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -3387,6 +3409,7 @@ FinalityTagEnabled = false LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -3467,6 +3490,7 @@ FinalityTagEnabled = false LogBackfillBatchSize = 1000 LogPollInterval = '30s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -3548,6 +3572,7 @@ FinalityTagEnabled = false LogBackfillBatchSize = 1000 LogPollInterval = '3s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -3629,6 +3654,7 @@ FinalityTagEnabled = false LogBackfillBatchSize = 1000 LogPollInterval = '3s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -3709,6 +3735,7 @@ FinalityTagEnabled = false LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 1 MinContractPayment = '100' NonceAutoSync = true @@ -3789,6 +3816,7 @@ FinalityTagEnabled = false LogBackfillBatchSize = 1000 LogPollInterval = '30s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -3870,6 +3898,7 @@ FinalityTagEnabled = false LogBackfillBatchSize = 1000 LogPollInterval = '2s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -3951,6 +3980,7 @@ LinkContractAddress = '0xfaFedb041c0DD4fA2Dc0d87a6B0979Ee6FA7af5F' LogBackfillBatchSize = 1000 LogPollInterval = '1s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -4031,6 +4061,7 @@ FinalityTagEnabled = false LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -4112,6 +4143,7 @@ FinalityTagEnabled = false LogBackfillBatchSize = 1000 LogPollInterval = '2s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -4193,6 +4225,7 @@ FinalityTagEnabled = false LogBackfillBatchSize = 1000 LogPollInterval = '5s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -4275,6 +4308,7 @@ LinkContractAddress = '0xf97f4df75117a78c1A5a0DBb814Af92458539FB4' LogBackfillBatchSize = 1000 LogPollInterval = '1s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -4356,6 +4390,7 @@ FinalityTagEnabled = false LogBackfillBatchSize = 1000 LogPollInterval = '5s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -4437,6 +4472,7 @@ LinkContractAddress = '0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846' LogBackfillBatchSize = 1000 LogPollInterval = '3s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -4518,6 +4554,7 @@ LinkContractAddress = '0x5947BB275c521040051D82396192181b413227A3' LogBackfillBatchSize = 1000 LogPollInterval = '3s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -4599,6 +4636,7 @@ FinalityTagEnabled = false LogBackfillBatchSize = 1000 LogPollInterval = '5s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -4679,6 +4717,7 @@ FinalityTagEnabled = false LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -4759,6 +4798,7 @@ FinalityTagEnabled = false LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -4840,6 +4880,7 @@ LinkContractAddress = '0x326C977E6efc84E512bB9C30f76E30c160eD06FB' LogBackfillBatchSize = 1000 LogPollInterval = '1s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 5 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -4921,6 +4962,7 @@ FinalityTagEnabled = false LogBackfillBatchSize = 1000 LogPollInterval = '2s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -5002,6 +5044,7 @@ FinalityTagEnabled = false LogBackfillBatchSize = 1000 LogPollInterval = '2s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -5084,6 +5127,7 @@ LinkContractAddress = '0x615fBe6372676474d9e6933d310469c9b68e9726' LogBackfillBatchSize = 1000 LogPollInterval = '1s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -5166,6 +5210,7 @@ LinkContractAddress = '0xd14838A68E8AFBAdE5efb411d5871ea0011AFd28' LogBackfillBatchSize = 1000 LogPollInterval = '1s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -5247,6 +5292,7 @@ FinalityTagEnabled = false LogBackfillBatchSize = 1000 LogPollInterval = '1s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -5328,6 +5374,7 @@ FinalityTagEnabled = false LogBackfillBatchSize = 1000 LogPollInterval = '3s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -5409,6 +5456,7 @@ FinalityTagEnabled = false LogBackfillBatchSize = 1000 LogPollInterval = '3s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -5490,6 +5538,7 @@ LinkContractAddress = '0x779877A7B0D9E8603169DdbD7836e478b4624789' LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true @@ -5571,6 +5620,7 @@ LinkContractAddress = '0x218532a12a389a4a92fC0C5Fb22901D1c19198aA' LogBackfillBatchSize = 1000 LogPollInterval = '2s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -5652,6 +5702,7 @@ LinkContractAddress = '0x8b12Ac23BFe11cAb03a634C1F117D64a7f2cFD3e' LogBackfillBatchSize = 1000 LogPollInterval = '2s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 1 MinContractPayment = '0.00001 link' NonceAutoSync = true @@ -5826,6 +5877,13 @@ LogKeepBlocksDepth = 100000 # Default ``` LogKeepBlocksDepth works in conjunction with Feature.LogPoller. Controls how many blocks the poller will keep, must be greater than FinalityDepth+1. +### LogPrunePageSize +:warning: **_ADVANCED_**: _Do not change this setting unless you know what you are doing._ +```toml +LogPrunePageSize = 0 # Default +``` +LogPrunePageSize defines size of the page for pruning logs. Controls how many logs/blocks (at most) are deleted in a single prune tick. Default value 0 means no paging, delete everything at once. + ### MinContractPayment ```toml MinContractPayment = '10000000000000 juels' # Default diff --git a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar index 648c94ed2a1..be61aef4898 100644 --- a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar @@ -296,6 +296,7 @@ LinkContractAddress = '0x514910771AF9Ca656af840dff83E8264EcF986CA' LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = 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 dc7f87375a0..bf39b7d8394 100644 --- a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar @@ -296,6 +296,7 @@ LinkContractAddress = '0x514910771AF9Ca656af840dff83E8264EcF986CA' LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true diff --git a/testdata/scripts/node/validate/disk-based-logging.txtar b/testdata/scripts/node/validate/disk-based-logging.txtar index 25cba748c78..0ef1c6a7132 100644 --- a/testdata/scripts/node/validate/disk-based-logging.txtar +++ b/testdata/scripts/node/validate/disk-based-logging.txtar @@ -296,6 +296,7 @@ LinkContractAddress = '0x514910771AF9Ca656af840dff83E8264EcF986CA' LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true diff --git a/testdata/scripts/node/validate/invalid.txtar b/testdata/scripts/node/validate/invalid.txtar index 036d9544e74..e4eccf9895a 100644 --- a/testdata/scripts/node/validate/invalid.txtar +++ b/testdata/scripts/node/validate/invalid.txtar @@ -286,6 +286,7 @@ LinkContractAddress = '0x514910771AF9Ca656af840dff83E8264EcF986CA' LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true diff --git a/testdata/scripts/node/validate/valid.txtar b/testdata/scripts/node/validate/valid.txtar index 6a95692d295..9c1026d7f4c 100644 --- a/testdata/scripts/node/validate/valid.txtar +++ b/testdata/scripts/node/validate/valid.txtar @@ -293,6 +293,7 @@ LinkContractAddress = '0x514910771AF9Ca656af840dff83E8264EcF986CA' LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' NonceAutoSync = true From 1e7e5b73966143e76c0076f0e70217db96c6ee39 Mon Sep 17 00:00:00 2001 From: chris-de-leon-cll <147140544+chris-de-leon-cll@users.noreply.github.com> Date: Fri, 23 Feb 2024 03:32:02 -0800 Subject: [PATCH 19/24] adds scroll l2ep fixes (#12103) --- contracts/gas-snapshots/l2ep.gas-snapshot | 8 +- .../v0.8/l2ep/dev/scroll/ScrollValidator.sol | 23 +++++- .../mocks/scroll/MockScrollL1MessageQueue.sol | 77 +++++++++++++++++++ .../test/v1_0_0/scroll/ScrollValidator.t.sol | 4 + .../test/v0.8/dev/ScrollValidator.test.ts | 9 +++ 5 files changed, 115 insertions(+), 6 deletions(-) create mode 100644 contracts/src/v0.8/l2ep/test/mocks/scroll/MockScrollL1MessageQueue.sol diff --git a/contracts/gas-snapshots/l2ep.gas-snapshot b/contracts/gas-snapshots/l2ep.gas-snapshot index 1f229f7d1d9..fdc9ec9b22c 100644 --- a/contracts/gas-snapshots/l2ep.gas-snapshot +++ b/contracts/gas-snapshots/l2ep.gas-snapshot @@ -140,7 +140,7 @@ ScrollSequencerUptimeFeed_UpdateStatus:test_RevertIfNotL2CrossDomainMessengerAdd 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 +ScrollValidator_SetGasLimit:test_CorrectlyUpdatesTheGasLimit() (gas: 15637) +ScrollValidator_Validate:test_PostSequencerOffline() (gas: 78367) +ScrollValidator_Validate:test_PostSequencerStatusWhenThereIsNotStatusChange() (gas: 78423) +ScrollValidator_Validate:test_RevertsIfCalledByAnAccountWithNoAccess() (gas: 15569) \ No newline at end of file diff --git a/contracts/src/v0.8/l2ep/dev/scroll/ScrollValidator.sol b/contracts/src/v0.8/l2ep/dev/scroll/ScrollValidator.sol index 31a5f0764ef..968b891b54a 100644 --- a/contracts/src/v0.8/l2ep/dev/scroll/ScrollValidator.sol +++ b/contracts/src/v0.8/l2ep/dev/scroll/ScrollValidator.sol @@ -7,6 +7,7 @@ import {ScrollSequencerUptimeFeedInterface} from "../interfaces/ScrollSequencerU import {SimpleWriteAccessController} from "../../../shared/access/SimpleWriteAccessController.sol"; +import {IL1MessageQueue} from "@scroll-tech/contracts/L1/rollup/IL1MessageQueue.sol"; import {IL1ScrollMessenger} from "@scroll-tech/contracts/L1/IL1ScrollMessenger.sol"; /// @title ScrollValidator - makes cross chain call to update the Sequencer Uptime Feed on L2 @@ -15,6 +16,8 @@ contract ScrollValidator is TypeAndVersionInterface, AggregatorValidatorInterfac 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/prefix-immutable-variables-with-i + address public immutable L1_MSG_QUEUE_ADDR; // solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables string public constant override typeAndVersion = "ScrollValidator 1.0.0"; @@ -28,13 +31,21 @@ contract ScrollValidator is TypeAndVersionInterface, AggregatorValidatorInterfac /// @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) { + constructor( + address l1CrossDomainMessengerAddress, + address l2UptimeFeedAddr, + address l1MessageQueueAddr, + uint32 gasLimit + ) { // solhint-disable-next-line custom-errors require(l1CrossDomainMessengerAddress != address(0), "Invalid xDomain Messenger address"); // solhint-disable-next-line custom-errors + require(l1MessageQueueAddr != address(0), "Invalid L1 message queue 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; + L1_MSG_QUEUE_ADDR = l1MessageQueueAddr; s_gasLimit = gasLimit; } @@ -50,6 +61,12 @@ contract ScrollValidator is TypeAndVersionInterface, AggregatorValidatorInterfac return s_gasLimit; } + /// @notice makes this contract payable + /// @dev receives funds: + /// - to use them (if configured) to pay for L2 execution on L1 + /// - when withdrawing funds from L2 xDomain alias address (pay for L2 execution on L2) + receive() external payable {} + /// @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. @@ -60,7 +77,9 @@ contract ScrollValidator is TypeAndVersionInterface, AggregatorValidatorInterfac int256 currentAnswer ) external override checkAccess returns (bool) { // Make the xDomain call - IL1ScrollMessenger(L1_CROSS_DOMAIN_MESSENGER_ADDRESS).sendMessage( + IL1ScrollMessenger(L1_CROSS_DOMAIN_MESSENGER_ADDRESS).sendMessage{ + value: IL1MessageQueue(L1_MSG_QUEUE_ADDR).estimateCrossDomainMessageFee(s_gasLimit) + }( L2_UPTIME_FEED_ADDR, 0, abi.encodeWithSelector( diff --git a/contracts/src/v0.8/l2ep/test/mocks/scroll/MockScrollL1MessageQueue.sol b/contracts/src/v0.8/l2ep/test/mocks/scroll/MockScrollL1MessageQueue.sol new file mode 100644 index 00000000000..1700bcbe168 --- /dev/null +++ b/contracts/src/v0.8/l2ep/test/mocks/scroll/MockScrollL1MessageQueue.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {IL1MessageQueue} from "@scroll-tech/contracts/L1/rollup/IL1MessageQueue.sol"; + +contract MockScrollL1MessageQueue is IL1MessageQueue { + /// @notice The start index of all pending inclusion messages. + function pendingQueueIndex() external pure returns (uint256) { + return 0; + } + + /// @notice Return the index of next appended message. + function nextCrossDomainMessageIndex() external pure returns (uint256) { + return 0; + } + + /// @notice Return the message of in `queueIndex`. + function getCrossDomainMessage(uint256 /* queueIndex */) external pure returns (bytes32) { + return ""; + } + + /// @notice Return the amount of ETH should pay for cross domain message. + function estimateCrossDomainMessageFee(uint256 /* gasLimit */) external pure returns (uint256) { + return 0; + } + + /// @notice Return the amount of intrinsic gas fee should pay for cross domain message. + function calculateIntrinsicGasFee(bytes memory /* _calldata */) external pure returns (uint256) { + return 0; + } + + /// @notice Return the hash of a L1 message. + function computeTransactionHash( + address /* sender */, + uint256 /* queueIndex */, + uint256 /* value */, + address /* target */, + uint256 /* gasLimit */, + bytes calldata /* data */ + ) external pure returns (bytes32) { + return 0; + } + + /// @notice Append a L1 to L2 message into this contract. + /// @param target The address of target contract to call in L2. + /// @param gasLimit The maximum gas should be used for relay this message in L2. + /// @param data The calldata passed to target contract. + function appendCrossDomainMessage(address target, uint256 gasLimit, bytes calldata data) external {} + + /// @notice Append an enforced transaction to this contract. + /// @dev The address of sender should be an EOA. + /// @param sender The address of sender who will initiate this transaction in L2. + /// @param target The address of target contract to call in L2. + /// @param value The value passed + /// @param gasLimit The maximum gas should be used for this transaction in L2. + /// @param data The calldata passed to target contract. + function appendEnforcedTransaction( + address sender, + address target, + uint256 value, + uint256 gasLimit, + bytes calldata data + ) external {} + + /// @notice Pop finalized messages from queue. + /// + /// @dev We can pop at most 256 messages each time. And if the message is not skipped, + /// the corresponding entry will be cleared. + /// + /// @param startIndex The start index to pop. + /// @param count The number of messages to pop. + /// @param skippedBitmap A bitmap indicates whether a message is skipped. + function popCrossDomainMessage(uint256 startIndex, uint256 count, uint256 skippedBitmap) external {} + + /// @notice Drop a skipped message from the queue. + function dropCrossDomainMessage(uint256 index) external {} +} 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 index 969c78c72ef..f425ca1c6e5 100644 --- 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 @@ -3,6 +3,7 @@ pragma solidity 0.8.19; import {MockScrollL1CrossDomainMessenger} from "../../mocks/scroll/MockScrollL1CrossDomainMessenger.sol"; import {MockScrollL2CrossDomainMessenger} from "../../mocks/scroll/MockScrollL2CrossDomainMessenger.sol"; +import {MockScrollL1MessageQueue} from "../../mocks/scroll/MockScrollL1MessageQueue.sol"; import {ScrollSequencerUptimeFeed} from "../../../dev/scroll/ScrollSequencerUptimeFeed.sol"; import {ScrollValidator} from "../../../dev/scroll/ScrollValidator.sol"; import {L2EPTest} from "../L2EPTest.t.sol"; @@ -15,6 +16,7 @@ contract ScrollValidatorTest is L2EPTest { /// L2EP contracts MockScrollL1CrossDomainMessenger internal s_mockScrollL1CrossDomainMessenger; MockScrollL2CrossDomainMessenger internal s_mockScrollL2CrossDomainMessenger; + MockScrollL1MessageQueue internal s_mockScrollL1MessageQueue; ScrollSequencerUptimeFeed internal s_scrollSequencerUptimeFeed; ScrollValidator internal s_scrollValidator; @@ -32,6 +34,7 @@ contract ScrollValidatorTest is L2EPTest { function setUp() public { s_mockScrollL1CrossDomainMessenger = new MockScrollL1CrossDomainMessenger(); s_mockScrollL2CrossDomainMessenger = new MockScrollL2CrossDomainMessenger(); + s_mockScrollL1MessageQueue = new MockScrollL1MessageQueue(); s_scrollSequencerUptimeFeed = new ScrollSequencerUptimeFeed( address(s_mockScrollL1CrossDomainMessenger), @@ -42,6 +45,7 @@ contract ScrollValidatorTest is L2EPTest { s_scrollValidator = new ScrollValidator( address(s_mockScrollL1CrossDomainMessenger), address(s_scrollSequencerUptimeFeed), + address(s_mockScrollL1MessageQueue), INIT_GAS_LIMIT ); } diff --git a/contracts/test/v0.8/dev/ScrollValidator.test.ts b/contracts/test/v0.8/dev/ScrollValidator.test.ts index c5ec59c5c99..94205a03112 100644 --- a/contracts/test/v0.8/dev/ScrollValidator.test.ts +++ b/contracts/test/v0.8/dev/ScrollValidator.test.ts @@ -9,6 +9,7 @@ describe('ScrollValidator', () => { const L2_SEQ_STATUS_RECORDER_ADDRESS = '0x491B1dDA0A8fa069bbC1125133A975BF4e85a91b' let scrollValidator: Contract + let l1MessageQueue: Contract let scrollUptimeFeedFactory: ContractFactory let mockScrollL1CrossDomainMessenger: Contract let deployer: SignerWithAddress @@ -35,6 +36,13 @@ describe('ScrollValidator', () => { mockScrollL1CrossDomainMessenger = await mockScrollL1CrossDomainMessengerFactory.deploy() + // Scroll Message Queue contract on L1 + const l1MessageQueueFactory = await ethers.getContractFactory( + 'src/v0.8/l2ep/test/mocks/scroll/MockScrollL1MessageQueue.sol:MockScrollL1MessageQueue', + deployer, + ) + l1MessageQueue = await l1MessageQueueFactory.deploy() + // Contract under test const scrollValidatorFactory = await ethers.getContractFactory( 'src/v0.8/l2ep/dev/scroll/ScrollValidator.sol:ScrollValidator', @@ -44,6 +52,7 @@ describe('ScrollValidator', () => { scrollValidator = await scrollValidatorFactory.deploy( mockScrollL1CrossDomainMessenger.address, L2_SEQ_STATUS_RECORDER_ADDRESS, + l1MessageQueue.address, GAS_LIMIT, ) }) From 4415ddb4b3f31b519239ab87d9c01e1c4191aa6f Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 23 Feb 2024 08:57:03 -0500 Subject: [PATCH 20/24] Required version of postgres is now >= 12 (#12155) * Required version of postgres is now >= 12 11 has been EOL'd in November 2023. See: https://www.postgresql.org/support/versioning/ * Enforce minimum required Postgres version - We should not allow Chainlink to run on EOL'd postgres * Remove panic, update docker client version --- README.md | 5 +-- core/services/pg/connection.go | 46 ++++++++++++++++++++++++ core/services/pg/connection_test.go | 54 ++++++++++++++++++++++++++++- docs/CHANGELOG.md | 4 +++ 4 files changed, 106 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index eff798d46b8..099712061d5 100644 --- a/README.md +++ b/README.md @@ -36,8 +36,9 @@ regarding Chainlink social accounts, news, and networking. - Example Path for macOS `export PATH=$GOPATH/bin:$PATH` & `export GOPATH=/Users/$USER/go` 2. Install [NodeJS v16](https://nodejs.org/en/download/package-manager/) & [pnpm via npm](https://pnpm.io/installation#using-npm). - It might be easier long term to use [nvm](https://nodejs.org/en/download/package-manager/#nvm) to switch between node versions for different projects. For example, assuming $NODE_VERSION was set to a valid version of NodeJS, you could run: `nvm install $NODE_VERSION && nvm use $NODE_VERSION` -3. Install [Postgres (>= 11.x and <= 15.x)](https://wiki.postgresql.org/wiki/Detailed_installation_guides). - - You should [configure Postgres](https://www.postgresql.org/docs/12/ssl-tcp.html) to use SSL connection (or for testing you can set `?sslmode=disable` in your Postgres query string). +3. Install [Postgres (>= 12.x)](https://wiki.postgresql.org/wiki/Detailed_installation_guides). It is recommended to run the latest major version of postgres. + - Note if you are running the official Chainlink docker image, the highest supported Postgres version is 15.x due to the bundled client. + - You should [configure Postgres](https://www.postgresql.org/docs/current/ssl-tcp.html) to use SSL connection (or for testing you can set `?sslmode=disable` in your Postgres query string). 4. Ensure you have Python 3 installed (this is required by [solc-select](https://github.com/crytic/solc-select) which is needed to compile solidity contracts) 5. Download Chainlink: `git clone https://github.com/smartcontractkit/chainlink && cd chainlink` 6. Build and install Chainlink: `make install` diff --git a/core/services/pg/connection.go b/core/services/pg/connection.go index 7848c0ed5e1..3fcfd3f4ad4 100644 --- a/core/services/pg/connection.go +++ b/core/services/pg/connection.go @@ -2,6 +2,8 @@ package pg import ( "fmt" + "log" + "os" "time" "github.com/google/uuid" @@ -16,6 +18,24 @@ import ( "github.com/XSAM/otelsql" ) +var MinRequiredPGVersion = 110000 + +func init() { + // from: https://www.postgresql.org/support/versioning/ + now := time.Now() + if now.Year() > 2023 { + MinRequiredPGVersion = 120000 + } else if now.Year() > 2024 { + MinRequiredPGVersion = 130000 + } else if now.Year() > 2025 { + MinRequiredPGVersion = 140000 + } else if now.Year() > 2026 { + MinRequiredPGVersion = 150000 + } else if now.Year() > 2027 { + MinRequiredPGVersion = 160000 + } +} + type ConnectionConfig interface { DefaultIdleInTxSessionTimeout() time.Duration DefaultLockTimeout() time.Duration @@ -65,9 +85,35 @@ func NewConnection(uri string, dialect dialects.DialectName, config ConnectionCo db.SetMaxOpenConns(config.MaxOpenConns()) db.SetMaxIdleConns(config.MaxIdleConns()) + if os.Getenv("SKIP_PG_VERSION_CHECK") != "true" { + if err := checkVersion(db, MinRequiredPGVersion); err != nil { + return nil, err + } + } + return db, disallowReplica(db) } +type Getter interface { + Get(dest interface{}, query string, args ...interface{}) error +} + +func checkVersion(db Getter, minVersion int) error { + var version int + if err := db.Get(&version, "SHOW server_version_num"); err != nil { + log.Printf("Error getting server version, skipping Postgres version check: %s", err.Error()) + return nil + } + if version < 10000 { + log.Printf("Unexpectedly small version, skipping Postgres version check (you are running: %d)", version) + return nil + } + if version < minVersion { + return fmt.Errorf("The minimum required Postgres server version is %d, you are running: %d, which is EOL (see: https://www.postgresql.org/support/versioning/). It is recommended to upgrade your Postgres server. To forcibly override this check, set SKIP_PG_VERSION_CHECK=true", minVersion/10000, version/10000) + } + return nil +} + func disallowReplica(db *sqlx.DB) error { var val string err := db.Get(&val, "SHOW session_replication_role") diff --git a/core/services/pg/connection_test.go b/core/services/pg/connection_test.go index 651bf9d2d9b..b10625a82c9 100644 --- a/core/services/pg/connection_test.go +++ b/core/services/pg/connection_test.go @@ -2,18 +2,70 @@ package pg import ( "testing" + "time" "github.com/google/uuid" _ "github.com/jackc/pgx/v4/stdlib" "github.com/jmoiron/sqlx" + "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/store/dialects" ) -func Test_disallowReplica(t *testing.T) { +var _ Getter = &mockGetter{} + +type mockGetter struct { + version int + err error +} + +func (m *mockGetter) Get(dest interface{}, query string, args ...interface{}) error { + if m.err != nil { + return m.err + } + *(dest.(*int)) = m.version + return nil +} + +func Test_checkVersion(t *testing.T) { + if time.Now().Year() > 2027 { + t.Fatal("Postgres version numbers only registered until 2028, please update the postgres version check using: https://www.postgresql.org/support/versioning/ then fix this test") + } + t.Run("when the version is too low", func(t *testing.T) { + m := &mockGetter{version: 100000} + err := checkVersion(m, 110000) + require.Error(t, err) + assert.Contains(t, err.Error(), "The minimum required Postgres server version is 11, you are running: 10") + }) + t.Run("when the version is at minimum", func(t *testing.T) { + m := &mockGetter{version: 110000} + err := checkVersion(m, 110000) + require.NoError(t, err) + }) + t.Run("when the version is above minimum", func(t *testing.T) { + m := &mockGetter{version: 110001} + err := checkVersion(m, 110000) + require.NoError(t, err) + m = &mockGetter{version: 120000} + err = checkVersion(m, 110001) + require.NoError(t, err) + }) + t.Run("ignores wildly small versions, 0 etc", func(t *testing.T) { + m := &mockGetter{version: 9000} + err := checkVersion(m, 110001) + require.NoError(t, err) + }) + t.Run("ignores errors", func(t *testing.T) { + m := &mockGetter{err: errors.New("some error")} + err := checkVersion(m, 110001) + require.NoError(t, err) + }) +} +func Test_disallowReplica(t *testing.T) { testutils.SkipShortDB(t) db, err := sqlx.Open(string(dialects.TransactionWrappedPostgres), uuid.New().String()) require.NoError(t, err) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 7ef27305809..b7c37ecb348 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -19,6 +19,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `P2P.V2` is required in configuration when either `OCR` or `OCR2` are enabled. The node will fail to boot if `P2P.V2` is not enabled. +### Changed + +- Minimum required version of Postgres is now >= 12. Postgres 11 was EOL'd in November 2023. Added a new version check that will prevent Chainlink from running on EOL'd Postgres. If you are running Postgres <= 11 you should upgrade to the latest version. The check can be forcibly overridden by setting SKIP_PG_VERSION_CHECK=true. + ## 2.9.0 - UNRELEASED ### Added From ec28bb9bbb37408403d80fb7bb00af90d770175c Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 23 Feb 2024 09:12:35 -0500 Subject: [PATCH 21/24] Make the test a bit more reliable (#12129) --- core/services/ocr2/plugins/mercury/integration_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/services/ocr2/plugins/mercury/integration_test.go b/core/services/ocr2/plugins/mercury/integration_test.go index 36189475898..a12052e0b74 100644 --- a/core/services/ocr2/plugins/mercury/integration_test.go +++ b/core/services/ocr2/plugins/mercury/integration_test.go @@ -283,8 +283,8 @@ func integration_MercuryV1(t *testing.T) { 2*time.Second, // DeltaProgress 20*time.Second, // DeltaResend 400*time.Millisecond, // DeltaInitial - 100*time.Millisecond, // DeltaRound - 0, // DeltaGrace + 200*time.Millisecond, // DeltaRound + 100*time.Millisecond, // DeltaGrace 300*time.Millisecond, // DeltaCertifiedCommitRequest 1*time.Minute, // DeltaStage 100, // rMax From d10b4710d5710860f9e69a28c6a98eaba54b2caf Mon Sep 17 00:00:00 2001 From: amit-momin <108959691+amit-momin@users.noreply.github.com> Date: Fri, 23 Feb 2024 08:52:10 -0600 Subject: [PATCH 22/24] Fix losing batch call errors for individual requests (#12127) * Fixed losing batch call errors for individual requests * Fixed linting * Added comments * Fixed linting * Improved error check in test --- common/client/mock_rpc_test.go | 70 +- common/client/multi_node.go | 94 +- common/client/multi_node_test.go | 12 +- common/client/types.go | 6 +- core/chains/evm/client/chain_client.go | 17 +- core/chains/evm/client/chain_client_test.go | 70 ++ core/chains/evm/client/chain_id_sub_test.go | 26 +- core/chains/evm/client/helpers_test.go | 42 + core/chains/evm/client/mocks/rpc_client.go | 1028 +++++++++++++++++ core/chains/evm/client/rpc_client.go | 11 +- core/chains/evm/client/send_only_node_test.go | 17 +- 11 files changed, 1258 insertions(+), 135 deletions(-) create mode 100644 core/chains/evm/client/chain_client_test.go create mode 100644 core/chains/evm/client/mocks/rpc_client.go diff --git a/common/client/mock_rpc_test.go b/common/client/mock_rpc_test.go index 72c6eb19029..465445b8092 100644 --- a/common/client/mock_rpc_test.go +++ b/common/client/mock_rpc_test.go @@ -17,12 +17,12 @@ import ( ) // mockRPC is an autogenerated mock type for the RPC type -type mockRPC[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH]] struct { +type mockRPC[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}] struct { mock.Mock } // BalanceAt provides a mock function with given fields: ctx, accountAddress, blockNumber -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) BalanceAt(ctx context.Context, accountAddress ADDR, blockNumber *big.Int) (*big.Int, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) BalanceAt(ctx context.Context, accountAddress ADDR, blockNumber *big.Int) (*big.Int, error) { ret := _m.Called(ctx, accountAddress, blockNumber) if len(ret) == 0 { @@ -52,7 +52,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // BatchCallContext provides a mock function with given fields: ctx, b -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) BatchCallContext(ctx context.Context, b []interface{}) error { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) BatchCallContext(ctx context.Context, b []BATCH_ELEM) error { ret := _m.Called(ctx, b) if len(ret) == 0 { @@ -60,7 +60,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, []interface{}) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, []BATCH_ELEM) error); ok { r0 = rf(ctx, b) } else { r0 = ret.Error(0) @@ -70,7 +70,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // BlockByHash provides a mock function with given fields: ctx, hash -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) BlockByHash(ctx context.Context, hash BLOCK_HASH) (HEAD, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) BlockByHash(ctx context.Context, hash BLOCK_HASH) (HEAD, error) { ret := _m.Called(ctx, hash) if len(ret) == 0 { @@ -98,7 +98,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // BlockByNumber provides a mock function with given fields: ctx, number -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) BlockByNumber(ctx context.Context, number *big.Int) (HEAD, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) BlockByNumber(ctx context.Context, number *big.Int) (HEAD, error) { ret := _m.Called(ctx, number) if len(ret) == 0 { @@ -126,7 +126,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // CallContext provides a mock function with given fields: ctx, result, method, args -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { var _ca []interface{} _ca = append(_ca, ctx, result, method) _ca = append(_ca, args...) @@ -147,7 +147,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // CallContract provides a mock function with given fields: ctx, msg, blockNumber -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) CallContract(ctx context.Context, msg interface{}, blockNumber *big.Int) ([]byte, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) CallContract(ctx context.Context, msg interface{}, blockNumber *big.Int) ([]byte, error) { ret := _m.Called(ctx, msg, blockNumber) if len(ret) == 0 { @@ -177,7 +177,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // ChainID provides a mock function with given fields: ctx -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) ChainID(ctx context.Context) (CHAIN_ID, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) ChainID(ctx context.Context) (CHAIN_ID, error) { ret := _m.Called(ctx) if len(ret) == 0 { @@ -205,7 +205,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // ClientVersion provides a mock function with given fields: _a0 -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) ClientVersion(_a0 context.Context) (string, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) ClientVersion(_a0 context.Context) (string, error) { ret := _m.Called(_a0) if len(ret) == 0 { @@ -233,12 +233,12 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // Close provides a mock function with given fields: -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) Close() { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Close() { _m.Called() } // CodeAt provides a mock function with given fields: ctx, account, blockNumber -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) CodeAt(ctx context.Context, account ADDR, blockNumber *big.Int) ([]byte, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) CodeAt(ctx context.Context, account ADDR, blockNumber *big.Int) ([]byte, error) { ret := _m.Called(ctx, account, blockNumber) if len(ret) == 0 { @@ -268,7 +268,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // Dial provides a mock function with given fields: ctx -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) Dial(ctx context.Context) error { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Dial(ctx context.Context) error { ret := _m.Called(ctx) if len(ret) == 0 { @@ -286,7 +286,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // DialHTTP provides a mock function with given fields: -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) DialHTTP() error { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) DialHTTP() error { ret := _m.Called() if len(ret) == 0 { @@ -304,12 +304,12 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // DisconnectAll provides a mock function with given fields: -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) DisconnectAll() { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) DisconnectAll() { _m.Called() } // EstimateGas provides a mock function with given fields: ctx, call -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) EstimateGas(ctx context.Context, call interface{}) (uint64, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) EstimateGas(ctx context.Context, call interface{}) (uint64, error) { ret := _m.Called(ctx, call) if len(ret) == 0 { @@ -337,7 +337,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // FilterEvents provides a mock function with given fields: ctx, query -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) FilterEvents(ctx context.Context, query EVENT_OPS) ([]EVENT, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) FilterEvents(ctx context.Context, query EVENT_OPS) ([]EVENT, error) { ret := _m.Called(ctx, query) if len(ret) == 0 { @@ -367,7 +367,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // LINKBalance provides a mock function with given fields: ctx, accountAddress, linkAddress -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) LINKBalance(ctx context.Context, accountAddress ADDR, linkAddress ADDR) (*assets.Link, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) LINKBalance(ctx context.Context, accountAddress ADDR, linkAddress ADDR) (*assets.Link, error) { ret := _m.Called(ctx, accountAddress, linkAddress) if len(ret) == 0 { @@ -397,7 +397,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // LatestBlockHeight provides a mock function with given fields: _a0 -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) LatestBlockHeight(_a0 context.Context) (*big.Int, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) LatestBlockHeight(_a0 context.Context) (*big.Int, error) { ret := _m.Called(_a0) if len(ret) == 0 { @@ -427,7 +427,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // PendingCallContract provides a mock function with given fields: ctx, msg -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) PendingCallContract(ctx context.Context, msg interface{}) ([]byte, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) PendingCallContract(ctx context.Context, msg interface{}) ([]byte, error) { ret := _m.Called(ctx, msg) if len(ret) == 0 { @@ -457,7 +457,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // PendingSequenceAt provides a mock function with given fields: ctx, addr -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) PendingSequenceAt(ctx context.Context, addr ADDR) (SEQ, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) PendingSequenceAt(ctx context.Context, addr ADDR) (SEQ, error) { ret := _m.Called(ctx, addr) if len(ret) == 0 { @@ -485,7 +485,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // SendEmptyTransaction provides a mock function with given fields: ctx, newTxAttempt, seq, gasLimit, fee, fromAddress -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) SendEmptyTransaction(ctx context.Context, newTxAttempt func(SEQ, uint32, FEE, ADDR) (interface{}, error), seq SEQ, gasLimit uint32, fee FEE, fromAddress ADDR) (string, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SendEmptyTransaction(ctx context.Context, newTxAttempt func(SEQ, uint32, FEE, ADDR) (interface{}, error), seq SEQ, gasLimit uint32, fee FEE, fromAddress ADDR) (string, error) { ret := _m.Called(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) if len(ret) == 0 { @@ -513,7 +513,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // SendTransaction provides a mock function with given fields: ctx, tx -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) SendTransaction(ctx context.Context, tx TX) error { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SendTransaction(ctx context.Context, tx TX) error { ret := _m.Called(ctx, tx) if len(ret) == 0 { @@ -531,7 +531,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // SequenceAt provides a mock function with given fields: ctx, accountAddress, blockNumber -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) SequenceAt(ctx context.Context, accountAddress ADDR, blockNumber *big.Int) (SEQ, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SequenceAt(ctx context.Context, accountAddress ADDR, blockNumber *big.Int) (SEQ, error) { ret := _m.Called(ctx, accountAddress, blockNumber) if len(ret) == 0 { @@ -559,12 +559,12 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // SetAliveLoopSub provides a mock function with given fields: _a0 -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) SetAliveLoopSub(_a0 types.Subscription) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SetAliveLoopSub(_a0 types.Subscription) { _m.Called(_a0) } // SimulateTransaction provides a mock function with given fields: ctx, tx -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) SimulateTransaction(ctx context.Context, tx TX) error { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SimulateTransaction(ctx context.Context, tx TX) error { ret := _m.Called(ctx, tx) if len(ret) == 0 { @@ -582,7 +582,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // Subscribe provides a mock function with given fields: ctx, channel, args -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) Subscribe(ctx context.Context, channel chan<- HEAD, args ...interface{}) (types.Subscription, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) Subscribe(ctx context.Context, channel chan<- HEAD, args ...interface{}) (types.Subscription, error) { var _ca []interface{} _ca = append(_ca, ctx, channel) _ca = append(_ca, args...) @@ -615,7 +615,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // SubscribersCount provides a mock function with given fields: -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) SubscribersCount() int32 { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) SubscribersCount() int32 { ret := _m.Called() if len(ret) == 0 { @@ -633,7 +633,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // TokenBalance provides a mock function with given fields: ctx, accountAddress, tokenAddress -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) TokenBalance(ctx context.Context, accountAddress ADDR, tokenAddress ADDR) (*big.Int, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) TokenBalance(ctx context.Context, accountAddress ADDR, tokenAddress ADDR) (*big.Int, error) { ret := _m.Called(ctx, accountAddress, tokenAddress) if len(ret) == 0 { @@ -663,7 +663,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // TransactionByHash provides a mock function with given fields: ctx, txHash -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) TransactionByHash(ctx context.Context, txHash TX_HASH) (TX, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) TransactionByHash(ctx context.Context, txHash TX_HASH) (TX, error) { ret := _m.Called(ctx, txHash) if len(ret) == 0 { @@ -691,7 +691,7 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // TransactionReceipt provides a mock function with given fields: ctx, txHash -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) TransactionReceipt(ctx context.Context, txHash TX_HASH) (TX_RECEIPT, error) { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) TransactionReceipt(ctx context.Context, txHash TX_HASH) (TX_RECEIPT, error) { ret := _m.Called(ctx, txHash) if len(ret) == 0 { @@ -719,17 +719,17 @@ func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS } // UnsubscribeAllExceptAliveLoop provides a mock function with given fields: -func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) UnsubscribeAllExceptAliveLoop() { +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]) UnsubscribeAllExceptAliveLoop() { _m.Called() } // newMockRPC creates a new instance of mockRPC. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. -func newMockRPC[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH]](t interface { +func newMockRPC[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], BATCH_ELEM interface{}](t interface { mock.TestingT Cleanup(func()) -}) *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD] { - mock := &mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]{} +}) *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM] { + mock := &mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM]{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) diff --git a/common/client/multi_node.go b/common/client/multi_node.go index ae9b3afd0d4..e86a7631982 100644 --- a/common/client/multi_node.go +++ b/common/client/multi_node.go @@ -50,7 +50,8 @@ type MultiNode[ TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], - RPC_CLIENT RPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD], + RPC_CLIENT RPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM], + BATCH_ELEM any, ] interface { clientAPI[ CHAIN_ID, @@ -64,12 +65,13 @@ type MultiNode[ TX_RECEIPT, FEE, HEAD, + BATCH_ELEM, ] Close() error NodeStates() map[string]string SelectNodeRPC() (RPC_CLIENT, error) - BatchCallContextAll(ctx context.Context, b []any) error + BatchCallContextAll(ctx context.Context, b []BATCH_ELEM) error ConfiguredChainID() CHAIN_ID IsL2() bool } @@ -86,7 +88,8 @@ type multiNode[ TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], - RPC_CLIENT RPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD], + RPC_CLIENT RPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM], + BATCH_ELEM any, ] struct { services.StateMachine nodes []Node[CHAIN_ID, HEAD, RPC_CLIENT] @@ -124,7 +127,8 @@ func NewMultiNode[ TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], - RPC_CLIENT RPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD], + RPC_CLIENT RPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, BATCH_ELEM], + BATCH_ELEM any, ]( lggr logger.Logger, selectionMode string, @@ -137,7 +141,7 @@ func NewMultiNode[ chainFamily string, classifySendTxError func(tx TX, err error) SendTxReturnCode, sendTxSoftTimeout time.Duration, -) MultiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT] { +) MultiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM] { nodeSelector := newNodeSelector(selectionMode, nodes) // Prometheus' default interval is 15s, set this to under 7.5s to avoid // aliasing (see: https://en.wikipedia.org/wiki/Nyquist_frequency) @@ -145,7 +149,7 @@ func NewMultiNode[ if sendTxSoftTimeout == 0 { sendTxSoftTimeout = QueryTimeout / 2 } - c := &multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]{ + c := &multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]{ nodes: nodes, sendonlys: sendonlys, chainID: chainID, @@ -171,7 +175,7 @@ func NewMultiNode[ // // Nodes handle their own redialing and runloops, so this function does not // return any error if the nodes aren't available -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) Dial(ctx context.Context) error { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) Dial(ctx context.Context) error { return c.StartOnce("MultiNode", func() (merr error) { if len(c.nodes) == 0 { return fmt.Errorf("no available nodes for chain %s", c.chainID.String()) @@ -218,7 +222,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP } // Close tears down the MultiNode and closes all nodes -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) Close() error { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) Close() error { return c.StopOnce("MultiNode", func() error { close(c.chStop) c.wg.Wait() @@ -229,7 +233,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP // SelectNodeRPC returns an RPC of an active node. If there are no active nodes it returns an error. // Call this method from your chain-specific client implementation to access any chain-specific rpc calls. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) SelectNodeRPC() (rpc RPC_CLIENT, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) SelectNodeRPC() (rpc RPC_CLIENT, err error) { n, err := c.selectNode() if err != nil { return rpc, err @@ -239,7 +243,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP } // selectNode returns the active Node, if it is still nodeStateAlive, otherwise it selects a new one from the NodeSelector. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) selectNode() (node Node[CHAIN_ID, HEAD, RPC_CLIENT], err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) selectNode() (node Node[CHAIN_ID, HEAD, RPC_CLIENT], err error) { c.activeMu.RLock() node = c.activeNode c.activeMu.RUnlock() @@ -269,7 +273,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP // nLiveNodes returns the number of currently alive nodes, as well as the highest block number and greatest total difficulty. // totalDifficulty will be 0 if all nodes return nil. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) nLiveNodes() (nLiveNodes int, blockNumber int64, totalDifficulty *big.Int) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) nLiveNodes() (nLiveNodes int, blockNumber int64, totalDifficulty *big.Int) { totalDifficulty = big.NewInt(0) for _, n := range c.nodes { if s, num, td := n.StateAndLatest(); s == nodeStateAlive { @@ -285,7 +289,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) checkLease() { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) checkLease() { bestNode := c.nodeSelector.Select() for _, n := range c.nodes { // Terminate client subscriptions. Services are responsible for reconnecting, which will be routed to the new @@ -303,7 +307,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP c.activeMu.Unlock() } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) checkLeaseLoop() { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) checkLeaseLoop() { defer c.wg.Done() c.leaseTicker = time.NewTicker(c.leaseDuration) defer c.leaseTicker.Stop() @@ -318,7 +322,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP } } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) runLoop() { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) runLoop() { defer c.wg.Done() c.report() @@ -336,7 +340,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP } } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) report() { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) report() { type nodeWithState struct { Node string State string @@ -371,7 +375,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP } // ClientAPI methods -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) BalanceAt(ctx context.Context, account ADDR, blockNumber *big.Int) (*big.Int, error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) BalanceAt(ctx context.Context, account ADDR, blockNumber *big.Int) (*big.Int, error) { n, err := c.selectNode() if err != nil { return nil, err @@ -379,7 +383,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().BalanceAt(ctx, account, blockNumber) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) BatchCallContext(ctx context.Context, b []any) error { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) BatchCallContext(ctx context.Context, b []BATCH_ELEM) error { n, err := c.selectNode() if err != nil { return err @@ -391,7 +395,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP // sendonlys. // CAUTION: This should only be used for mass re-transmitting transactions, it // might have unexpected effects to use it for anything else. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) BatchCallContextAll(ctx context.Context, b []any) error { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) BatchCallContextAll(ctx context.Context, b []BATCH_ELEM) error { var wg sync.WaitGroup defer wg.Wait() @@ -429,7 +433,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return main.RPC().BatchCallContext(ctx, b) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) BlockByHash(ctx context.Context, hash BLOCK_HASH) (h HEAD, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) BlockByHash(ctx context.Context, hash BLOCK_HASH) (h HEAD, err error) { n, err := c.selectNode() if err != nil { return h, err @@ -437,7 +441,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().BlockByHash(ctx, hash) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) BlockByNumber(ctx context.Context, number *big.Int) (h HEAD, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) BlockByNumber(ctx context.Context, number *big.Int) (h HEAD, err error) { n, err := c.selectNode() if err != nil { return h, err @@ -445,7 +449,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().BlockByNumber(ctx, number) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { n, err := c.selectNode() if err != nil { return err @@ -453,7 +457,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().CallContext(ctx, result, method, args...) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) CallContract( +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) CallContract( ctx context.Context, attempt interface{}, blockNumber *big.Int, @@ -465,7 +469,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().CallContract(ctx, attempt, blockNumber) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) PendingCallContract( +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) PendingCallContract( ctx context.Context, attempt interface{}, ) (rpcErr []byte, extractErr error) { @@ -478,7 +482,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP // ChainID makes a direct RPC call. In most cases it should be better to use the configured chain id instead by // calling ConfiguredChainID. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) ChainID(ctx context.Context) (id CHAIN_ID, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) ChainID(ctx context.Context) (id CHAIN_ID, err error) { n, err := c.selectNode() if err != nil { return id, err @@ -486,11 +490,11 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().ChainID(ctx) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) ChainType() config.ChainType { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) ChainType() config.ChainType { return c.chainType } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) CodeAt(ctx context.Context, account ADDR, blockNumber *big.Int) (code []byte, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) CodeAt(ctx context.Context, account ADDR, blockNumber *big.Int) (code []byte, err error) { n, err := c.selectNode() if err != nil { return code, err @@ -498,11 +502,11 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().CodeAt(ctx, account, blockNumber) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) ConfiguredChainID() CHAIN_ID { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) ConfiguredChainID() CHAIN_ID { return c.chainID } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) EstimateGas(ctx context.Context, call any) (gas uint64, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) EstimateGas(ctx context.Context, call any) (gas uint64, err error) { n, err := c.selectNode() if err != nil { return gas, err @@ -510,7 +514,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().EstimateGas(ctx, call) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) FilterEvents(ctx context.Context, query EVENT_OPS) (e []EVENT, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) FilterEvents(ctx context.Context, query EVENT_OPS) (e []EVENT, err error) { n, err := c.selectNode() if err != nil { return e, err @@ -518,11 +522,11 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().FilterEvents(ctx, query) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) IsL2() bool { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) IsL2() bool { return c.ChainType().IsL2() } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) LatestBlockHeight(ctx context.Context) (h *big.Int, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) LatestBlockHeight(ctx context.Context) (h *big.Int, err error) { n, err := c.selectNode() if err != nil { return h, err @@ -530,7 +534,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().LatestBlockHeight(ctx) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) LINKBalance(ctx context.Context, accountAddress ADDR, linkAddress ADDR) (b *assets.Link, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) LINKBalance(ctx context.Context, accountAddress ADDR, linkAddress ADDR) (b *assets.Link, err error) { n, err := c.selectNode() if err != nil { return b, err @@ -538,7 +542,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().LINKBalance(ctx, accountAddress, linkAddress) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) NodeStates() (states map[string]string) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) NodeStates() (states map[string]string) { states = make(map[string]string) for _, n := range c.nodes { states[n.Name()] = n.State().String() @@ -549,7 +553,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) PendingSequenceAt(ctx context.Context, addr ADDR) (s SEQ, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) PendingSequenceAt(ctx context.Context, addr ADDR) (s SEQ, err error) { n, err := c.selectNode() if err != nil { return s, err @@ -557,7 +561,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().PendingSequenceAt(ctx, addr) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) SendEmptyTransaction( +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) SendEmptyTransaction( ctx context.Context, newTxAttempt func(seq SEQ, feeLimit uint32, fee FEE, fromAddress ADDR) (attempt any, err error), seq SEQ, @@ -577,7 +581,7 @@ type sendTxResult struct { ResultCode SendTxReturnCode } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) broadcastTxAsync(ctx context.Context, +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) broadcastTxAsync(ctx context.Context, n SendOnlyNode[CHAIN_ID, RPC_CLIENT], tx TX) sendTxResult { txErr := n.RPC().SendTransaction(ctx, tx) c.lggr.Debugw("Node sent transaction", "name", n.String(), "tx", tx, "err", txErr) @@ -590,7 +594,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP } // collectTxResults - refer to SendTransaction comment for implementation details, -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) collectTxResults(ctx context.Context, tx TX, healthyNodesNum int, txResults <-chan sendTxResult) error { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) collectTxResults(ctx context.Context, tx TX, healthyNodesNum int, txResults <-chan sendTxResult) error { if healthyNodesNum == 0 { return ErroringNodeError } @@ -633,7 +637,7 @@ loop: } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) reportSendTxAnomalies(tx TX, txResults <-chan sendTxResult) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) reportSendTxAnomalies(tx TX, txResults <-chan sendTxResult) { defer c.wg.Done() resultsByCode := map[SendTxReturnCode][]error{} // txResults eventually will be closed @@ -698,7 +702,7 @@ const sendTxQuorum = 0.7 // * If there is at least one terminal error - returns terminal error // * If there is both success and terminal error - returns success and reports invariant violation // * Otherwise, returns any (effectively random) of the errors. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) SendTransaction(ctx context.Context, tx TX) error { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) SendTransaction(ctx context.Context, tx TX) error { if len(c.nodes) == 0 { return ErroringNodeError } @@ -768,7 +772,7 @@ func findFirstIn[K comparable, V any](set map[K]V, keys []K) (V, bool) { return v, false } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) SequenceAt(ctx context.Context, account ADDR, blockNumber *big.Int) (s SEQ, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) SequenceAt(ctx context.Context, account ADDR, blockNumber *big.Int) (s SEQ, err error) { n, err := c.selectNode() if err != nil { return s, err @@ -776,7 +780,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().SequenceAt(ctx, account, blockNumber) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) SimulateTransaction(ctx context.Context, tx TX) error { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) SimulateTransaction(ctx context.Context, tx TX) error { n, err := c.selectNode() if err != nil { return err @@ -784,7 +788,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().SimulateTransaction(ctx, tx) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) Subscribe(ctx context.Context, channel chan<- HEAD, args ...interface{}) (s types.Subscription, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) Subscribe(ctx context.Context, channel chan<- HEAD, args ...interface{}) (s types.Subscription, err error) { n, err := c.selectNode() if err != nil { return s, err @@ -792,7 +796,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().Subscribe(ctx, channel, args...) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) TokenBalance(ctx context.Context, account ADDR, tokenAddr ADDR) (b *big.Int, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) TokenBalance(ctx context.Context, account ADDR, tokenAddr ADDR) (b *big.Int, err error) { n, err := c.selectNode() if err != nil { return b, err @@ -800,7 +804,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().TokenBalance(ctx, account, tokenAddr) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) TransactionByHash(ctx context.Context, txHash TX_HASH) (tx TX, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) TransactionByHash(ctx context.Context, txHash TX_HASH) (tx TX, err error) { n, err := c.selectNode() if err != nil { return tx, err @@ -808,7 +812,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP return n.RPC().TransactionByHash(ctx, txHash) } -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) TransactionReceipt(ctx context.Context, txHash TX_HASH) (txr TX_RECEIPT, err error) { +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT, BATCH_ELEM]) TransactionReceipt(ctx context.Context, txHash TX_HASH) (txr TX_RECEIPT, err error) { n, err := c.selectNode() if err != nil { return txr, err diff --git a/common/client/multi_node_test.go b/common/client/multi_node_test.go index dabaae57c5d..43e4127556a 100644 --- a/common/client/multi_node_test.go +++ b/common/client/multi_node_test.go @@ -22,11 +22,11 @@ import ( ) type multiNodeRPCClient RPC[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, - types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable]] + types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], any] type testMultiNode struct { *multiNode[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, - types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], multiNodeRPCClient] + types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], multiNodeRPCClient, any] } type multiNodeOpts struct { @@ -49,19 +49,19 @@ func newTestMultiNode(t *testing.T, opts multiNodeOpts) testMultiNode { } result := NewMultiNode[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, - types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], multiNodeRPCClient](opts.logger, + types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], multiNodeRPCClient, any](opts.logger, opts.selectionMode, opts.leaseDuration, opts.noNewHeadsThreshold, opts.nodes, opts.sendonlys, opts.chainID, opts.chainType, opts.chainFamily, opts.classifySendTxError, opts.sendTxSoftTimeout) return testMultiNode{ result.(*multiNode[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, - types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], multiNodeRPCClient]), + types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], multiNodeRPCClient, any]), } } func newMultiNodeRPCClient(t *testing.T) *mockRPC[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, - types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable]] { + types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], any] { return newMockRPC[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, - types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable]](t) + types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], any](t) } func newHealthyNode(t *testing.T, chainID types.ID) *mockNode[types.ID, types.Head[Hashable], multiNodeRPCClient] { diff --git a/common/client/types.go b/common/client/types.go index fe9e4d7d482..c05ab238f57 100644 --- a/common/client/types.go +++ b/common/client/types.go @@ -24,7 +24,7 @@ type RPC[ TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], - + BATCH_ELEM any, ] interface { NodeClient[ CHAIN_ID, @@ -42,6 +42,7 @@ type RPC[ TX_RECEIPT, FEE, HEAD, + BATCH_ELEM, ] } @@ -84,6 +85,7 @@ type clientAPI[ TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH], + BATCH_ELEM any, ] interface { connection[CHAIN_ID, HEAD] @@ -118,7 +120,7 @@ type clientAPI[ FilterEvents(ctx context.Context, query EVENT_OPS) ([]EVENT, error) // Misc - BatchCallContext(ctx context.Context, b []any) error + BatchCallContext(ctx context.Context, b []BATCH_ELEM) error CallContract( ctx context.Context, msg interface{}, diff --git a/core/chains/evm/client/chain_client.go b/core/chains/evm/client/chain_client.go index b16054b69a8..33deed1df32 100644 --- a/core/chains/evm/client/chain_client.go +++ b/core/chains/evm/client/chain_client.go @@ -36,6 +36,7 @@ type chainClient struct { *assets.Wei, *evmtypes.Head, RPCCLient, + rpc.BatchElem, ] logger logger.SugaredLogger } @@ -88,20 +89,16 @@ func (c *chainClient) BalanceAt(ctx context.Context, account common.Address, blo return c.multiNode.BalanceAt(ctx, account, blockNumber) } +// Request specific errors for batch calls are returned to the individual BatchElem. +// Ensure the same BatchElem slice provided by the caller is passed through the call stack +// to ensure the caller has access to the errors. func (c *chainClient) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error { - batch := make([]any, len(b)) - for i, arg := range b { - batch[i] = any(arg) - } - return c.multiNode.BatchCallContext(ctx, batch) + return c.multiNode.BatchCallContext(ctx, b) } +// Similar to BatchCallContext, ensure the provided BatchElem slice is passed through func (c *chainClient) BatchCallContextAll(ctx context.Context, b []rpc.BatchElem) error { - batch := make([]any, len(b)) - for i, arg := range b { - batch[i] = any(arg) - } - return c.multiNode.BatchCallContextAll(ctx, batch) + return c.multiNode.BatchCallContextAll(ctx, b) } // TODO-1663: return custom Block type instead of geth's once client.go is deprecated. diff --git a/core/chains/evm/client/chain_client_test.go b/core/chains/evm/client/chain_client_test.go new file mode 100644 index 00000000000..6af9a67ee1c --- /dev/null +++ b/core/chains/evm/client/chain_client_test.go @@ -0,0 +1,70 @@ +package client_test + +import ( + "errors" + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rpc" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + commonclient "github.com/smartcontractkit/chainlink/v2/common/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" +) + +func newMockRpc(t *testing.T) *mocks.RPCCLient { + mockRpc := mocks.NewRPCCLient(t) + mockRpc.On("Dial", mock.Anything).Return(nil).Once() + mockRpc.On("Close").Return(nil).Once() + mockRpc.On("ChainID", mock.Anything).Return(testutils.FixtureChainID, nil).Once() + mockRpc.On("Subscribe", mock.Anything, mock.Anything, mock.Anything).Return(client.NewMockSubscription(), nil).Once() + mockRpc.On("SetAliveLoopSub", mock.Anything).Return().Once() + return mockRpc +} + +func TestChainClient_BatchCallContext(t *testing.T) { + t.Parallel() + + t.Run("batch requests return errors", func(t *testing.T) { + ctx := testutils.Context(t) + rpcError := errors.New("something went wrong") + blockNumResp := "" + blockNum := hexutil.EncodeBig(big.NewInt(42)) + b := []rpc.BatchElem{ + { + Method: "eth_getBlockByNumber", + Args: []interface{}{blockNum, true}, + Result: &types.Block{}, + }, + { + Method: "eth_blockNumber", + Result: &blockNumResp, + }, + } + + mockRpc := newMockRpc(t) + mockRpc.On("BatchCallContext", mock.Anything, b).Run(func(args mock.Arguments) { + reqs := args.Get(1).([]rpc.BatchElem) + for i := 0; i < len(reqs); i++ { + elem := &reqs[i] + elem.Error = rpcError + } + }).Return(nil).Once() + + client := client.NewChainClientWithMockedRpc(t, commonclient.NodeSelectionModeRoundRobin, time.Second*0, time.Second*0, testutils.FixtureChainID, mockRpc) + err := client.Dial(ctx) + require.NoError(t, err) + + err = client.BatchCallContext(ctx, b) + require.NoError(t, err) + for _, elem := range b { + require.ErrorIs(t, rpcError, elem.Error) + } + }) +} diff --git a/core/chains/evm/client/chain_id_sub_test.go b/core/chains/evm/client/chain_id_sub_test.go index c71b45c489e..f959376acca 100644 --- a/core/chains/evm/client/chain_id_sub_test.go +++ b/core/chains/evm/client/chain_id_sub_test.go @@ -11,22 +11,6 @@ import ( ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" ) -type mockSubscription struct { - unsubscribed bool - Errors chan error -} - -func newMockSubscription() *mockSubscription { - return &mockSubscription{Errors: make(chan error)} -} - -func (mes *mockSubscription) Err() <-chan error { return mes.Errors } - -func (mes *mockSubscription) Unsubscribe() { - mes.unsubscribed = true - close(mes.Errors) -} - func TestChainIDSubForwarder(t *testing.T) { t.Parallel() @@ -37,7 +21,7 @@ func TestChainIDSubForwarder(t *testing.T) { ch := make(chan *evmtypes.Head) forwarder := newChainIDSubForwarder(chainID, ch) - sub := newMockSubscription() + sub := NewMockSubscription() err := forwarder.start(sub, nil) assert.NoError(t, err) forwarder.Unsubscribe() @@ -54,7 +38,7 @@ func TestChainIDSubForwarder(t *testing.T) { ch := make(chan *evmtypes.Head) forwarder := newChainIDSubForwarder(chainID, ch) - sub := newMockSubscription() + sub := NewMockSubscription() err := forwarder.start(sub, nil) assert.NoError(t, err) sub.Errors <- errors.New("boo") @@ -72,7 +56,7 @@ func TestChainIDSubForwarder(t *testing.T) { ch := make(chan *evmtypes.Head) forwarder := newChainIDSubForwarder(chainID, ch) - sub := newMockSubscription() + sub := NewMockSubscription() err := forwarder.start(sub, nil) assert.NoError(t, err) forwarder.srcCh <- &evmtypes.Head{} @@ -90,7 +74,7 @@ func TestChainIDSubForwarder(t *testing.T) { ch := make(chan *evmtypes.Head) forwarder := newChainIDSubForwarder(chainID, ch) - sub := newMockSubscription() + sub := NewMockSubscription() errIn := errors.New("foo") errOut := forwarder.start(sub, errIn) assert.Equal(t, errIn, errOut) @@ -101,7 +85,7 @@ func TestChainIDSubForwarder(t *testing.T) { ch := make(chan *evmtypes.Head) forwarder := newChainIDSubForwarder(chainID, ch) - sub := newMockSubscription() + sub := NewMockSubscription() err := forwarder.start(sub, nil) assert.NoError(t, err) diff --git a/core/chains/evm/client/helpers_test.go b/core/chains/evm/client/helpers_test.go index c2f60e13f55..467195e11e8 100644 --- a/core/chains/evm/client/helpers_test.go +++ b/core/chains/evm/client/helpers_test.go @@ -127,6 +127,32 @@ func NewChainClientWithEmptyNode( return c } +func NewChainClientWithMockedRpc( + t *testing.T, + selectionMode string, + leaseDuration time.Duration, + noNewHeadsThreshold time.Duration, + chainID *big.Int, + rpc RPCCLient, +) Client { + + lggr := logger.Test(t) + + var chainType commonconfig.ChainType + + cfg := TestNodePoolConfig{ + NodeSelectionMode: NodeSelectionMode_RoundRobin, + } + parsed, _ := url.ParseRequestURI("ws://test") + + n := commonclient.NewNode[*big.Int, *evmtypes.Head, RPCCLient]( + cfg, noNewHeadsThreshold, lggr, *parsed, nil, "eth-primary-node-0", 1, chainID, 1, rpc, "EVM") + primaries := []commonclient.Node[*big.Int, *evmtypes.Head, RPCCLient]{n} + c := NewChainClient(lggr, selectionMode, leaseDuration, noNewHeadsThreshold, primaries, nil, chainID, chainType) + t.Cleanup(c.Close) + return c +} + type TestableSendOnlyNode interface { SendOnlyNode SetEthClient(newBatchSender BatchSender, newSender TxSender) @@ -137,3 +163,19 @@ const HeadResult = `{"difficulty":"0xf3a00","extraData":"0xd88301050384676574688 func IsDialed(s SendOnlyNode) bool { return s.(*sendOnlyNode).dialed } + +type mockSubscription struct { + unsubscribed bool + Errors chan error +} + +func NewMockSubscription() *mockSubscription { + return &mockSubscription{Errors: make(chan error)} +} + +func (mes *mockSubscription) Err() <-chan error { return mes.Errors } + +func (mes *mockSubscription) Unsubscribe() { + mes.unsubscribed = true + close(mes.Errors) +} diff --git a/core/chains/evm/client/mocks/rpc_client.go b/core/chains/evm/client/mocks/rpc_client.go new file mode 100644 index 00000000000..186fd2534e3 --- /dev/null +++ b/core/chains/evm/client/mocks/rpc_client.go @@ -0,0 +1,1028 @@ +// Code generated by mockery v2.38.0. DO NOT EDIT. + +package mocks + +import ( + big "math/big" + + assets "github.com/smartcontractkit/chainlink-common/pkg/assets" + + common "github.com/ethereum/go-ethereum/common" + + commontypes "github.com/smartcontractkit/chainlink/v2/common/types" + + context "context" + + coretypes "github.com/ethereum/go-ethereum/core/types" + + ethereum "github.com/ethereum/go-ethereum" + + evmassets "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + + mock "github.com/stretchr/testify/mock" + + rpc "github.com/ethereum/go-ethereum/rpc" + + types "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" +) + +// RPCCLient is an autogenerated mock type for the RPCCLient type +type RPCCLient struct { + mock.Mock +} + +// BalanceAt provides a mock function with given fields: ctx, accountAddress, blockNumber +func (_m *RPCCLient) BalanceAt(ctx context.Context, accountAddress common.Address, blockNumber *big.Int) (*big.Int, error) { + ret := _m.Called(ctx, accountAddress, blockNumber) + + if len(ret) == 0 { + panic("no return value specified for BalanceAt") + } + + var r0 *big.Int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) (*big.Int, error)); ok { + return rf(ctx, accountAddress, blockNumber) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) *big.Int); ok { + r0 = rf(ctx, accountAddress, blockNumber) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address, *big.Int) error); ok { + r1 = rf(ctx, accountAddress, blockNumber) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// BatchCallContext provides a mock function with given fields: ctx, b +func (_m *RPCCLient) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error { + ret := _m.Called(ctx, b) + + if len(ret) == 0 { + panic("no return value specified for BatchCallContext") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, []rpc.BatchElem) error); ok { + r0 = rf(ctx, b) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// BlockByHash provides a mock function with given fields: ctx, hash +func (_m *RPCCLient) BlockByHash(ctx context.Context, hash common.Hash) (*types.Head, error) { + ret := _m.Called(ctx, hash) + + if len(ret) == 0 { + panic("no return value specified for BlockByHash") + } + + var r0 *types.Head + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*types.Head, error)); ok { + return rf(ctx, hash) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *types.Head); ok { + r0 = rf(ctx, hash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Head) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { + r1 = rf(ctx, hash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// BlockByHashGeth provides a mock function with given fields: ctx, hash +func (_m *RPCCLient) BlockByHashGeth(ctx context.Context, hash common.Hash) (*coretypes.Block, error) { + ret := _m.Called(ctx, hash) + + if len(ret) == 0 { + panic("no return value specified for BlockByHashGeth") + } + + var r0 *coretypes.Block + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*coretypes.Block, error)); ok { + return rf(ctx, hash) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *coretypes.Block); ok { + r0 = rf(ctx, hash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*coretypes.Block) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { + r1 = rf(ctx, hash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// BlockByNumber provides a mock function with given fields: ctx, number +func (_m *RPCCLient) BlockByNumber(ctx context.Context, number *big.Int) (*types.Head, error) { + ret := _m.Called(ctx, number) + + if len(ret) == 0 { + panic("no return value specified for BlockByNumber") + } + + var r0 *types.Head + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*types.Head, error)); ok { + return rf(ctx, number) + } + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) *types.Head); ok { + r0 = rf(ctx, number) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Head) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *big.Int) error); ok { + r1 = rf(ctx, number) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// BlockByNumberGeth provides a mock function with given fields: ctx, number +func (_m *RPCCLient) BlockByNumberGeth(ctx context.Context, number *big.Int) (*coretypes.Block, error) { + ret := _m.Called(ctx, number) + + if len(ret) == 0 { + panic("no return value specified for BlockByNumberGeth") + } + + var r0 *coretypes.Block + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*coretypes.Block, error)); ok { + return rf(ctx, number) + } + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) *coretypes.Block); ok { + r0 = rf(ctx, number) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*coretypes.Block) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *big.Int) error); ok { + r1 = rf(ctx, number) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// CallContext provides a mock function with given fields: ctx, result, method, args +func (_m *RPCCLient) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { + var _ca []interface{} + _ca = append(_ca, ctx, result, method) + _ca = append(_ca, args...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for CallContext") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, interface{}, string, ...interface{}) error); ok { + r0 = rf(ctx, result, method, args...) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CallContract provides a mock function with given fields: ctx, msg, blockNumber +func (_m *RPCCLient) CallContract(ctx context.Context, msg interface{}, blockNumber *big.Int) ([]byte, error) { + ret := _m.Called(ctx, msg, blockNumber) + + if len(ret) == 0 { + panic("no return value specified for CallContract") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, interface{}, *big.Int) ([]byte, error)); ok { + return rf(ctx, msg, blockNumber) + } + if rf, ok := ret.Get(0).(func(context.Context, interface{}, *big.Int) []byte); ok { + r0 = rf(ctx, msg, blockNumber) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, interface{}, *big.Int) error); ok { + r1 = rf(ctx, msg, blockNumber) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ChainID provides a mock function with given fields: ctx +func (_m *RPCCLient) ChainID(ctx context.Context) (*big.Int, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for ChainID") + } + + var r0 *big.Int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (*big.Int, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) *big.Int); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ClientVersion provides a mock function with given fields: _a0 +func (_m *RPCCLient) ClientVersion(_a0 context.Context) (string, error) { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for ClientVersion") + } + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (string, error)); ok { + return rf(_a0) + } + if rf, ok := ret.Get(0).(func(context.Context) string); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(_a0) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Close provides a mock function with given fields: +func (_m *RPCCLient) Close() { + _m.Called() +} + +// CodeAt provides a mock function with given fields: ctx, account, blockNumber +func (_m *RPCCLient) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) { + ret := _m.Called(ctx, account, blockNumber) + + if len(ret) == 0 { + panic("no return value specified for CodeAt") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) ([]byte, error)); ok { + return rf(ctx, account, blockNumber) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) []byte); ok { + r0 = rf(ctx, account, blockNumber) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address, *big.Int) error); ok { + r1 = rf(ctx, account, blockNumber) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Dial provides a mock function with given fields: ctx +func (_m *RPCCLient) Dial(ctx context.Context) error { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for Dial") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DialHTTP provides a mock function with given fields: +func (_m *RPCCLient) DialHTTP() error { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for DialHTTP") + } + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DisconnectAll provides a mock function with given fields: +func (_m *RPCCLient) DisconnectAll() { + _m.Called() +} + +// EstimateGas provides a mock function with given fields: ctx, call +func (_m *RPCCLient) EstimateGas(ctx context.Context, call interface{}) (uint64, error) { + ret := _m.Called(ctx, call) + + if len(ret) == 0 { + panic("no return value specified for EstimateGas") + } + + var r0 uint64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, interface{}) (uint64, error)); ok { + return rf(ctx, call) + } + if rf, ok := ret.Get(0).(func(context.Context, interface{}) uint64); ok { + r0 = rf(ctx, call) + } else { + r0 = ret.Get(0).(uint64) + } + + if rf, ok := ret.Get(1).(func(context.Context, interface{}) error); ok { + r1 = rf(ctx, call) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FilterEvents provides a mock function with given fields: ctx, query +func (_m *RPCCLient) FilterEvents(ctx context.Context, query ethereum.FilterQuery) ([]coretypes.Log, error) { + ret := _m.Called(ctx, query) + + if len(ret) == 0 { + panic("no return value specified for FilterEvents") + } + + var r0 []coretypes.Log + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery) ([]coretypes.Log, error)); ok { + return rf(ctx, query) + } + if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery) []coretypes.Log); ok { + r0 = rf(ctx, query) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]coretypes.Log) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, ethereum.FilterQuery) error); ok { + r1 = rf(ctx, query) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// HeaderByHash provides a mock function with given fields: ctx, h +func (_m *RPCCLient) HeaderByHash(ctx context.Context, h common.Hash) (*coretypes.Header, error) { + ret := _m.Called(ctx, h) + + if len(ret) == 0 { + panic("no return value specified for HeaderByHash") + } + + var r0 *coretypes.Header + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*coretypes.Header, error)); ok { + return rf(ctx, h) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *coretypes.Header); ok { + r0 = rf(ctx, h) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*coretypes.Header) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { + r1 = rf(ctx, h) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// HeaderByNumber provides a mock function with given fields: ctx, n +func (_m *RPCCLient) HeaderByNumber(ctx context.Context, n *big.Int) (*coretypes.Header, error) { + ret := _m.Called(ctx, n) + + if len(ret) == 0 { + panic("no return value specified for HeaderByNumber") + } + + var r0 *coretypes.Header + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (*coretypes.Header, error)); ok { + return rf(ctx, n) + } + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) *coretypes.Header); ok { + r0 = rf(ctx, n) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*coretypes.Header) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *big.Int) error); ok { + r1 = rf(ctx, n) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// LINKBalance provides a mock function with given fields: ctx, accountAddress, linkAddress +func (_m *RPCCLient) LINKBalance(ctx context.Context, accountAddress common.Address, linkAddress common.Address) (*assets.Link, error) { + ret := _m.Called(ctx, accountAddress, linkAddress) + + if len(ret) == 0 { + panic("no return value specified for LINKBalance") + } + + var r0 *assets.Link + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address, common.Address) (*assets.Link, error)); ok { + return rf(ctx, accountAddress, linkAddress) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, common.Address) *assets.Link); ok { + r0 = rf(ctx, accountAddress, linkAddress) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*assets.Link) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address, common.Address) error); ok { + r1 = rf(ctx, accountAddress, linkAddress) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// LatestBlockHeight provides a mock function with given fields: _a0 +func (_m *RPCCLient) LatestBlockHeight(_a0 context.Context) (*big.Int, error) { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for LatestBlockHeight") + } + + var r0 *big.Int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (*big.Int, error)); ok { + return rf(_a0) + } + if rf, ok := ret.Get(0).(func(context.Context) *big.Int); ok { + r0 = rf(_a0) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(_a0) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PendingCallContract provides a mock function with given fields: ctx, msg +func (_m *RPCCLient) PendingCallContract(ctx context.Context, msg interface{}) ([]byte, error) { + ret := _m.Called(ctx, msg) + + if len(ret) == 0 { + panic("no return value specified for PendingCallContract") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, interface{}) ([]byte, error)); ok { + return rf(ctx, msg) + } + if rf, ok := ret.Get(0).(func(context.Context, interface{}) []byte); ok { + r0 = rf(ctx, msg) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, interface{}) error); ok { + r1 = rf(ctx, msg) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PendingCodeAt provides a mock function with given fields: ctx, account +func (_m *RPCCLient) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) { + ret := _m.Called(ctx, account) + + if len(ret) == 0 { + panic("no return value specified for PendingCodeAt") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address) ([]byte, error)); ok { + return rf(ctx, account) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address) []byte); ok { + r0 = rf(ctx, account) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address) error); ok { + r1 = rf(ctx, account) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PendingSequenceAt provides a mock function with given fields: ctx, addr +func (_m *RPCCLient) PendingSequenceAt(ctx context.Context, addr common.Address) (types.Nonce, error) { + ret := _m.Called(ctx, addr) + + if len(ret) == 0 { + panic("no return value specified for PendingSequenceAt") + } + + var r0 types.Nonce + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address) (types.Nonce, error)); ok { + return rf(ctx, addr) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address) types.Nonce); ok { + r0 = rf(ctx, addr) + } else { + r0 = ret.Get(0).(types.Nonce) + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address) error); ok { + r1 = rf(ctx, addr) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SendEmptyTransaction provides a mock function with given fields: ctx, newTxAttempt, seq, gasLimit, fee, fromAddress +func (_m *RPCCLient) SendEmptyTransaction(ctx context.Context, newTxAttempt func(types.Nonce, uint32, *evmassets.Wei, common.Address) (interface{}, error), seq types.Nonce, gasLimit uint32, fee *evmassets.Wei, fromAddress common.Address) (string, error) { + ret := _m.Called(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) + + if len(ret) == 0 { + panic("no return value specified for SendEmptyTransaction") + } + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, func(types.Nonce, uint32, *evmassets.Wei, common.Address) (interface{}, error), types.Nonce, uint32, *evmassets.Wei, common.Address) (string, error)); ok { + return rf(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) + } + if rf, ok := ret.Get(0).(func(context.Context, func(types.Nonce, uint32, *evmassets.Wei, common.Address) (interface{}, error), types.Nonce, uint32, *evmassets.Wei, common.Address) string); ok { + r0 = rf(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(context.Context, func(types.Nonce, uint32, *evmassets.Wei, common.Address) (interface{}, error), types.Nonce, uint32, *evmassets.Wei, common.Address) error); ok { + r1 = rf(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SendTransaction provides a mock function with given fields: ctx, tx +func (_m *RPCCLient) SendTransaction(ctx context.Context, tx *coretypes.Transaction) error { + ret := _m.Called(ctx, tx) + + if len(ret) == 0 { + panic("no return value specified for SendTransaction") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *coretypes.Transaction) error); ok { + r0 = rf(ctx, tx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// SequenceAt provides a mock function with given fields: ctx, accountAddress, blockNumber +func (_m *RPCCLient) SequenceAt(ctx context.Context, accountAddress common.Address, blockNumber *big.Int) (types.Nonce, error) { + ret := _m.Called(ctx, accountAddress, blockNumber) + + if len(ret) == 0 { + panic("no return value specified for SequenceAt") + } + + var r0 types.Nonce + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) (types.Nonce, error)); ok { + return rf(ctx, accountAddress, blockNumber) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, *big.Int) types.Nonce); ok { + r0 = rf(ctx, accountAddress, blockNumber) + } else { + r0 = ret.Get(0).(types.Nonce) + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address, *big.Int) error); ok { + r1 = rf(ctx, accountAddress, blockNumber) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SetAliveLoopSub provides a mock function with given fields: _a0 +func (_m *RPCCLient) SetAliveLoopSub(_a0 commontypes.Subscription) { + _m.Called(_a0) +} + +// SimulateTransaction provides a mock function with given fields: ctx, tx +func (_m *RPCCLient) SimulateTransaction(ctx context.Context, tx *coretypes.Transaction) error { + ret := _m.Called(ctx, tx) + + if len(ret) == 0 { + panic("no return value specified for SimulateTransaction") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *coretypes.Transaction) error); ok { + r0 = rf(ctx, tx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Subscribe provides a mock function with given fields: ctx, channel, args +func (_m *RPCCLient) Subscribe(ctx context.Context, channel chan<- *types.Head, args ...interface{}) (commontypes.Subscription, error) { + var _ca []interface{} + _ca = append(_ca, ctx, channel) + _ca = append(_ca, args...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for Subscribe") + } + + var r0 commontypes.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, chan<- *types.Head, ...interface{}) (commontypes.Subscription, error)); ok { + return rf(ctx, channel, args...) + } + if rf, ok := ret.Get(0).(func(context.Context, chan<- *types.Head, ...interface{}) commontypes.Subscription); ok { + r0 = rf(ctx, channel, args...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(commontypes.Subscription) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, chan<- *types.Head, ...interface{}) error); ok { + r1 = rf(ctx, channel, args...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SubscribeFilterLogs provides a mock function with given fields: ctx, q, ch +func (_m *RPCCLient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- coretypes.Log) (ethereum.Subscription, error) { + ret := _m.Called(ctx, q, ch) + + if len(ret) == 0 { + panic("no return value specified for SubscribeFilterLogs") + } + + var r0 ethereum.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery, chan<- coretypes.Log) (ethereum.Subscription, error)); ok { + return rf(ctx, q, ch) + } + if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery, chan<- coretypes.Log) ethereum.Subscription); ok { + r0 = rf(ctx, q, ch) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(ethereum.Subscription) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, ethereum.FilterQuery, chan<- coretypes.Log) error); ok { + r1 = rf(ctx, q, ch) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SubscribersCount provides a mock function with given fields: +func (_m *RPCCLient) SubscribersCount() int32 { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for SubscribersCount") + } + + var r0 int32 + if rf, ok := ret.Get(0).(func() int32); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int32) + } + + return r0 +} + +// SuggestGasPrice provides a mock function with given fields: ctx +func (_m *RPCCLient) SuggestGasPrice(ctx context.Context) (*big.Int, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for SuggestGasPrice") + } + + var r0 *big.Int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (*big.Int, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) *big.Int); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SuggestGasTipCap provides a mock function with given fields: ctx +func (_m *RPCCLient) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for SuggestGasTipCap") + } + + var r0 *big.Int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (*big.Int, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) *big.Int); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// TokenBalance provides a mock function with given fields: ctx, accountAddress, tokenAddress +func (_m *RPCCLient) TokenBalance(ctx context.Context, accountAddress common.Address, tokenAddress common.Address) (*big.Int, error) { + ret := _m.Called(ctx, accountAddress, tokenAddress) + + if len(ret) == 0 { + panic("no return value specified for TokenBalance") + } + + var r0 *big.Int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address, common.Address) (*big.Int, error)); ok { + return rf(ctx, accountAddress, tokenAddress) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, common.Address) *big.Int); ok { + r0 = rf(ctx, accountAddress, tokenAddress) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address, common.Address) error); ok { + r1 = rf(ctx, accountAddress, tokenAddress) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// TransactionByHash provides a mock function with given fields: ctx, txHash +func (_m *RPCCLient) TransactionByHash(ctx context.Context, txHash common.Hash) (*coretypes.Transaction, error) { + ret := _m.Called(ctx, txHash) + + if len(ret) == 0 { + panic("no return value specified for TransactionByHash") + } + + var r0 *coretypes.Transaction + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*coretypes.Transaction, error)); ok { + return rf(ctx, txHash) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *coretypes.Transaction); ok { + r0 = rf(ctx, txHash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*coretypes.Transaction) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { + r1 = rf(ctx, txHash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// TransactionReceipt provides a mock function with given fields: ctx, txHash +func (_m *RPCCLient) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { + ret := _m.Called(ctx, txHash) + + if len(ret) == 0 { + panic("no return value specified for TransactionReceipt") + } + + var r0 *types.Receipt + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*types.Receipt, error)); ok { + return rf(ctx, txHash) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *types.Receipt); ok { + r0 = rf(ctx, txHash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Receipt) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { + r1 = rf(ctx, txHash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// TransactionReceiptGeth provides a mock function with given fields: ctx, txHash +func (_m *RPCCLient) TransactionReceiptGeth(ctx context.Context, txHash common.Hash) (*coretypes.Receipt, error) { + ret := _m.Called(ctx, txHash) + + if len(ret) == 0 { + panic("no return value specified for TransactionReceiptGeth") + } + + var r0 *coretypes.Receipt + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (*coretypes.Receipt, error)); ok { + return rf(ctx, txHash) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *coretypes.Receipt); ok { + r0 = rf(ctx, txHash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*coretypes.Receipt) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { + r1 = rf(ctx, txHash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UnsubscribeAllExceptAliveLoop provides a mock function with given fields: +func (_m *RPCCLient) UnsubscribeAllExceptAliveLoop() { + _m.Called() +} + +// NewRPCCLient creates a new instance of RPCCLient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewRPCCLient(t interface { + mock.TestingT + Cleanup(func()) +}) *RPCCLient { + mock := &RPCCLient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index 54656cf1d3e..29c5d8fbb8b 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -42,6 +42,7 @@ type RPCCLient interface { *evmtypes.Receipt, *assets.Wei, *evmtypes.Head, + rpc.BatchElem, ] BlockByHashGeth(ctx context.Context, hash common.Hash) (b *types.Block, err error) BlockByNumberGeth(ctx context.Context, number *big.Int) (b *types.Block, err error) @@ -315,24 +316,20 @@ func (r *rpcClient) CallContext(ctx context.Context, result interface{}, method return err } -func (r *rpcClient) BatchCallContext(ctx context.Context, b []any) error { +func (r *rpcClient) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error { ctx, cancel, ws, http, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) if err != nil { return err } - batch := make([]rpc.BatchElem, len(b)) - for i, arg := range b { - batch[i] = arg.(rpc.BatchElem) - } defer cancel() lggr := r.newRqLggr().With("nBatchElems", len(b), "batchElems", b) lggr.Trace("RPC call: evmclient.Client#BatchCallContext") start := time.Now() if http != nil { - err = r.wrapHTTP(http.rpc.BatchCallContext(ctx, batch)) + err = r.wrapHTTP(http.rpc.BatchCallContext(ctx, b)) } else { - err = r.wrapWS(ws.rpc.BatchCallContext(ctx, batch)) + err = r.wrapWS(ws.rpc.BatchCallContext(ctx, b)) } duration := time.Since(start) diff --git a/core/chains/evm/client/send_only_node_test.go b/core/chains/evm/client/send_only_node_test.go index c2fdad06ec1..61db09a448c 100644 --- a/core/chains/evm/client/send_only_node_test.go +++ b/core/chains/evm/client/send_only_node_test.go @@ -20,7 +20,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "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" @@ -37,7 +36,7 @@ func TestNewSendOnlyNode(t *testing.T) { name := "TestNewSendOnlyNode" chainID := testutils.NewRandomEVMChainID() - node := evmclient.NewSendOnlyNode(lggr, *url, name, chainID) + node := client.NewSendOnlyNode(lggr, *url, name, chainID) assert.NotNil(t, node) // Must contain name & url with redacted password @@ -54,7 +53,7 @@ func TestStartSendOnlyNode(t *testing.T) { r := chainIDResp{chainID.Int64(), nil} url := r.newHTTPServer(t) lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) - s := evmclient.NewSendOnlyNode(lggr, *url, t.Name(), chainID) + s := client.NewSendOnlyNode(lggr, *url, t.Name(), chainID) defer func() { assert.NoError(t, s.Close()) }() err := s.Start(testutils.Context(t)) assert.NoError(t, err) // No errors expected @@ -67,7 +66,7 @@ func TestStartSendOnlyNode(t *testing.T) { chainID := testutils.FixtureChainID r := chainIDResp{chainID.Int64(), nil} url := r.newHTTPServer(t) - s := evmclient.NewSendOnlyNode(lggr, *url, t.Name(), testutils.FixtureChainID) + s := client.NewSendOnlyNode(lggr, *url, t.Name(), testutils.FixtureChainID) defer func() { assert.NoError(t, s.Close()) }() err := s.Start(testutils.Context(t)) @@ -80,7 +79,7 @@ func TestStartSendOnlyNode(t *testing.T) { t.Parallel() lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) invalidURL := url.URL{Scheme: "some rubbish", Host: "not a valid host"} - s := evmclient.NewSendOnlyNode(lggr, invalidURL, t.Name(), testutils.FixtureChainID) + s := client.NewSendOnlyNode(lggr, invalidURL, t.Name(), testutils.FixtureChainID) defer func() { assert.NoError(t, s.Close()) }() err := s.Start(testutils.Context(t)) @@ -112,10 +111,10 @@ func TestSendTransaction(t *testing.T) { chainID := testutils.FixtureChainID lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) url := testutils.MustParseURL(t, "http://place.holder") - s := evmclient.NewSendOnlyNode(lggr, + s := client.NewSendOnlyNode(lggr, *url, t.Name(), - testutils.FixtureChainID).(evmclient.TestableSendOnlyNode) + testutils.FixtureChainID).(client.TestableSendOnlyNode) require.NotNil(t, s) signedTx := createSignedTx(t, chainID, 1, []byte{1, 2, 3}) @@ -139,10 +138,10 @@ func TestBatchCallContext(t *testing.T) { lggr := logger.Test(t) chainID := testutils.FixtureChainID url := testutils.MustParseURL(t, "http://place.holder") - s := evmclient.NewSendOnlyNode( + s := client.NewSendOnlyNode( lggr, *url, "TestBatchCallContext", - chainID).(evmclient.TestableSendOnlyNode) + chainID).(client.TestableSendOnlyNode) blockNum := hexutil.EncodeBig(big.NewInt(42)) req := []rpc.BatchElem{ From 2b6cf28c5b86f6d5983e9cd29709a56c9fac1d8c Mon Sep 17 00:00:00 2001 From: Sergey Kudasov Date: Fri, 23 Feb 2024 16:47:00 +0100 Subject: [PATCH 23/24] bump pg to 15.6, add awscli to nix (#12157) --- charts/chainlink-cluster/values.yaml | 1 + shell.nix | 1 + 2 files changed, 2 insertions(+) diff --git a/charts/chainlink-cluster/values.yaml b/charts/chainlink-cluster/values.yaml index fefb819cf2f..67350af68c4 100644 --- a/charts/chainlink-cluster/values.yaml +++ b/charts/chainlink-cluster/values.yaml @@ -99,6 +99,7 @@ db: runAsUser: 999 runAsGroup: 999 stateful: false + image: "postgres:15.6" resources: requests: cpu: 1 diff --git a/shell.nix b/shell.nix index 7d219553368..44a6b6ab2c1 100644 --- a/shell.nix +++ b/shell.nix @@ -32,6 +32,7 @@ mkShell { jq # deployment + awscli2 devspace kubectl kubernetes-helm From 9a20034a39c8acca867a1fc061b1223288641ea9 Mon Sep 17 00:00:00 2001 From: Lei Date: Fri, 23 Feb 2024 10:33:01 -0800 Subject: [PATCH 24/24] use the block number provided or derived to run basic checks (#12086) --- core/scripts/chaincli/handler/debug.go | 85 ++++++++++++++++---------- 1 file changed, 52 insertions(+), 33 deletions(-) diff --git a/core/scripts/chaincli/handler/debug.go b/core/scripts/chaincli/handler/debug.go index 8b06937fc2c..4825499601d 100644 --- a/core/scripts/chaincli/handler/debug.go +++ b/core/scripts/chaincli/handler/debug.go @@ -98,50 +98,27 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { failCheckArgs("invalid upkeep ID", nil) } } - // get upkeep info + + // get trigger type, trigger type is immutable after its first setup triggerType, err := keeperRegistry21.GetTriggerType(latestCallOpts, upkeepID) if err != nil { failUnknown("failed to get trigger type: ", err) } - upkeepInfo, err := keeperRegistry21.GetUpkeep(latestCallOpts, upkeepID) - if err != nil { - failUnknown("failed to get trigger type: ", err) - } - minBalance, err := keeperRegistry21.GetMinBalance(latestCallOpts, upkeepID) - if err != nil { - failUnknown("failed to get min balance: ", err) - } - // do basic sanity checks - if (upkeepInfo.Target == gethcommon.Address{}) { - failCheckArgs("this upkeep does not exist on this registry", nil) - } - addLink("upkeep link", common.UpkeepLink(chainID, upkeepID)) - addLink("upkeep contract address", common.ContractExplorerLink(chainID, upkeepInfo.Target)) - if upkeepInfo.Paused { - resolveIneligible("upkeep is paused") - } - if upkeepInfo.MaxValidBlocknumber != math.MaxUint32 { - resolveIneligible("upkeep is cancelled") - } - message("upkeep is active (not paused or cancelled)") - if upkeepInfo.Balance.Cmp(minBalance) == -1 { - resolveIneligible("minBalance is < upkeep balance") - } - message("upkeep is funded above the min balance") - if bigmath.Div(bigmath.Mul(bigmath.Sub(upkeepInfo.Balance, minBalance), big.NewInt(100)), minBalance).Cmp(big.NewInt(5)) == -1 { - warning("upkeep balance is < 5% larger than minBalance") - } + // local state for pipeline results + var upkeepInfo iregistry21.KeeperRegistryBase21UpkeepInfo var checkResult iregistry21.CheckUpkeep var blockNum uint64 var performData []byte var workID [32]byte var trigger ocr2keepers.Trigger upkeepNeeded := false - // check upkeep + + // run basic checks and check upkeep by trigger type if triggerType == ConditionTrigger { message("upkeep identified as conditional trigger") + // validate inputs 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) @@ -154,6 +131,9 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { triggerCallOpts = latestCallOpts } + // do basic checks + upkeepInfo = getUpkeepInfoAndRunBasicChecks(keeperRegistry21, triggerCallOpts, upkeepID, chainID) + var tmpCheckResult iregistry21.CheckUpkeep0 tmpCheckResult, err = keeperRegistry21.CheckUpkeep0(triggerCallOpts, upkeepID) if err != nil { @@ -214,6 +194,7 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { 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) if err != nil { @@ -223,6 +204,10 @@ 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())} + + // do basic checks + upkeepInfo = getUpkeepInfoAndRunBasicChecks(keeperRegistry21, triggerCallOpts, upkeepID, chainID) + var rawTriggerConfig []byte rawTriggerConfig, err = keeperRegistry21.GetUpkeepTriggerConfig(triggerCallOpts, upkeepID) if err != nil { @@ -265,10 +250,10 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { } else { resolveIneligible(fmt.Sprintf("invalid trigger type: %d", triggerType)) } - upkeepNeeded, performData = checkResult.UpkeepNeeded, checkResult.PerformData + upkeepNeeded, performData = checkResult.UpkeepNeeded, checkResult.PerformData if checkResult.UpkeepFailureReason != 0 { - message(fmt.Sprintf("checkUpkeep failed with UpkeepFailureReason %s", getCheckUpkeepFailureReason(checkResult.UpkeepFailureReason))) + message(fmt.Sprintf("checkUpkeep reverted with UpkeepFailureReason %s", getCheckUpkeepFailureReason(checkResult.UpkeepFailureReason))) } // handle data streams lookup @@ -316,7 +301,7 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { } 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) + failCheckConfig("Data streams configs not set properly for this network, check your DATA_STREAMS settings in .env", nil) } // do mercury request @@ -394,6 +379,40 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { } } +func getUpkeepInfoAndRunBasicChecks(keeperRegistry21 *iregistry21.IKeeperRegistryMaster, callOpts *bind.CallOpts, upkeepID *big.Int, chainID int64) iregistry21.KeeperRegistryBase21UpkeepInfo { + // get upkeep info + upkeepInfo, err := keeperRegistry21.GetUpkeep(callOpts, upkeepID) + if err != nil { + failUnknown("failed to get upkeep info: ", err) + } + // get min balance + minBalance, err := keeperRegistry21.GetMinBalance(callOpts, upkeepID) + if err != nil { + failUnknown("failed to get min balance: ", err) + } + // do basic sanity checks + if (upkeepInfo.Target == gethcommon.Address{}) { + failCheckArgs("this upkeep does not exist on this registry", nil) + } + addLink("upkeep link", common.UpkeepLink(chainID, upkeepID)) + addLink("upkeep contract address", common.ContractExplorerLink(chainID, upkeepInfo.Target)) + if upkeepInfo.Paused { + resolveIneligible("upkeep is paused") + } + if upkeepInfo.MaxValidBlocknumber != math.MaxUint32 { + resolveIneligible("upkeep is canceled") + } + message("upkeep is active (not paused or canceled)") + if upkeepInfo.Balance.Cmp(minBalance) == -1 { + resolveIneligible("minBalance is < upkeep balance") + } + message("upkeep is funded above the min balance") + if bigmath.Div(bigmath.Mul(bigmath.Sub(upkeepInfo.Balance, minBalance), big.NewInt(100)), minBalance).Cmp(big.NewInt(5)) == -1 { + warning("upkeep balance is < 5% larger than minBalance") + } + return upkeepInfo +} + func getCheckUpkeepFailureReason(reasonIndex uint8) string { // Copied from KeeperRegistryBase2_1.sol reasonStrings := []string{