diff --git a/.github/actions/golangci-lint/action.yml b/.github/actions/golangci-lint/action.yml index f3697ed719..ffcfeea3d0 100644 --- a/.github/actions/golangci-lint/action.yml +++ b/.github/actions/golangci-lint/action.yml @@ -58,7 +58,7 @@ runs: skip-pkg-cache: true skip-build-cache: true # 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 + only-new-issues: true args: --out-format colored-line-number,checkstyle:golangci-lint-report.xml working-directory: ${{ inputs.go-directory }} - name: Print lint report artifact diff --git a/.gitignore b/.gitignore index 622455dd29..6c443928ab 100644 --- a/.gitignore +++ b/.gitignore @@ -72,11 +72,13 @@ ztarrepo.tar.gz **/test-ledger/* __debug_bin* .test_summary/ +db_dumps/ .run.id integration-tests/**/traces/ benchmark_report.csv benchmark_summary.json -integration-tests/citool/output.csv +secrets.toml +tmp_laneconfig/ # goreleaser builds cosign.* @@ -97,6 +99,7 @@ core/scripts/ccip/csv/node-wallets *report.xml *report.json *.out +dot_graphs/ contracts/yarn.lock @@ -119,7 +122,7 @@ tools/flakeytests/coverage.txt # Runtime test configuration that might contain secrets override*.toml -# Pythin venv +# Python venv .venv/ ocr_soak_report.csv diff --git a/.golangci.yml b/.golangci.yml index 7155cff2d5..9bc5a5b94d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -71,9 +71,10 @@ linters-settings: - name: error-return - name: error-strings - name: error-naming +# - name: exported - name: if-return - name: increment-decrement - # - name: var-naming +# - name: var-naming - name: var-declaration - name: package-comments - name: range @@ -92,7 +93,7 @@ linters-settings: - name: struct-tag # - name: string-format - name: string-of-int - # - name: range-val-address +# - name: range-val-address - name: range-val-in-closure - name: modifies-value-receiver - name: modifies-parameter diff --git a/.goreleaser.develop.yaml b/.goreleaser.develop.yaml index 7fbd2aa667..f8757676f8 100644 --- a/.goreleaser.develop.yaml +++ b/.goreleaser.develop.yaml @@ -1,9 +1,12 @@ -## goreleaser <1.14.0 project_name: chainlink +version: 2 + env: - ZIG_EXEC={{ if index .Env "ZIG_EXEC" }}{{ .Env.ZIG_EXEC }}{{ else }}zig{{ end }} - IMAGE_PREFIX={{ if index .Env "IMAGE_PREFIX" }}{{ .Env.IMAGE_PREFIX }}{{ else }}localhost:5001{{ end }} + - IMAGE_NAME={{ if index .Env "IMAGE_NAME" }}{{ .Env.IMAGE_NAME }}{{ else }}chainlink{{ end }} + - IMAGE_TAG={{ if index .Env "IMAGE_TAG" }}{{ .Env.IMAGE_TAG }}{{ else }}develop{{ end }} - IMAGE_LABEL_DESCRIPTION="node of the decentralized oracle network, bridging on and off-chain computation" - IMAGE_LABEL_LICENSES="MIT" - IMAGE_LABEL_SOURCE="https://github.com/smartcontractkit/{{ .ProjectName }}" @@ -56,18 +59,18 @@ builds: # See https://goreleaser.com/customization/docker/ dockers: - - id: root-linux-amd64 + - id: linux-amd64 dockerfile: core/chainlink.goreleaser.Dockerfile use: buildx goos: linux goarch: amd64 extra_files: - tmp/linux_amd64/libs - - tmp/linux_amd64/plugins - tools/bin/ldd_fix build_flag_templates: - "--platform=linux/amd64" - "--pull" + - "--build-arg=CHAINLINK_USER=chainlink" - "--build-arg=COMMIT_SHA={{ .FullCommit }}" - "--label=org.opencontainers.image.created={{ .Date }}" - "--label=org.opencontainers.image.description={{ .Env.IMAGE_LABEL_DESCRIPTION }}" @@ -78,20 +81,21 @@ dockers: - "--label=org.opencontainers.image.version={{ .Env.CHAINLINK_VERSION }}" - "--label=org.opencontainers.image.url={{ .Env.IMAGE_LABEL_SOURCE }}" image_templates: - - "{{ .Env.IMAGE_PREFIX }}/{{ .ProjectName }}-develop:develop-root-amd64" - - "{{ .Env.IMAGE_PREFIX }}/{{ .ProjectName }}-develop:sha-{{ .ShortCommit }}-root-amd64" - - id: root-linux-arm64 + - "{{ .Env.IMAGE_PREFIX }}/{{ .Env.IMAGE_NAME }}:{{ .Env.IMAGE_TAG }}" + - "{{ .Env.IMAGE_PREFIX }}/{{ .Env.IMAGE_NAME }}:{{ .Env.IMAGE_TAG }}-amd64" + - "{{ .Env.IMAGE_PREFIX }}/{{ .Env.IMAGE_NAME }}:sha-{{ .ShortCommit }}-amd64" + - id: linux-arm64 dockerfile: core/chainlink.goreleaser.Dockerfile use: buildx goos: linux goarch: arm64 extra_files: - tmp/linux_arm64/libs - - tmp/linux_arm64/plugins - tools/bin/ldd_fix build_flag_templates: - "--platform=linux/arm64" - "--pull" + - "--build-arg=CHAINLINK_USER=chainlink" - "--build-arg=COMMIT_SHA={{ .FullCommit }}" - "--label=org.opencontainers.image.created={{ .Date }}" - "--label=org.opencontainers.image.description={{ .Env.IMAGE_LABEL_DESCRIPTION }}" @@ -102,9 +106,9 @@ dockers: - "--label=org.opencontainers.image.version={{ .Env.CHAINLINK_VERSION }}" - "--label=org.opencontainers.image.url={{ .Env.IMAGE_LABEL_SOURCE }}" image_templates: - - "{{ .Env.IMAGE_PREFIX }}/{{ .ProjectName }}-develop:develop-root-arm64" - - "{{ .Env.IMAGE_PREFIX }}/{{ .ProjectName }}-develop:sha-{{ .ShortCommit }}-root-arm64" - - id: linux-amd64 + - "{{ .Env.IMAGE_PREFIX }}/{{ .Env.IMAGE_NAME }}:{{ .Env.IMAGE_TAG }}-arm64" + - "{{ .Env.IMAGE_PREFIX }}/{{ .Env.IMAGE_NAME }}:sha-{{ .ShortCommit }}-arm64" + - id: linux-amd64-plugins dockerfile: core/chainlink.goreleaser.Dockerfile use: buildx goos: linux @@ -118,6 +122,10 @@ dockers: - "--pull" - "--build-arg=CHAINLINK_USER=chainlink" - "--build-arg=COMMIT_SHA={{ .FullCommit }}" + - "--build-arg=CL_MEDIAN_CMD=chainlink-feeds" + - "--build-arg=CL_MERCURY_CMD=chainlink-mercury" + - "--build-arg=CL_SOLANA_CMD=chainlink-solana" + - "--build-arg=CL_STARKNET_CMD=chainlink-starknet" - "--label=org.opencontainers.image.created={{ .Date }}" - "--label=org.opencontainers.image.description={{ .Env.IMAGE_LABEL_DESCRIPTION }}" - "--label=org.opencontainers.image.licenses={{ .Env.IMAGE_LABEL_LICENSES }}" @@ -127,9 +135,10 @@ dockers: - "--label=org.opencontainers.image.version={{ .Env.CHAINLINK_VERSION }}" - "--label=org.opencontainers.image.url={{ .Env.IMAGE_LABEL_SOURCE }}" image_templates: - - "{{ .Env.IMAGE_PREFIX }}/{{ .ProjectName }}-develop:develop-amd64" - - "{{ .Env.IMAGE_PREFIX }}/{{ .ProjectName }}-develop:sha-{{ .ShortCommit }}-amd64" - - id: linux-arm64 + - "{{ .Env.IMAGE_PREFIX }}/{{ .Env.IMAGE_NAME }}:{{ .Env.IMAGE_TAG }}-plugins" + - "{{ .Env.IMAGE_PREFIX }}/{{ .Env.IMAGE_NAME }}:{{ .Env.IMAGE_TAG }}-plugins-amd64" + - "{{ .Env.IMAGE_PREFIX }}/{{ .Env.IMAGE_NAME }}:sha-{{ .ShortCommit }}-plugins-amd64" + - id: linux-arm64-plugins dockerfile: core/chainlink.goreleaser.Dockerfile use: buildx goos: linux @@ -143,6 +152,10 @@ dockers: - "--pull" - "--build-arg=CHAINLINK_USER=chainlink" - "--build-arg=COMMIT_SHA={{ .FullCommit }}" + - "--build-arg=CL_MEDIAN_CMD=chainlink-feeds" + - "--build-arg=CL_MERCURY_CMD=chainlink-mercury" + - "--build-arg=CL_SOLANA_CMD=chainlink-solana" + - "--build-arg=CL_STARKNET_CMD=chainlink-starknet" - "--label=org.opencontainers.image.created={{ .Date }}" - "--label=org.opencontainers.image.description={{ .Env.IMAGE_LABEL_DESCRIPTION }}" - "--label=org.opencontainers.image.licenses={{ .Env.IMAGE_LABEL_LICENSES }}" @@ -152,38 +165,50 @@ dockers: - "--label=org.opencontainers.image.version={{ .Env.CHAINLINK_VERSION }}" - "--label=org.opencontainers.image.url={{ .Env.IMAGE_LABEL_SOURCE }}" image_templates: - - "{{ .Env.IMAGE_PREFIX }}/{{ .ProjectName }}-develop:develop-arm64" - - "{{ .Env.IMAGE_PREFIX }}/{{ .ProjectName }}-develop:sha-{{ .ShortCommit }}-arm64" + - "{{ .Env.IMAGE_PREFIX }}/{{ .Env.IMAGE_NAME }}:{{ .Env.IMAGE_TAG }}-plugins-arm64" + - "{{ .Env.IMAGE_PREFIX }}/{{ .Env.IMAGE_NAME }}:sha-{{ .ShortCommit }}-plugins-arm64" # See https://goreleaser.com/customization/docker_manifest/ docker_manifests: - - name_template: "{{ .Env.IMAGE_PREFIX }}/{{ .ProjectName }}-develop:develop-root" + - name_template: "{{ .Env.IMAGE_PREFIX }}/{{ .Env.IMAGE_NAME }}:{{ .Env.IMAGE_TAG }}" image_templates: - - "{{ .Env.IMAGE_PREFIX }}/{{ .ProjectName }}-develop:develop-root-amd64" - - "{{ .Env.IMAGE_PREFIX }}/{{ .ProjectName }}-develop:develop-root-arm64" - - name_template: "{{ .Env.IMAGE_PREFIX }}/{{ .ProjectName }}-develop:sha-{{ .ShortCommit }}-root" + - "{{ .Env.IMAGE_PREFIX }}/{{ .Env.IMAGE_NAME }}:{{ .Env.IMAGE_TAG }}" + - "{{ .Env.IMAGE_PREFIX }}/{{ .Env.IMAGE_NAME }}:{{ .Env.IMAGE_TAG }}-amd64" + - "{{ .Env.IMAGE_PREFIX }}/{{ .Env.IMAGE_NAME }}:{{ .Env.IMAGE_TAG }}-arm64" + - name_template: "{{ .Env.IMAGE_PREFIX }}/{{ .Env.IMAGE_NAME }}:sha-{{ .ShortCommit }}" image_templates: - - "{{ .Env.IMAGE_PREFIX }}/{{ .ProjectName }}-develop:sha-{{ .ShortCommit }}-root-amd64" - - "{{ .Env.IMAGE_PREFIX }}/{{ .ProjectName }}-develop:sha-{{ .ShortCommit }}-root-arm64" - - name_template: "{{ .Env.IMAGE_PREFIX }}/{{ .ProjectName }}-develop:develop" + - "{{ .Env.IMAGE_PREFIX }}/{{ .Env.IMAGE_NAME }}:sha-{{ .ShortCommit }}-amd64" + - "{{ .Env.IMAGE_PREFIX }}/{{ .Env.IMAGE_NAME }}:sha-{{ .ShortCommit }}-arm64" + - name_template: "{{ .Env.IMAGE_PREFIX }}/{{ .Env.IMAGE_NAME }}:{{ .Env.IMAGE_TAG }}-plugins" image_templates: - - "{{ .Env.IMAGE_PREFIX }}/{{ .ProjectName }}-develop:develop-amd64" - - "{{ .Env.IMAGE_PREFIX }}/{{ .ProjectName }}-develop:develop-arm64" - - name_template: "{{ .Env.IMAGE_PREFIX }}/{{ .ProjectName }}-develop:sha-{{ .ShortCommit }}" + - "{{ .Env.IMAGE_PREFIX }}/{{ .Env.IMAGE_NAME }}:{{ .Env.IMAGE_TAG }}-plugins" + - "{{ .Env.IMAGE_PREFIX }}/{{ .Env.IMAGE_NAME }}:{{ .Env.IMAGE_TAG }}-plugins-amd64" + - "{{ .Env.IMAGE_PREFIX }}/{{ .Env.IMAGE_NAME }}:{{ .Env.IMAGE_TAG }}-plugins-arm64" + - name_template: "{{ .Env.IMAGE_PREFIX }}/{{ .Env.IMAGE_NAME }}:sha-{{ .ShortCommit }}-plugins" image_templates: - - "{{ .Env.IMAGE_PREFIX }}/{{ .ProjectName }}-develop:sha-{{ .ShortCommit }}-amd64" - - "{{ .Env.IMAGE_PREFIX }}/{{ .ProjectName }}-develop:sha-{{ .ShortCommit }}-arm64" + - "{{ .Env.IMAGE_PREFIX }}/{{ .Env.IMAGE_NAME }}:sha-{{ .ShortCommit }}-plugins-amd64" + - "{{ .Env.IMAGE_PREFIX }}/{{ .Env.IMAGE_NAME }}:sha-{{ .ShortCommit }}-plugins-arm64" # See https://goreleaser.com/customization/docker_sign/ docker_signs: - artifacts: all - stdin: "{{ .Env.COSIGN_PASSWORD }}" + args: + - "sign" + - "${artifact}" + - "--yes" checksum: name_template: "checksums.txt" snapshot: - name_template: "{{ .Env.CHAINLINK_VERSION }}-{{ .ShortCommit }}" + version_template: "{{ .Env.CHAINLINK_VERSION }}-{{ .ShortCommit }}" + +partial: + by: target + +# See https://goreleaser.com/customization/release/ +release: + disable: true changelog: sort: asc diff --git a/.goreleaser.devspace.yaml b/.goreleaser.devspace.yaml index 2f1e9754b4..ccea1de96b 100644 --- a/.goreleaser.devspace.yaml +++ b/.goreleaser.devspace.yaml @@ -1,6 +1,8 @@ ## goreleaser <1.14.0 project_name: ccip +version: 2 + env: - ZIG_EXEC={{ if index .Env "ZIG_EXEC" }}{{ .Env.ZIG_EXEC }}{{ else }}zig{{ end }} - IMAGE_LABEL_DESCRIPTION="node of the decentralized oracle network, bridging on and off-chain computation" @@ -75,7 +77,7 @@ checksum: name_template: "checksums.txt" snapshot: - name_template: '{{ .Env.CHAINLINK_VERSION }}-{{ .Runtime.Goarch }}-{{ .Now.Format "2006-01-02-15-04-05Z" }}' + version_template: '{{ .Env.CHAINLINK_VERSION }}-{{ .Runtime.Goarch }}-{{ .Now.Format "2006-01-02-15-04-05Z" }}' changelog: sort: asc diff --git a/.mockery.yaml b/.mockery.yaml index cfe202ac59..0a80a6b815 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -261,10 +261,14 @@ packages: ORM: Runner: PipelineParamUnmarshaler: - github.com/smartcontractkit/chainlink/v2/core/services/promreporter: + github.com/smartcontractkit/chainlink/v2/core/services/headreporter: config: - dir: core/internal/mocks + dir: "{{ .InterfaceDir }}" + filename: "{{ .InterfaceName | snakecase }}_mock.go" + inpackage: true + mockname: "Mock{{ .InterfaceName | camelcase }}" interfaces: + HeadReporter: PrometheusBackend: github.com/smartcontractkit/libocr/commontypes: config: @@ -341,14 +345,14 @@ packages: filename: evm2_evm_on_ramp_interface.go outpkg: mock_contracts interfaces: - EVM2EVMOnRampInterface: + EVM2EVMOnRampInterface: github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp: config: dir: core/gethwrappers/ccip/mocks/ filename: evm2_evm_off_ramp_interface.go outpkg: mock_contracts interfaces: - EVM2EVMOffRampInterface: + EVM2EVMOffRampInterface: github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp_1_2_0: config: dir: core/gethwrappers/ccip/mocks/v1_2_0/ @@ -377,34 +381,41 @@ packages: outpkg: mock_contracts interfaces: FeeQuoterInterface: + github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/price_registry: + config: + dir: core/gethwrappers/ccip/mocks/ + filename: price_registry_interface.go + outpkg: mock_contracts + interfaces: + PriceRegistryInterface: github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface: config: dir: core/gethwrappers/ccip/mocks/ filename: link_token_interface.go outpkg: mock_contracts interfaces: - LinkTokenInterface: + LinkTokenInterface: github.com/smartcontractkit/chainlink/v2/core/gethwrappers/liquiditymanager/generated/arbitrum_l1_bridge_adapter: config: dir: core/gethwrappers/liquiditymanager/mocks/mock_arbitrum_l1_bridge_adapter/ filename: arbitrum_l1_bridge_adapter_interface.go outpkg: mock_arbitrum_l1_bridge_adapter interfaces: - ArbitrumL1BridgeAdapterInterface: + ArbitrumL1BridgeAdapterInterface: github.com/smartcontractkit/chainlink/v2/core/gethwrappers/liquiditymanager/generated/arbitrum_l2_bridge_adapter: config: dir: core/gethwrappers/liquiditymanager/mocks/mock_arbitrum_l2_bridge_adapter/ filename: arbitrum_l2_bridge_adapter_interface.go outpkg: mock_arbitrum_l2_bridge_adapter interfaces: - ArbitrumL2BridgeAdapterInterface: + ArbitrumL2BridgeAdapterInterface: github.com/smartcontractkit/chainlink/v2/core/gethwrappers/liquiditymanager/generated/arbitrum_gateway_router: config: dir: core/gethwrappers/liquiditymanager/mocks/mock_arbitrum_gateway_router/ filename: arbitrum_gateway_router_interface.go outpkg: mock_arbitrum_gateway_router interfaces: - ArbitrumGatewayRouterInterface: + ArbitrumGatewayRouterInterface: github.com/smartcontractkit/chainlink/v2/core/gethwrappers/liquiditymanager/generated/arbitrum_inbox: config: dir: core/gethwrappers/liquiditymanager/mocks/mock_arbitrum_inbox/ @@ -418,7 +429,7 @@ packages: filename: l2_arbitrum_gateway_interface.go outpkg: mock_l2_arbitrum_gateway interfaces: - L2ArbitrumGatewayInterface: + L2ArbitrumGatewayInterface: github.com/smartcontractkit/chainlink/v2/core/gethwrappers/liquiditymanager/generated/arbsys: config: dir: core/gethwrappers/liquiditymanager/mocks/mock_arbsys/ @@ -432,7 +443,7 @@ packages: filename: node_interface_interface.go outpkg: mock_node_interface interfaces: - NodeInterfaceInterface: + NodeInterfaceInterface: github.com/smartcontractkit/chainlink/v2/core/gethwrappers/liquiditymanager/generated/l2_arbitrum_messenger: config: dir: core/gethwrappers/liquiditymanager/mocks/mock_l2_arbitrum_messenger/ @@ -446,7 +457,7 @@ packages: filename: arb_rollup_core_interface.go outpkg: mock_arbitrum_rollup_core interfaces: - ArbRollupCoreInterface: + ArbRollupCoreInterface: github.com/smartcontractkit/chainlink/v2/core/gethwrappers/liquiditymanager/generated/optimism_portal: config: dir: core/gethwrappers/liquiditymanager/mocks/mock_optimism_portal/ @@ -460,7 +471,7 @@ packages: filename: optimism_l2_output_oracle_interface.go outpkg: mock_optimism_l2_output_oracle interfaces: - OptimismL2OutputOracleInterface: + OptimismL2OutputOracleInterface: github.com/smartcontractkit/chainlink/v2/core/gethwrappers/liquiditymanager/generated/optimism_portal_2: config: dir: core/gethwrappers/liquiditymanager/mocks/mock_optimism_portal_2/ @@ -474,7 +485,7 @@ packages: filename: optimism_dispute_game_factory_interface.go outpkg: mock_optimism_dispute_game_factory interfaces: - OptimismDisputeGameFactoryInterface: + OptimismDisputeGameFactoryInterface: github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/cache: config: filename: chain_health_mock.go @@ -612,4 +623,7 @@ packages: dir: "{{ .InterfaceDir }}/../mocks" filename: rebalancer_mock.go interfaces: - RebalancingAlgo: \ No newline at end of file + RebalancingAlgo: + github.com/smartcontractkit/chainlink/v2/core/services/registrysyncer: + interfaces: + ORM: \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a78e070b43..395b95e63f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,146 @@ # Changelog Chainlink Core -## 2.15.0 - 2024-08-19 +## 2.16.0 - 2024-09-23 + +### Minor Changes + +- [#14138](https://github.com/smartcontractkit/chainlink/pull/14138) [`69335dc6b0`](https://github.com/smartcontractkit/chainlink/commit/69335dc6b0837ba9726a2772bf1dc98174c03310) Thanks [@silaslenihan](https://github.com/silaslenihan)! - #internal Exposed Confirmed state to ChainWriter GetTransactionStatus method + +- [#14157](https://github.com/smartcontractkit/chainlink/pull/14157) [`1852353bbf`](https://github.com/smartcontractkit/chainlink/commit/1852353bbf6ae4726287cb376bc7a323f657c92a) Thanks [@dimriou](https://github.com/dimriou)! - Fix bhe datarace #internal + +- [#14132](https://github.com/smartcontractkit/chainlink/pull/14132) [`2e314cddf0`](https://github.com/smartcontractkit/chainlink/commit/2e314cddf0f4dbd29cad4a43926dc1a5390cc70f) Thanks [@amit-momin](https://github.com/amit-momin)! - Updated ZK overflow detection to skip transactions with non-broadcasted attempts. Delayed detection for zkEVM using the MinAttempts config. Updated XLayer to use the same detection logic as zkEVM. #internal + +- [#13948](https://github.com/smartcontractkit/chainlink/pull/13948) [`3b4c2b58c3`](https://github.com/smartcontractkit/chainlink/commit/3b4c2b58c3ebb04a2261108e758a3419de436a71) Thanks [@chainchad](https://github.com/chainchad)! - Initialize start of v2.16.0 release + +- [#14100](https://github.com/smartcontractkit/chainlink/pull/14100) [`6a9528db29`](https://github.com/smartcontractkit/chainlink/commit/6a9528db29dadd231ec592f10d655e5367301d8f) Thanks [@huangzhen1997](https://github.com/huangzhen1997)! - add error handling when arbitrum sequencer is not accessible #added + +- [#13794](https://github.com/smartcontractkit/chainlink/pull/13794) [`c330defde2`](https://github.com/smartcontractkit/chainlink/commit/c330defde2211aa4a0d8392f867400a829220b2f) Thanks [@Farber98](https://github.com/Farber98)! - remove dependency on FinalityDepth in EVM TXM code. #internal + +- [#14099](https://github.com/smartcontractkit/chainlink/pull/14099) [`1d1af81c51`](https://github.com/smartcontractkit/chainlink/commit/1d1af81c51d78a7e1406d3e182b8740a2ae43c9c) Thanks [@huangzhen1997](https://github.com/huangzhen1997)! - add error handle for gnosis chiado for seen tx #added + +- [#14039](https://github.com/smartcontractkit/chainlink/pull/14039) [`b0e31e08d5`](https://github.com/smartcontractkit/chainlink/commit/b0e31e08d5a635521afc48570a4b2a01e1daa0fb) Thanks [@huangzhen1997](https://github.com/huangzhen1997)! - Improve TXM performance by optimizing Confirmer and Finalizer queries to stop pulling EVM receipt. #internal + +- [#14096](https://github.com/smartcontractkit/chainlink/pull/14096) [`3f0fad643d`](https://github.com/smartcontractkit/chainlink/commit/3f0fad643d554d2445273a67f58974cb6a785ec4) Thanks [@Farber98](https://github.com/Farber98)! - use FilteredLogs in EventBinding GetLatestValue instead of manual filtering. #internal + +- [#14068](https://github.com/smartcontractkit/chainlink/pull/14068) [`6ab3eb5b67`](https://github.com/smartcontractkit/chainlink/commit/6ab3eb5b67739ff88d3c4cf8ea125fd8273bc2b1) Thanks [@asoliman92](https://github.com/asoliman92)! - #added merging core/capabilities/ccip from https://github.com/smartcontractkit/ccip + +- [#14095](https://github.com/smartcontractkit/chainlink/pull/14095) [`aa4e981c8f`](https://github.com/smartcontractkit/chainlink/commit/aa4e981c8f51692ae19f57569260171736a3e4d9) Thanks [@cedric-cordenier](https://github.com/cedric-cordenier)! - #internal Change CapabilityType to string; remove possiblity of a panic + +- [#13957](https://github.com/smartcontractkit/chainlink/pull/13957) [`20dbba8e76`](https://github.com/smartcontractkit/chainlink/commit/20dbba8e76604a2488b0717d53d706ee11b11a9c) Thanks [@amit-momin](https://github.com/amit-momin)! - Added nonce validation immediately after broadcast for Hedera #internal + +- [#13638](https://github.com/smartcontractkit/chainlink/pull/13638) [`2312827156`](https://github.com/smartcontractkit/chainlink/commit/2312827156f24fa4a6e420aec12e5a3aeac81e2b) Thanks [@amit-momin](https://github.com/amit-momin)! - Introduced finalized transaction state. Added a finalizer component to the TXM to mark transactions as finalized. #internal + +- [#14041](https://github.com/smartcontractkit/chainlink/pull/14041) [`8d818ea265`](https://github.com/smartcontractkit/chainlink/commit/8d818ea265ff08887e61ace4f83364a3ee149ef0) Thanks [@amit-momin](https://github.com/amit-momin)! - Added gas limit estimation feature to EVM gas estimators. Introduced a new config `EVM.GasEstimator.EstimateLimit` to toggle this feature. #added + +- [#14165](https://github.com/smartcontractkit/chainlink/pull/14165) [`e76463cfa9`](https://github.com/smartcontractkit/chainlink/commit/e76463cfa9a0fbe6e35a74cbb3f7d63c85efcd88) Thanks [@silaslenihan](https://github.com/silaslenihan)! - #internal Add hexutil Bytes encoding to batchcall data + +- [#11654](https://github.com/smartcontractkit/chainlink/pull/11654) [`bf2b72d164`](https://github.com/smartcontractkit/chainlink/commit/bf2b72d164f8cc714cfbf57df59a3f3bf952b153) Thanks [@reductionista](https://github.com/reductionista)! - #bugfix More robust error handling in LogPoller, including no more misleading CRITICAL errors emitted under non-critical conditions + +- [#13647](https://github.com/smartcontractkit/chainlink/pull/13647) [`a41b353a20`](https://github.com/smartcontractkit/chainlink/commit/a41b353a20d73aa2d3fe3e8e979a0bcacc46fafe) Thanks [@bukata-sa](https://github.com/bukata-sa)! - #added Report new heads as a telemetry to OTI + +- [#13981](https://github.com/smartcontractkit/chainlink/pull/13981) [`6ef1d6eb44`](https://github.com/smartcontractkit/chainlink/commit/6ef1d6eb449ee1dc1d7d10d50990de7da55561ee) Thanks [@amaechiokolobi](https://github.com/amaechiokolobi)! - error handling for Treasure #added + +- [#14057](https://github.com/smartcontractkit/chainlink/pull/14057) [`e0850a6a31`](https://github.com/smartcontractkit/chainlink/commit/e0850a6a31843606015d1c49d52b5a6ad8727378) Thanks [@reductionista](https://github.com/reductionista)! - #bugfix Addresses 2 minor issues with the pruning of LogPoller's db tables: logs not matching any filter will now be pruned, and rows deleted are now properly reported for observability + +- [#14146](https://github.com/smartcontractkit/chainlink/pull/14146) [`d0d2f3046d`](https://github.com/smartcontractkit/chainlink/commit/d0d2f3046d44dc929b97bfff69b2daf4de2d4c8e) Thanks [@Farber98](https://github.com/Farber98)! - remove chainReader from the Relayer struct. #internal + +- [#14016](https://github.com/smartcontractkit/chainlink/pull/14016) [`8b9f2b6b90`](https://github.com/smartcontractkit/chainlink/commit/8b9f2b6b9098e8ec2368773368239106d066e4e3) Thanks [@ilija42](https://github.com/ilija42)! - #internal Add evm Chain Reader GetLatestValue support for filtering on indexed topic types that get hashed. + +- [#14033](https://github.com/smartcontractkit/chainlink/pull/14033) [`375e17b70f`](https://github.com/smartcontractkit/chainlink/commit/375e17b70fe6f17483556a491370e72218896dbc) Thanks [@Farber98](https://github.com/Farber98)! - Change ChainReader Block primitive field from int to string. #internal + +- [#14160](https://github.com/smartcontractkit/chainlink/pull/14160) [`c98feb205d`](https://github.com/smartcontractkit/chainlink/commit/c98feb205d5eef64d71c42b43516a87b83796a1d) Thanks [@ma33r](https://github.com/ma33r)! - Edited the Optimism Stack L1 Oracle to add support for Mantle #added + +- [#13999](https://github.com/smartcontractkit/chainlink/pull/13999) [`2a032e83a5`](https://github.com/smartcontractkit/chainlink/commit/2a032e83a5e09ae128e8c751779a7d1eebb729ea) Thanks [@amit-momin](https://github.com/amit-momin)! - Updated AutoPurge.Threshold and AutoPurge.MinAttempts configs to only be required for heuristic and added content-type header for Scroll API #internal + +- [#14021](https://github.com/smartcontractkit/chainlink/pull/14021) [`bd648bd73d`](https://github.com/smartcontractkit/chainlink/commit/bd648bd73df2a1de91a463a988f4c5b61e74b240) Thanks [@dhaidashenko](https://github.com/dhaidashenko)! - Added custom finality calculation for Astar #internal + +- [#14145](https://github.com/smartcontractkit/chainlink/pull/14145) [`567ce229ed`](https://github.com/smartcontractkit/chainlink/commit/567ce229ed434a74b09124feadf3265017ec5313) Thanks [@cedric-cordenier](https://github.com/cedric-cordenier)! - Formalize trigger API #internal + +- [#14127](https://github.com/smartcontractkit/chainlink/pull/14127) [`5e99bdb764`](https://github.com/smartcontractkit/chainlink/commit/5e99bdb764171f584df1fc6e10495c8ec0a3bb63) Thanks [@amit-momin](https://github.com/amit-momin)! - Added client error classification for terminally stuck transactions in the TXM #internal + +- [#14043](https://github.com/smartcontractkit/chainlink/pull/14043) [`55e7c8b505`](https://github.com/smartcontractkit/chainlink/commit/55e7c8b5055c975665a59199d5eda9fa21801a07) Thanks [@asoliman92](https://github.com/asoliman92)! - Added CCIP plugins code from https://github.com/smartcontractkit/ccip/ #added + +### Patch Changes + +- [#14148](https://github.com/smartcontractkit/chainlink/pull/14148) [`0ceb9b5fc6`](https://github.com/smartcontractkit/chainlink/commit/0ceb9b5fc67199b850d16b6a5ab1848327e91a5b) Thanks [@vyzaldysanchez](https://github.com/vyzaldysanchez)! - #bugfix Fixes test flake + +- [#14174](https://github.com/smartcontractkit/chainlink/pull/14174) [`b9a433bff5`](https://github.com/smartcontractkit/chainlink/commit/b9a433bff513223378b8b29c6f694446d00c345b) Thanks [@DeividasK](https://github.com/DeividasK)! - #added Allow workflows to run without external registry configured + +- [#13987](https://github.com/smartcontractkit/chainlink/pull/13987) [`c1bd103e9b`](https://github.com/smartcontractkit/chainlink/commit/c1bd103e9b134a90e0bd5f77b6e54797c7c881a8) Thanks [@KodeyThomas](https://github.com/KodeyThomas)! - #added L3X Config + +- [#14236](https://github.com/smartcontractkit/chainlink/pull/14236) [`0294e1f381`](https://github.com/smartcontractkit/chainlink/commit/0294e1f3813c0643b61af828ec438307dcab3123) Thanks [@dhaidashenko](https://github.com/dhaidashenko)! - Fixed deadlock in RPCClient causing CL Node to stop performing RPC requests for the affected chain #bugfix + +- [#14206](https://github.com/smartcontractkit/chainlink/pull/14206) [`621e87538c`](https://github.com/smartcontractkit/chainlink/commit/621e87538c931d5d3996974589dc27a0ab43f758) Thanks [@bukata-sa](https://github.com/bukata-sa)! - #bugfix head reporter non-zero reporting period + +- [#13862](https://github.com/smartcontractkit/chainlink/pull/13862) [`05ef7fdbb1`](https://github.com/smartcontractkit/chainlink/commit/05ef7fdbb115f55a85bcbbc5402350818501e1f5) Thanks [@martin-cll](https://github.com/martin-cll)! - New Mercury v4 report schema #added + +- [#14112](https://github.com/smartcontractkit/chainlink/pull/14112) [`1b584366d6`](https://github.com/smartcontractkit/chainlink/commit/1b584366d6bedc114946d0c8e202e95d031d5d37) Thanks [@giogam](https://github.com/giogam)! - #updated Sync feeds-manager wsrpc proto + +- [#14246](https://github.com/smartcontractkit/chainlink/pull/14246) [`f1bc2e7ad3`](https://github.com/smartcontractkit/chainlink/commit/f1bc2e7ad3610339145930991bf6a3c9ef94fa52) Thanks [@amit-momin](https://github.com/amit-momin)! - Updated gas limit estimation feature to set From address #internal + +- [#14018](https://github.com/smartcontractkit/chainlink/pull/14018) [`82accfff5c`](https://github.com/smartcontractkit/chainlink/commit/82accfff5c445fd1d29a26607234eba73e6b30fd) Thanks [@ettec](https://github.com/ettec)! - #internal fix to keystone e2e test dispatcher to correctly mock duplicate registration error + +- [#13990](https://github.com/smartcontractkit/chainlink/pull/13990) [`98fc8813dd`](https://github.com/smartcontractkit/chainlink/commit/98fc8813dd7f46e86a15fc3e838bbb681f835d0b) Thanks [@flodesi](https://github.com/flodesi)! - #added Add Astar TerminallyUnderpriced error mapping + +- [#14179](https://github.com/smartcontractkit/chainlink/pull/14179) [`633eb41a44`](https://github.com/smartcontractkit/chainlink/commit/633eb41a4467f91506e05e7fda6873c7b34f4731) Thanks [@bukata-sa](https://github.com/bukata-sa)! - #internal log info on missed finalized head instead of returning an error + +- [#14154](https://github.com/smartcontractkit/chainlink/pull/14154) [`a937d5c577`](https://github.com/smartcontractkit/chainlink/commit/a937d5c577d8ba13dc7542a757359339442ae33f) Thanks [@mateusz-sekara](https://github.com/mateusz-sekara)! - Separate price updates schedule for token prices in CCIP #updated + +- [#14185](https://github.com/smartcontractkit/chainlink/pull/14185) [`b563d77dd3`](https://github.com/smartcontractkit/chainlink/commit/b563d77dd30ad96253ae6586c06fd34a66d61936) Thanks [@mateusz-sekara](https://github.com/mateusz-sekara)! - Reporting all the token prices from the job spec for CCIP #updated + +- [#13756](https://github.com/smartcontractkit/chainlink/pull/13756) [`c92a7212ee`](https://github.com/smartcontractkit/chainlink/commit/c92a7212ee77b08c40d62925216e5081278a4e3f) Thanks [@vyzaldysanchez](https://github.com/vyzaldysanchez)! - #updated Adds DB syncing for registry syncer + +- [#13876](https://github.com/smartcontractkit/chainlink/pull/13876) [`15dc74cabd`](https://github.com/smartcontractkit/chainlink/commit/15dc74cabd3a83041ca97df54ea0fbb7e76e2a0a) Thanks [@dhaidashenko](https://github.com/dhaidashenko)! - Custom (30s) timeout for Hedera RPC requests with large payloads (SendTransaction, CallContext, etc.) #internal + +- [#14214](https://github.com/smartcontractkit/chainlink/pull/14214) [`32a2ccd2ba`](https://github.com/smartcontractkit/chainlink/commit/32a2ccd2ba4cbe59e46779c82ec35c909141ba2a) Thanks [@ettec](https://github.com/ettec)! - #internal allow gas limit to be specified when submitting transaction + +- [#14092](https://github.com/smartcontractkit/chainlink/pull/14092) [`3399dd6d7f`](https://github.com/smartcontractkit/chainlink/commit/3399dd6d7fee12bd8d099b74397edcc4dc56c11d) Thanks [@cds95](https://github.com/cds95)! - #internal prevent editing whether or not a DON accepts workflows + +- [#13780](https://github.com/smartcontractkit/chainlink/pull/13780) [`af335c1a52`](https://github.com/smartcontractkit/chainlink/commit/af335c1a522769c8c29858d8d6510330af3204cf) Thanks [@samsondav](https://github.com/samsondav)! - Further development of LLO plugin (parallel composition) #wip + +- [#14030](https://github.com/smartcontractkit/chainlink/pull/14030) [`d90bb66934`](https://github.com/smartcontractkit/chainlink/commit/d90bb66934a46bb1c6d376b000d860e1588d91c7) Thanks [@ettec](https://github.com/ettec)! - #internal restore common version to head of develop + +- [#14105](https://github.com/smartcontractkit/chainlink/pull/14105) [`eb31cf7970`](https://github.com/smartcontractkit/chainlink/commit/eb31cf7970bef1615b10b5a734c16879b448f30a) Thanks [@ettec](https://github.com/ettec)! - #internal speed up keystone e2e tests + +- [#14047](https://github.com/smartcontractkit/chainlink/pull/14047) [`d963b0aaac`](https://github.com/smartcontractkit/chainlink/commit/d963b0aaac2117902742cf1d6fc8471e82ae711b) Thanks [@ettec](https://github.com/ettec)! - #internal fix the mock trigger to ensure events are sent + +- [#13853](https://github.com/smartcontractkit/chainlink/pull/13853) [`0f557ae1e0`](https://github.com/smartcontractkit/chainlink/commit/0f557ae1e08040c931f6f3e5c6a96b93b1ca2182) Thanks [@flodesi](https://github.com/flodesi)! - #bugfix Bump BSC PriceMin to 3 gwei to match BSC node's required gas price. This value can be pushed back down to 1 gwei to enable cheaper transactions if the GasPrice field under the Eth.Miner header in the BSC node's config is also pushed down to 1000000000 + +- [#13935](https://github.com/smartcontractkit/chainlink/pull/13935) [`7ec99efc64`](https://github.com/smartcontractkit/chainlink/commit/7ec99efc64832750825f8bc6711fb9794d6e40df) Thanks [@ettec](https://github.com/ettec)! - #internal ensure remote target request hash is deterministic + +- [#14017](https://github.com/smartcontractkit/chainlink/pull/14017) [`1257d33913`](https://github.com/smartcontractkit/chainlink/commit/1257d33913d243c146bccbf4bda67a2bb1c7d5af) Thanks [@DeividasK](https://github.com/DeividasK)! - #internal + +- [#14053](https://github.com/smartcontractkit/chainlink/pull/14053) [`4f0f7802a8`](https://github.com/smartcontractkit/chainlink/commit/4f0f7802a884e831cd76d9578ee5c4a7134034db) Thanks [@DylanTinianov](https://github.com/DylanTinianov)! - Added custom client error messages for Mantle to capture InsufficientEth and Fatal errors. #added + +- [#14059](https://github.com/smartcontractkit/chainlink/pull/14059) [`40f4becb1e`](https://github.com/smartcontractkit/chainlink/commit/40f4becb1eab96920d8bfd59019cdb9358a94122) Thanks [@DeividasK](https://github.com/DeividasK)! - #internal + +- [#14116](https://github.com/smartcontractkit/chainlink/pull/14116) [`7fdc0c8e95`](https://github.com/smartcontractkit/chainlink/commit/7fdc0c8e95c4157dd9e3ce3f9a4efe370554a19c) Thanks [@ettec](https://github.com/ettec)! - #internal ks-404 validate ids before using as seed of transmission schedule + +- [#13993](https://github.com/smartcontractkit/chainlink/pull/13993) [`f5e0bd614a`](https://github.com/smartcontractkit/chainlink/commit/f5e0bd614a6c42d195c4ad74a10f7070970d01d5) Thanks [@DeividasK](https://github.com/DeividasK)! - #internal + +- [#14209](https://github.com/smartcontractkit/chainlink/pull/14209) [`c00ac968e6`](https://github.com/smartcontractkit/chainlink/commit/c00ac968e651fd7b09f473d20f0fe4755ba57367) Thanks [@AnieeG](https://github.com/AnieeG)! - #internal Adding deployment package as new pattern for product deployment/configuration + +- [#14183](https://github.com/smartcontractkit/chainlink/pull/14183) [`35f68c806b`](https://github.com/smartcontractkit/chainlink/commit/35f68c806b10cc0fe4a565293e32e2f5581bfeb5) Thanks [@graham-chainlink](https://github.com/graham-chainlink)! - #bugfix Fix incorrect error handling when registering a new feed manager + +- [#14212](https://github.com/smartcontractkit/chainlink/pull/14212) [`25d2961154`](https://github.com/smartcontractkit/chainlink/commit/25d29611543c3d43484c168e7efc23a7bf83f035) Thanks [@bukata-sa](https://github.com/bukata-sa)! - #internal add head report chain_id + +- [#14066](https://github.com/smartcontractkit/chainlink/pull/14066) [`98b9054397`](https://github.com/smartcontractkit/chainlink/commit/98b90543972d37e4c00196f3f00bcf5f380ea04d) Thanks [@DeividasK](https://github.com/DeividasK)! - #internal + +- [#14014](https://github.com/smartcontractkit/chainlink/pull/14014) [`c2c31c05ac`](https://github.com/smartcontractkit/chainlink/commit/c2c31c05ac3fe19d4df8313af25eb740953b935a) Thanks [@Madalosso](https://github.com/Madalosso)! - #updated Update Polygon configs to match PIP-35 + +- [#14125](https://github.com/smartcontractkit/chainlink/pull/14125) [`8fa8c3a075`](https://github.com/smartcontractkit/chainlink/commit/8fa8c3a07512bb8358abdabc3fdcc8ae310c6c1c) Thanks [@bukata-sa](https://github.com/bukata-sa)! - #bugfix balance shutdown deadlock + +- [#14181](https://github.com/smartcontractkit/chainlink/pull/14181) [`ee57b4f940`](https://github.com/smartcontractkit/chainlink/commit/ee57b4f940b8a9d9d7bba41a74e4757874755f5f) Thanks [@ettec](https://github.com/ettec)! - #internal topeerid should validate []byte length + +- [#14074](https://github.com/smartcontractkit/chainlink/pull/14074) [`a865709ea1`](https://github.com/smartcontractkit/chainlink/commit/a865709ea18bfc792db758b60de6f03e953f141f) Thanks [@mateusz-sekara](https://github.com/mateusz-sekara)! - Simplify how token and gas prices are stored in the database - user upsert instead of insert/delete flow #db_update + +- [#14050](https://github.com/smartcontractkit/chainlink/pull/14050) [`537d2ec1ad`](https://github.com/smartcontractkit/chainlink/commit/537d2ec1ad846898f820874442c3f69915096bad) Thanks [@ettec](https://github.com/ettec)! - #internal fix data race in syncer launcher + +- [#13970](https://github.com/smartcontractkit/chainlink/pull/13970) [`cefbb09797`](https://github.com/smartcontractkit/chainlink/commit/cefbb09797249309ac18e4ef81147e30f7c24360) Thanks [@cds95](https://github.com/cds95)! - #internal prevent reentrancy when configuring DON in Capabilities Registry + +- [#13907](https://github.com/smartcontractkit/chainlink/pull/13907) [`1eaf5e087a`](https://github.com/smartcontractkit/chainlink/commit/1eaf5e087a5ac204e0b472e1c307722887104678) Thanks [@dhaidashenko](https://github.com/dhaidashenko)! - Added new health check that ensures RPC provides new finalized heads at least every `NoNewFinalizedHeadsThreshold` #added + +## 2.15.0 - 2024-08-21 ### Minor Changes diff --git a/GNUmakefile b/GNUmakefile index 5804d2c9d4..829075c30d 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -27,12 +27,8 @@ gomod: ## Ensure chainlink's go dependencies are installed. go mod download .PHONY: gomodtidy -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 - cd ./dashboard-lib && go mod tidy +gomodtidy: gomods ## Run go mod tidy on all modules. + gomods tidy .PHONY: docs docs: ## Install and run pkgsite to view Go docs @@ -93,12 +89,8 @@ abigen: ## Build & install abigen. ./tools/bin/build_abigen .PHONY: generate -generate: pnpmdep abigen codecgen mockery protoc ## Execute all go:generate commands. - go generate -x ./... - cd ./core/scripts && go generate -x ./... - cd ./integration-tests && go generate -x ./... - cd ./integration-tests/load && go generate -x ./... - cd ./dashboard-lib && go generate -x ./... +generate: abigen codecgen mockery protoc gomods ## Execute all go:generate commands. + gomods -w go generate -x ./... mockery .PHONY: rm-mocked @@ -140,7 +132,7 @@ presubmit: ## Format go files and imports. .PHONY: gomods gomods: ## Install gomods - go install github.com/jmank88/gomods@v0.1.1 + go install github.com/jmank88/gomods@v0.1.3 .PHONY: mockery mockery: $(mockery) ## Install mockery. @@ -172,8 +164,7 @@ config-docs: ## Generate core node configuration documentation .PHONY: golangci-lint golangci-lint: ## Run golangci-lint for all issues. [ -d "./golangci-lint" ] || mkdir ./golangci-lint && \ - docker run --rm -v $(shell pwd):/app -w /app golangci/golangci-lint:v1.59.1 golangci-lint run --max-issues-per-linter 0 --max-same-issues 0 > ./golangci-lint/$(shell date +%Y-%m-%d_%H:%M:%S).txt - + docker run --rm -v $(shell pwd):/app -w /app golangci/golangci-lint:v1.59.1 golangci-lint run --max-issues-per-linter 0 --max-same-issues 0 | tee ./golangci-lint/$(shell date +%Y-%m-%d_%H:%M:%S).txt GORELEASER_CONFIG ?= .goreleaser.yaml diff --git a/LICENSE b/LICENSE index 1d938d8d12..3af9faa6c6 100644 --- a/LICENSE +++ b/LICENSE @@ -2,14 +2,31 @@ Copyright (c) 2018 SmartContract ChainLink Limited SEZC Portions of this software are licensed as follows: -*All content residing under (1) "contracts/src/v0.8/ccip"; (2) "core/services/ocr2/plugins/ccip" are licensed under “Business Source License 1.1” with a Change Date of May 23, 2027 and Change License to “MIT License” +The MIT License (MIT) -* Content outside of the above mentioned directories or restrictions above is available under the "MIT" license as defined below. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -The MIT License (MIT) +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +*All content residing under (1) “/contracts/src/v0.8/ccip”; (2) +“/core/gethwrappers/ccip”; (3) “/core/services/ocr2/plugins/ccip”; (4) "/core/capabilities/ccip" are licensed +under “Business Source License 1.1” with a Change Date of May 23, 2027 and +Change License to “MIT License” -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* Content outside of the above mentioned directories or restrictions +above is available under the "MIT" license as defined above. \ No newline at end of file diff --git a/bors.toml b/bors.toml deleted file mode 100644 index 5970c638ce..0000000000 --- a/bors.toml +++ /dev/null @@ -1,20 +0,0 @@ -# https://github.com/bors-ng/bors-ng/issues/730 -status = [ - "sigscanner-check", - "lint", - "Core Tests (go_core_tests)", - "Core Tests (go_core_race_tests)", - "Solana Smoke Tests", - "Prettier Formatting", - "ETH Smoke Tests", - "Solidity" -] -block_labels = [ "do-not-merge", "do-not-merge-yet", "needs changes", "wip" ] -timeout_sec = 3600 # one hour -required_approvals = 1 -up_to_date_approvals = true -delete_merged_branches = true -update_base_for_deletes = true -# todo: enable after organizing codeowners -# use_codeowners = true -use_squash_merge = true diff --git a/common/client/poller.go b/common/client/poller.go index d6080722c5..eeb6c3af57 100644 --- a/common/client/poller.go +++ b/common/client/poller.go @@ -2,7 +2,6 @@ package client import ( "context" - "sync" "time" "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -15,83 +14,80 @@ import ( // and delivers the result to a channel. It is used by multinode to poll // for new heads and implements the Subscription interface. type Poller[T any] struct { - services.StateMachine + services.Service + eng *services.Engine + pollingInterval time.Duration pollingFunc func(ctx context.Context) (T, error) pollingTimeout time.Duration - logger logger.Logger channel chan<- T errCh chan error - - stopCh services.StopChan - wg sync.WaitGroup } // NewPoller creates a new Poller instance and returns a channel to receive the polled data func NewPoller[ T any, -](pollingInterval time.Duration, pollingFunc func(ctx context.Context) (T, error), pollingTimeout time.Duration, logger logger.Logger) (Poller[T], <-chan T) { +](pollingInterval time.Duration, pollingFunc func(ctx context.Context) (T, error), pollingTimeout time.Duration, lggr logger.Logger) (Poller[T], <-chan T) { channel := make(chan T) - return Poller[T]{ + p := Poller[T]{ pollingInterval: pollingInterval, pollingFunc: pollingFunc, pollingTimeout: pollingTimeout, channel: channel, - logger: logger, errCh: make(chan error), - stopCh: make(chan struct{}), - }, channel + } + p.Service, p.eng = services.Config{ + Name: "Poller", + Start: p.start, + Close: p.close, + }.NewServiceEngine(lggr) + return p, channel } var _ types.Subscription = &Poller[any]{} -func (p *Poller[T]) Start() error { - return p.StartOnce("Poller", func() error { - p.wg.Add(1) - go p.pollingLoop() - return nil - }) +func (p *Poller[T]) start(ctx context.Context) error { + p.eng.Go(p.pollingLoop) + return nil } // Unsubscribe cancels the sending of events to the data channel func (p *Poller[T]) Unsubscribe() { - _ = p.StopOnce("Poller", func() error { - close(p.stopCh) - p.wg.Wait() - close(p.errCh) - close(p.channel) - return nil - }) + _ = p.Close() +} + +func (p *Poller[T]) close() error { + close(p.errCh) + close(p.channel) + return nil } func (p *Poller[T]) Err() <-chan error { return p.errCh } -func (p *Poller[T]) pollingLoop() { - defer p.wg.Done() - +func (p *Poller[T]) pollingLoop(ctx context.Context) { ticker := time.NewTicker(p.pollingInterval) defer ticker.Stop() for { select { - case <-p.stopCh: + case <-ctx.Done(): return case <-ticker.C: // Set polling timeout - pollingCtx, cancelPolling := p.stopCh.CtxCancel(context.WithTimeout(context.Background(), p.pollingTimeout)) + pollingCtx, cancelPolling := context.WithTimeout(ctx, p.pollingTimeout) // Execute polling function result, err := p.pollingFunc(pollingCtx) cancelPolling() if err != nil { - p.logger.Warnf("polling error: %v", err) + p.eng.Warnf("polling error: %v", err) continue } // Send result to channel or block if channel is full select { case p.channel <- result: - case <-p.stopCh: + case <-ctx.Done(): return } } diff --git a/common/client/poller_test.go b/common/client/poller_test.go index 91af579307..930b101751 100644 --- a/common/client/poller_test.go +++ b/common/client/poller_test.go @@ -20,20 +20,22 @@ func Test_Poller(t *testing.T) { lggr := logger.Test(t) t.Run("Test multiple start", func(t *testing.T) { + ctx := tests.Context(t) pollFunc := func(ctx context.Context) (Head, error) { return nil, nil } poller, _ := NewPoller[Head](time.Millisecond, pollFunc, time.Second, lggr) - err := poller.Start() + err := poller.Start(ctx) require.NoError(t, err) - err = poller.Start() + err = poller.Start(ctx) require.Error(t, err) poller.Unsubscribe() }) t.Run("Test polling for heads", func(t *testing.T) { + ctx := tests.Context(t) // Mock polling function that returns a new value every time it's called var pollNumber int pollLock := sync.Mutex{} @@ -50,7 +52,7 @@ func Test_Poller(t *testing.T) { // Create poller and start to receive data poller, channel := NewPoller[Head](time.Millisecond, pollFunc, time.Second, lggr) - require.NoError(t, poller.Start()) + require.NoError(t, poller.Start(ctx)) defer poller.Unsubscribe() // Receive updates from the poller @@ -63,6 +65,7 @@ func Test_Poller(t *testing.T) { }) t.Run("Test polling errors", func(t *testing.T) { + ctx := tests.Context(t) // Mock polling function that returns an error var pollNumber int pollLock := sync.Mutex{} @@ -77,7 +80,7 @@ func Test_Poller(t *testing.T) { // Create poller and subscribe to receive data poller, _ := NewPoller[Head](time.Millisecond, pollFunc, time.Second, olggr) - require.NoError(t, poller.Start()) + require.NoError(t, poller.Start(ctx)) defer poller.Unsubscribe() // Ensure that all errors were logged as expected @@ -94,6 +97,7 @@ func Test_Poller(t *testing.T) { }) t.Run("Test polling timeout", func(t *testing.T) { + ctx := tests.Context(t) pollFunc := func(ctx context.Context) (Head, error) { if <-ctx.Done(); true { return nil, ctx.Err() @@ -108,7 +112,7 @@ func Test_Poller(t *testing.T) { // Create poller and subscribe to receive data poller, _ := NewPoller[Head](time.Millisecond, pollFunc, pollingTimeout, olggr) - require.NoError(t, poller.Start()) + require.NoError(t, poller.Start(ctx)) defer poller.Unsubscribe() // Ensure that timeout errors were logged as expected @@ -119,6 +123,7 @@ func Test_Poller(t *testing.T) { }) t.Run("Test unsubscribe during polling", func(t *testing.T) { + ctx := tests.Context(t) wait := make(chan struct{}) closeOnce := sync.OnceFunc(func() { close(wait) }) pollFunc := func(ctx context.Context) (Head, error) { @@ -137,7 +142,7 @@ func Test_Poller(t *testing.T) { // Create poller and subscribe to receive data poller, _ := NewPoller[Head](time.Millisecond, pollFunc, pollingTimeout, olggr) - require.NoError(t, poller.Start()) + require.NoError(t, poller.Start(ctx)) // Unsubscribe while blocked in polling function <-wait @@ -167,8 +172,9 @@ func Test_Poller_Unsubscribe(t *testing.T) { } t.Run("Test multiple unsubscribe", func(t *testing.T) { + ctx := tests.Context(t) poller, channel := NewPoller[Head](time.Millisecond, pollFunc, time.Second, lggr) - err := poller.Start() + err := poller.Start(ctx) require.NoError(t, err) <-channel @@ -177,8 +183,9 @@ func Test_Poller_Unsubscribe(t *testing.T) { }) t.Run("Read channel after unsubscribe", func(t *testing.T) { + ctx := tests.Context(t) poller, channel := NewPoller[Head](time.Millisecond, pollFunc, time.Second, lggr) - err := poller.Start() + err := poller.Start(ctx) require.NoError(t, err) poller.Unsubscribe() diff --git a/common/headtracker/head_broadcaster.go b/common/headtracker/head_broadcaster.go index 7edcccfccb..c81c61141f 100644 --- a/common/headtracker/head_broadcaster.go +++ b/common/headtracker/head_broadcaster.go @@ -42,13 +42,12 @@ type HeadBroadcaster[H types.Head[BLOCK_HASH], BLOCK_HASH types.Hashable] interf } type headBroadcaster[H types.Head[BLOCK_HASH], BLOCK_HASH types.Hashable] struct { - services.StateMachine - logger logger.Logger + services.Service + eng *services.Engine + callbacks callbackSet[H, BLOCK_HASH] mailbox *mailbox.Mailbox[H] mutex sync.Mutex - chClose services.StopChan - wgDone sync.WaitGroup latest H lastCallbackID int } @@ -60,41 +59,29 @@ func NewHeadBroadcaster[ ]( lggr logger.Logger, ) HeadBroadcaster[H, BLOCK_HASH] { - return &headBroadcaster[H, BLOCK_HASH]{ - logger: logger.Named(lggr, "HeadBroadcaster"), + hb := &headBroadcaster[H, BLOCK_HASH]{ callbacks: make(callbackSet[H, BLOCK_HASH]), mailbox: mailbox.NewSingle[H](), - chClose: make(chan struct{}), } + hb.Service, hb.eng = services.Config{ + Name: "HeadBroadcaster", + Start: hb.start, + Close: hb.close, + }.NewServiceEngine(lggr) + return hb } -func (hb *headBroadcaster[H, BLOCK_HASH]) Start(context.Context) error { - return hb.StartOnce("HeadBroadcaster", func() error { - hb.wgDone.Add(1) - go hb.run() - return nil - }) -} - -func (hb *headBroadcaster[H, BLOCK_HASH]) Close() error { - return hb.StopOnce("HeadBroadcaster", func() error { - hb.mutex.Lock() - // clear all callbacks - hb.callbacks = make(callbackSet[H, BLOCK_HASH]) - hb.mutex.Unlock() - - close(hb.chClose) - hb.wgDone.Wait() - return nil - }) +func (hb *headBroadcaster[H, BLOCK_HASH]) start(context.Context) error { + hb.eng.Go(hb.run) + return nil } -func (hb *headBroadcaster[H, BLOCK_HASH]) Name() string { - return hb.logger.Name() -} - -func (hb *headBroadcaster[H, BLOCK_HASH]) HealthReport() map[string]error { - return map[string]error{hb.Name(): hb.Healthy()} +func (hb *headBroadcaster[H, BLOCK_HASH]) close() error { + hb.mutex.Lock() + // clear all callbacks + hb.callbacks = make(callbackSet[H, BLOCK_HASH]) + hb.mutex.Unlock() + return nil } func (hb *headBroadcaster[H, BLOCK_HASH]) BroadcastNewLongestChain(head H) { @@ -121,15 +108,13 @@ func (hb *headBroadcaster[H, BLOCK_HASH]) Subscribe(callback HeadTrackable[H, BL return } -func (hb *headBroadcaster[H, BLOCK_HASH]) run() { - defer hb.wgDone.Done() - +func (hb *headBroadcaster[H, BLOCK_HASH]) run(ctx context.Context) { for { select { - case <-hb.chClose: + case <-ctx.Done(): return case <-hb.mailbox.Notify(): - hb.executeCallbacks() + hb.executeCallbacks(ctx) } } } @@ -137,10 +122,10 @@ func (hb *headBroadcaster[H, BLOCK_HASH]) run() { // DEV: the head relayer makes no promises about head delivery! Subscribing // Jobs should expect to the relayer to skip heads if there is a large number of listeners // and all callbacks cannot be completed in the allotted time. -func (hb *headBroadcaster[H, BLOCK_HASH]) executeCallbacks() { +func (hb *headBroadcaster[H, BLOCK_HASH]) executeCallbacks(ctx context.Context) { head, exists := hb.mailbox.Retrieve() if !exists { - hb.logger.Info("No head to retrieve. It might have been skipped") + hb.eng.Info("No head to retrieve. It might have been skipped") return } @@ -149,7 +134,7 @@ func (hb *headBroadcaster[H, BLOCK_HASH]) executeCallbacks() { hb.latest = head hb.mutex.Unlock() - hb.logger.Debugw("Initiating callbacks", + hb.eng.Debugw("Initiating callbacks", "headNum", head.BlockNumber(), "numCallbacks", len(callbacks), ) @@ -157,9 +142,6 @@ func (hb *headBroadcaster[H, BLOCK_HASH]) executeCallbacks() { wg := sync.WaitGroup{} wg.Add(len(callbacks)) - ctx, cancel := hb.chClose.NewCtx() - defer cancel() - for _, callback := range callbacks { go func(trackable HeadTrackable[H, BLOCK_HASH]) { defer wg.Done() @@ -168,7 +150,7 @@ func (hb *headBroadcaster[H, BLOCK_HASH]) executeCallbacks() { defer cancel() trackable.OnNewLongestChain(cctx, head) elapsed := time.Since(start) - hb.logger.Debugw(fmt.Sprintf("Finished callback in %s", elapsed), + hb.eng.Debugw(fmt.Sprintf("Finished callback in %s", elapsed), "callbackType", reflect.TypeOf(trackable), "blockNumber", head.BlockNumber(), "time", elapsed) }(callback) } diff --git a/common/headtracker/head_listener.go b/common/headtracker/head_listener.go index 25715b3528..d240caab3c 100644 --- a/common/headtracker/head_listener.go +++ b/common/headtracker/head_listener.go @@ -29,14 +29,15 @@ var ( }, []string{"ChainID"}) ) -// headHandler is a callback that handles incoming heads -type headHandler[H types.Head[BLOCK_HASH], BLOCK_HASH types.Hashable] func(ctx context.Context, header H) error +// HeadHandler is a callback that handles incoming heads +type HeadHandler[H types.Head[BLOCK_HASH], BLOCK_HASH types.Hashable] func(ctx context.Context, header H) error // HeadListener is a chain agnostic interface that manages connection of Client that receives heads from the blockchain node type HeadListener[H types.Head[BLOCK_HASH], BLOCK_HASH types.Hashable] interface { - // ListenForNewHeads kicks off the listen loop (not thread safe) - // done() must be executed upon leaving ListenForNewHeads() - ListenForNewHeads(onSubscribe func(), handleNewHead headHandler[H, BLOCK_HASH], done func()) + services.Service + + // ListenForNewHeads runs the listen loop (not thread safe) + ListenForNewHeads(ctx context.Context) // ReceivingHeads returns true if the listener is receiving heads (thread safe) ReceivingHeads() bool @@ -54,10 +55,13 @@ type headListener[ ID types.ID, BLOCK_HASH types.Hashable, ] struct { + services.Service + eng *services.Engine + config htrktypes.Config client htrktypes.Client[HTH, S, ID, BLOCK_HASH] - logger logger.Logger - chStop services.StopChan + onSubscription func(context.Context) + handleNewHead HeadHandler[HTH, BLOCK_HASH] chHeaders chan HTH headSubscription types.Subscription connected atomic.Bool @@ -74,38 +78,43 @@ func NewHeadListener[ lggr logger.Logger, client CLIENT, config htrktypes.Config, - chStop chan struct{}, + onSubscription func(context.Context), + handleNewHead HeadHandler[HTH, BLOCK_HASH], ) HeadListener[HTH, BLOCK_HASH] { - return &headListener[HTH, S, ID, BLOCK_HASH]{ - config: config, - client: client, - logger: logger.Named(lggr, "HeadListener"), - chStop: chStop, + hl := &headListener[HTH, S, ID, BLOCK_HASH]{ + config: config, + client: client, + onSubscription: onSubscription, + handleNewHead: handleNewHead, } + hl.Service, hl.eng = services.Config{ + Name: "HeadListener", + Start: hl.start, + }.NewServiceEngine(lggr) + return hl } -func (hl *headListener[HTH, S, ID, BLOCK_HASH]) Name() string { - return hl.logger.Name() +func (hl *headListener[HTH, S, ID, BLOCK_HASH]) start(context.Context) error { + hl.eng.Go(hl.ListenForNewHeads) + return nil } -func (hl *headListener[HTH, S, ID, BLOCK_HASH]) ListenForNewHeads(onSubscription func(), handleNewHead headHandler[HTH, BLOCK_HASH], done func()) { - defer done() +func (hl *headListener[HTH, S, ID, BLOCK_HASH]) ListenForNewHeads(ctx context.Context) { defer hl.unsubscribe() - ctx, cancel := hl.chStop.NewCtx() - defer cancel() - for { if !hl.subscribe(ctx) { break } - onSubscription() - err := hl.receiveHeaders(ctx, handleNewHead) + if hl.onSubscription != nil { + hl.onSubscription(ctx) + } + err := hl.receiveHeaders(ctx, hl.handleNewHead) if ctx.Err() != nil { break } else if err != nil { - hl.logger.Errorw("Error in new head subscription, unsubscribed", "err", err) + hl.eng.Errorw("Error in new head subscription, unsubscribed", "err", err) continue } break @@ -131,7 +140,7 @@ func (hl *headListener[HTH, S, ID, BLOCK_HASH]) HealthReport() map[string]error return map[string]error{hl.Name(): err} } -func (hl *headListener[HTH, S, ID, BLOCK_HASH]) receiveHeaders(ctx context.Context, handleNewHead headHandler[HTH, BLOCK_HASH]) error { +func (hl *headListener[HTH, S, ID, BLOCK_HASH]) receiveHeaders(ctx context.Context, handleNewHead HeadHandler[HTH, BLOCK_HASH]) error { var noHeadsAlarmC <-chan time.Time var noHeadsAlarmT *time.Ticker noHeadsAlarmDuration := hl.config.BlockEmissionIdleWarningThreshold() @@ -142,7 +151,7 @@ func (hl *headListener[HTH, S, ID, BLOCK_HASH]) receiveHeaders(ctx context.Conte for { select { - case <-hl.chStop: + case <-ctx.Done(): return nil case blockHeader, open := <-hl.chHeaders: @@ -158,13 +167,13 @@ func (hl *headListener[HTH, S, ID, BLOCK_HASH]) receiveHeaders(ctx context.Conte return errors.New("head listener: chHeaders prematurely closed") } if !blockHeader.IsValid() { - hl.logger.Error("got nil block header") + hl.eng.Error("got nil block header") continue } // Compare the chain ID of the block header to the chain ID of the client if !blockHeader.HasChainID() || blockHeader.ChainID().String() != chainId.String() { - hl.logger.Panicf("head listener for %s received block header for %s", chainId, blockHeader.ChainID()) + hl.eng.Panicf("head listener for %s received block header for %s", chainId, blockHeader.ChainID()) } promNumHeadsReceived.WithLabelValues(chainId.String()).Inc() @@ -184,7 +193,7 @@ func (hl *headListener[HTH, S, ID, BLOCK_HASH]) receiveHeaders(ctx context.Conte case <-noHeadsAlarmC: // We haven't received a head on the channel for a long time, log a warning - hl.logger.Warnf("have not received a head for %v", noHeadsAlarmDuration) + hl.eng.Warnf("have not received a head for %v", noHeadsAlarmDuration) hl.receivingHeads.Store(false) } } @@ -198,19 +207,19 @@ func (hl *headListener[HTH, S, ID, BLOCK_HASH]) subscribe(ctx context.Context) b for { hl.unsubscribe() - hl.logger.Debugf("Subscribing to new heads on chain %s", chainId.String()) + hl.eng.Debugf("Subscribing to new heads on chain %s", chainId.String()) select { - case <-hl.chStop: + case <-ctx.Done(): return false case <-time.After(subscribeRetryBackoff.Duration()): err := hl.subscribeToHead(ctx) if err != nil { promEthConnectionErrors.WithLabelValues(chainId.String()).Inc() - hl.logger.Warnw("Failed to subscribe to heads on chain", "chainID", chainId.String(), "err", err) + hl.eng.Warnw("Failed to subscribe to heads on chain", "chainID", chainId.String(), "err", err) } else { - hl.logger.Debugf("Subscribed to heads on chain %s", chainId.String()) + hl.eng.Debugf("Subscribed to heads on chain %s", chainId.String()) return true } } diff --git a/common/headtracker/head_tracker.go b/common/headtracker/head_tracker.go index afa5d931ee..8546d856b6 100644 --- a/common/headtracker/head_tracker.go +++ b/common/headtracker/head_tracker.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "math/big" - "sync" "time" "github.com/prometheus/client_golang/prometheus" @@ -51,7 +50,9 @@ type headTracker[ ID types.ID, BLOCK_HASH types.Hashable, ] struct { - services.StateMachine + services.Service + eng *services.Engine + log logger.SugaredLogger headBroadcaster HeadBroadcaster[HTH, BLOCK_HASH] headSaver HeadSaver[HTH, BLOCK_HASH] @@ -64,8 +65,6 @@ type headTracker[ backfillMB *mailbox.Mailbox[HTH] broadcastMB *mailbox.Mailbox[HTH] headListener HeadListener[HTH, BLOCK_HASH] - chStop services.StopChan - wgDone sync.WaitGroup getNilHead func() HTH } @@ -85,52 +84,52 @@ func NewHeadTracker[ mailMon *mailbox.Monitor, getNilHead func() HTH, ) HeadTracker[HTH, BLOCK_HASH] { - chStop := make(chan struct{}) - lggr = logger.Named(lggr, "HeadTracker") - return &headTracker[HTH, S, ID, BLOCK_HASH]{ + ht := &headTracker[HTH, S, ID, BLOCK_HASH]{ headBroadcaster: headBroadcaster, client: client, chainID: client.ConfiguredChainID(), config: config, htConfig: htConfig, - log: logger.Sugared(lggr), backfillMB: mailbox.NewSingle[HTH](), broadcastMB: mailbox.New[HTH](HeadsBufferSize), - chStop: chStop, - headListener: NewHeadListener[HTH, S, ID, BLOCK_HASH](lggr, client, config, chStop), headSaver: headSaver, mailMon: mailMon, getNilHead: getNilHead, } + ht.Service, ht.eng = services.Config{ + Name: "HeadTracker", + NewSubServices: func(lggr logger.Logger) []services.Service { + ht.headListener = NewHeadListener[HTH, S, ID, BLOCK_HASH](lggr, client, config, + // NOTE: Always try to start the head tracker off with whatever the + // latest head is, without waiting for the subscription to send us one. + // + // In some cases the subscription will send us the most recent head + // anyway when we connect (but we should not rely on this because it is + // not specced). If it happens this is fine, and the head will be + // ignored as a duplicate. + func(ctx context.Context) { + err := ht.handleInitialHead(ctx) + if err != nil { + ht.log.Errorw("Error handling initial head", "err", err.Error()) + } + }, ht.handleNewHead) + return []services.Service{ht.headListener} + }, + Start: ht.start, + Close: ht.close, + }.NewServiceEngine(lggr) + ht.log = logger.Sugared(ht.eng) + return ht } // Start starts HeadTracker service. -func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) Start(ctx context.Context) error { - return ht.StartOnce("HeadTracker", func() error { - ht.log.Debugw("Starting HeadTracker", "chainID", ht.chainID) - // NOTE: Always try to start the head tracker off with whatever the - // latest head is, without waiting for the subscription to send us one. - // - // In some cases the subscription will send us the most recent head - // anyway when we connect (but we should not rely on this because it is - // not specced). If it happens this is fine, and the head will be - // ignored as a duplicate. - onSubscribe := func() { - err := ht.handleInitialHead(ctx) - if err != nil { - ht.log.Errorw("Error handling initial head", "err", err.Error()) - } - } +func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) start(context.Context) error { + ht.eng.Go(ht.backfillLoop) + ht.eng.Go(ht.broadcastLoop) - ht.wgDone.Add(3) - go ht.headListener.ListenForNewHeads(onSubscribe, ht.handleNewHead, ht.wgDone.Done) - go ht.backfillLoop() - go ht.broadcastLoop() + ht.mailMon.Monitor(ht.broadcastMB, "HeadTracker", "Broadcast", ht.chainID.String()) - ht.mailMon.Monitor(ht.broadcastMB, "HeadTracker", "Broadcast", ht.chainID.String()) - - return nil - }) + return nil } func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) handleInitialHead(ctx context.Context) error { @@ -176,23 +175,8 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) handleInitialHead(ctx context.Con return nil } -// Close stops HeadTracker service. -func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) Close() error { - return ht.StopOnce("HeadTracker", func() error { - close(ht.chStop) - ht.wgDone.Wait() - return ht.broadcastMB.Close() - }) -} - -func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) Name() string { - return ht.log.Name() -} - -func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) HealthReport() map[string]error { - report := map[string]error{ht.Name(): ht.Healthy()} - services.CopyHealth(report, ht.headListener.HealthReport()) - return report +func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) close() error { + return ht.broadcastMB.Close() } func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) Backfill(ctx context.Context, headWithChain HTH) (err error) { @@ -236,8 +220,7 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) handleNewHead(ctx context.Context "blockDifficulty", head.BlockDifficulty(), ) - err := ht.headSaver.Save(ctx, head) - if ctx.Err() != nil { + if err := ht.headSaver.Save(ctx, head); ctx.Err() != nil { return nil } else if err != nil { return fmt.Errorf("failed to save head: %#v: %w", head, err) @@ -264,16 +247,15 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) handleNewHead(ctx context.Context if prevLatestFinalized != nil && head.BlockNumber() <= prevLatestFinalized.BlockNumber() { promOldHead.WithLabelValues(ht.chainID.String()).Inc() - ht.log.Criticalf("Got very old block with number %d (highest seen was %d). This is a problem and either means a very deep re-org occurred, one of the RPC nodes has gotten far out of sync, or the chain went backwards in block numbers. This node may not function correctly without manual intervention.", head.BlockNumber(), prevHead.BlockNumber()) - ht.SvcErrBuffer.Append(errors.New("got very old block")) + err := fmt.Errorf("got very old block with number %d (highest seen was %d)", head.BlockNumber(), prevHead.BlockNumber()) + ht.log.Critical("Got very old block. Either a very deep re-org occurred, one of the RPC nodes has gotten far out of sync, or the chain went backwards in block numbers. This node may not function correctly without manual intervention.", "err", err) + ht.eng.EmitHealthErr(err) } } return nil } -func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) broadcastLoop() { - defer ht.wgDone.Done() - +func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) broadcastLoop(ctx context.Context) { samplingInterval := ht.htConfig.SamplingInterval() if samplingInterval > 0 { ht.log.Debugf("Head sampling is enabled - sampling interval is set to: %v", samplingInterval) @@ -281,7 +263,7 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) broadcastLoop() { defer debounceHead.Stop() for { select { - case <-ht.chStop: + case <-ctx.Done(): return case <-debounceHead.C: item := ht.broadcastMB.RetrieveLatestAndClear() @@ -295,7 +277,7 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) broadcastLoop() { ht.log.Info("Head sampling is disabled - callback will be called on every head") for { select { - case <-ht.chStop: + case <-ctx.Done(): return case <-ht.broadcastMB.Notify(): for { @@ -310,15 +292,10 @@ func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) broadcastLoop() { } } -func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) backfillLoop() { - defer ht.wgDone.Done() - - ctx, cancel := ht.chStop.NewCtx() - defer cancel() - +func (ht *headTracker[HTH, S, ID, BLOCK_HASH]) backfillLoop(ctx context.Context) { for { select { - case <-ht.chStop: + case <-ctx.Done(): return case <-ht.backfillMB.Notify(): for { diff --git a/common/txmgr/confirmer.go b/common/txmgr/confirmer.go index 0099f4a2ee..d67bd45122 100644 --- a/common/txmgr/confirmer.go +++ b/common/txmgr/confirmer.go @@ -102,6 +102,10 @@ var ( }, []string{"chainID"}) ) +type confirmerHeadTracker[HEAD types.Head[BLOCK_HASH], BLOCK_HASH types.Hashable] interface { + LatestAndFinalizedBlock(ctx context.Context) (latest, finalized HEAD, err error) +} + // Confirmer is a broad service which performs four different tasks in sequence on every new longest chain // Step 1: Mark that all currently pending transaction attempts were broadcast before this block // Step 2: Check pending transactions for receipts @@ -133,14 +137,15 @@ type Confirmer[ ks txmgrtypes.KeyStore[ADDR, CHAIN_ID, SEQ] enabledAddresses []ADDR - mb *mailbox.Mailbox[HEAD] - stopCh services.StopChan - wg sync.WaitGroup - initSync sync.Mutex - isStarted bool - + mb *mailbox.Mailbox[HEAD] + stopCh services.StopChan + wg sync.WaitGroup + initSync sync.Mutex + isStarted bool nConsecutiveBlocksChainTooShort int isReceiptNil func(R) bool + + headTracker confirmerHeadTracker[HEAD, BLOCK_HASH] } func NewConfirmer[ @@ -164,6 +169,7 @@ func NewConfirmer[ lggr logger.Logger, isReceiptNil func(R) bool, stuckTxDetector txmgrtypes.StuckTxDetector[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], + headTracker confirmerHeadTracker[HEAD, BLOCK_HASH], ) *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE] { lggr = logger.Named(lggr, "Confirmer") return &Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]{ @@ -181,6 +187,7 @@ func NewConfirmer[ mb: mailbox.NewSingle[HEAD](), isReceiptNil: isReceiptNil, stuckTxDetector: stuckTxDetector, + headTracker: headTracker, } } @@ -297,7 +304,20 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) pro return fmt.Errorf("CheckConfirmedMissingReceipt failed: %w", err) } - if err := ec.CheckForReceipts(ctx, head.BlockNumber()); err != nil { + _, latestFinalizedHead, err := ec.headTracker.LatestAndFinalizedBlock(ctx) + if err != nil { + return fmt.Errorf("failed to retrieve latest finalized head: %w", err) + } + + if !latestFinalizedHead.IsValid() { + return fmt.Errorf("latest finalized head is not valid") + } + + if latestFinalizedHead.BlockNumber() > head.BlockNumber() { + ec.lggr.Debugw("processHead received old block", "latestFinalizedHead", latestFinalizedHead.BlockNumber(), "headNum", head.BlockNumber(), "time", time.Since(mark), "id", "confirmer") + } + + if err := ec.CheckForReceipts(ctx, head.BlockNumber(), latestFinalizedHead.BlockNumber()); err != nil { return fmt.Errorf("CheckForReceipts failed: %w", err) } @@ -318,7 +338,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) pro ec.lggr.Debugw("Finished RebroadcastWhereNecessary", "headNum", head.BlockNumber(), "time", time.Since(mark), "id", "confirmer") mark = time.Now() - if err := ec.EnsureConfirmedTransactionsInLongestChain(ctx, head); err != nil { + if err := ec.EnsureConfirmedTransactionsInLongestChain(ctx, head, latestFinalizedHead.BlockNumber()); err != nil { return fmt.Errorf("EnsureConfirmedTransactionsInLongestChain failed: %w", err) } @@ -395,8 +415,8 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Che return } -// CheckForReceipts finds attempts that are still pending and checks to see if a receipt is present for the given block number -func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) CheckForReceipts(ctx context.Context, blockNum int64) error { +// CheckForReceipts finds attempts that are still pending and checks to see if a receipt is present for the given block number. +func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) CheckForReceipts(ctx context.Context, blockNum int64, latestFinalizedBlockNum int64) error { attempts, err := ec.txStore.FindTxAttemptsRequiringReceiptFetch(ctx, ec.chainID) if err != nil { return fmt.Errorf("FindTxAttemptsRequiringReceiptFetch failed: %w", err) @@ -443,7 +463,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Che return fmt.Errorf("unable to mark txes as 'confirmed_missing_receipt': %w", err) } - if err := ec.txStore.MarkOldTxesMissingReceiptAsErrored(ctx, blockNum, ec.chainConfig.FinalityDepth(), ec.chainID); err != nil { + if err := ec.txStore.MarkOldTxesMissingReceiptAsErrored(ctx, blockNum, latestFinalizedBlockNum, ec.chainID); err != nil { return fmt.Errorf("unable to confirm buried unconfirmed txes': %w", err) } return nil @@ -1019,22 +1039,30 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) han } } -// EnsureConfirmedTransactionsInLongestChain finds all confirmed txes up to the depth +// EnsureConfirmedTransactionsInLongestChain finds all confirmed txes up to the earliest head // of the given chain and ensures that every one has a receipt with a block hash that is // in the given chain. // // If any of the confirmed transactions does not have a receipt in the chain, it has been // re-org'd out and will be rebroadcast. -func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) EnsureConfirmedTransactionsInLongestChain(ctx context.Context, head types.Head[BLOCK_HASH]) error { - if head.ChainLength() < ec.chainConfig.FinalityDepth() { - logArgs := []interface{}{ - "chainLength", head.ChainLength(), "finalityDepth", ec.chainConfig.FinalityDepth(), - } +func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) EnsureConfirmedTransactionsInLongestChain(ctx context.Context, head types.Head[BLOCK_HASH], latestFinalizedHeadNumber int64) error { + logArgs := []interface{}{ + "chainLength", head.ChainLength(), "latestFinalizedHead number", latestFinalizedHeadNumber, + } + + if head.BlockNumber() < latestFinalizedHeadNumber { + errMsg := "current head is shorter than latest finalized head" + ec.lggr.Errorw(errMsg, append(logArgs, "head block number", head.BlockNumber())...) + return errors.New(errMsg) + } + + calculatedFinalityDepth := uint32(head.BlockNumber() - latestFinalizedHeadNumber) + if head.ChainLength() < calculatedFinalityDepth { if ec.nConsecutiveBlocksChainTooShort > logAfterNConsecutiveBlocksChainTooShort { - warnMsg := "Chain length supplied for re-org detection was shorter than FinalityDepth. Re-org protection is not working properly. This could indicate a problem with the remote RPC endpoint, a compatibility issue with a particular blockchain, a bug with this particular blockchain, heads table being truncated too early, remote node out of sync, or something else. If this happens a lot please raise a bug with the Chainlink team including a log output sample and details of the chain and RPC endpoint you are using." + warnMsg := "Chain length supplied for re-org detection was shorter than the depth from the latest head to the finalized head. Re-org protection is not working properly. This could indicate a problem with the remote RPC endpoint, a compatibility issue with a particular blockchain, a bug with this particular blockchain, heads table being truncated too early, remote node out of sync, or something else. If this happens a lot please raise a bug with the Chainlink team including a log output sample and details of the chain and RPC endpoint you are using." ec.lggr.Warnw(warnMsg, append(logArgs, "nConsecutiveBlocksChainTooShort", ec.nConsecutiveBlocksChainTooShort)...) } else { - logMsg := "Chain length supplied for re-org detection was shorter than FinalityDepth" + logMsg := "Chain length supplied for re-org detection was shorter than the depth from the latest head to the finalized head" ec.lggr.Debugw(logMsg, append(logArgs, "nConsecutiveBlocksChainTooShort", ec.nConsecutiveBlocksChainTooShort)...) } ec.nConsecutiveBlocksChainTooShort++ diff --git a/common/txmgr/models.go b/common/txmgr/models.go index dd121a2c7c..ca5e7d4f25 100644 --- a/common/txmgr/models.go +++ b/common/txmgr/models.go @@ -11,4 +11,5 @@ const ( TxUnconfirmed = txmgrtypes.TxState("unconfirmed") TxConfirmed = txmgrtypes.TxState("confirmed") TxConfirmedMissingReceipt = txmgrtypes.TxState("confirmed_missing_receipt") + TxFinalized = txmgrtypes.TxState("finalized") ) diff --git a/common/txmgr/reaper.go b/common/txmgr/reaper.go index 932b58f643..0c797548b1 100644 --- a/common/txmgr/reaper.go +++ b/common/txmgr/reaper.go @@ -14,7 +14,6 @@ import ( // Reaper handles periodic database cleanup for Txm type Reaper[CHAIN_ID types.ID] struct { store txmgrtypes.TxHistoryReaper[CHAIN_ID] - config txmgrtypes.ReaperChainConfig txConfig txmgrtypes.ReaperTransactionsConfig chainID CHAIN_ID log logger.Logger @@ -25,10 +24,9 @@ type Reaper[CHAIN_ID types.ID] struct { } // NewReaper instantiates a new reaper object -func NewReaper[CHAIN_ID types.ID](lggr logger.Logger, store txmgrtypes.TxHistoryReaper[CHAIN_ID], config txmgrtypes.ReaperChainConfig, txConfig txmgrtypes.ReaperTransactionsConfig, chainID CHAIN_ID) *Reaper[CHAIN_ID] { +func NewReaper[CHAIN_ID types.ID](lggr logger.Logger, store txmgrtypes.TxHistoryReaper[CHAIN_ID], txConfig txmgrtypes.ReaperTransactionsConfig, chainID CHAIN_ID) *Reaper[CHAIN_ID] { r := &Reaper[CHAIN_ID]{ store, - config, txConfig, chainID, logger.Named(lggr, "Reaper"), @@ -103,13 +101,12 @@ func (r *Reaper[CHAIN_ID]) ReapTxes(headNum int64) error { r.log.Debug("Transactions.ReaperThreshold set to 0; skipping ReapTxes") return nil } - minBlockNumberToKeep := headNum - int64(r.config.FinalityDepth()) mark := time.Now() timeThreshold := mark.Add(-threshold) - r.log.Debugw(fmt.Sprintf("reaping old txes created before %s", timeThreshold.Format(time.RFC3339)), "ageThreshold", threshold, "timeThreshold", timeThreshold, "minBlockNumberToKeep", minBlockNumberToKeep) + r.log.Debugw(fmt.Sprintf("reaping old txes created before %s", timeThreshold.Format(time.RFC3339)), "ageThreshold", threshold, "timeThreshold", timeThreshold) - if err := r.store.ReapTxHistory(ctx, minBlockNumberToKeep, timeThreshold, r.chainID); err != nil { + if err := r.store.ReapTxHistory(ctx, timeThreshold, r.chainID); err != nil { return err } diff --git a/common/txmgr/txmgr.go b/common/txmgr/txmgr.go index fc27e930c3..28d505e5e0 100644 --- a/common/txmgr/txmgr.go +++ b/common/txmgr/txmgr.go @@ -108,6 +108,7 @@ type Txm[ broadcaster *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] confirmer *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE] tracker *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE] + finalizer txmgrtypes.Finalizer[BLOCK_HASH, HEAD] fwdMgr txmgrtypes.ForwarderManager[ADDR] txAttemptBuilder txmgrtypes.TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] newErrorClassifier NewErrorClassifier @@ -143,6 +144,7 @@ func NewTxm[ confirmer *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE], resender *Resender[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE], tracker *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE], + finalizer txmgrtypes.Finalizer[BLOCK_HASH, HEAD], newErrorClassifierFunc NewErrorClassifier, ) *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE] { b := Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]{ @@ -165,13 +167,14 @@ func NewTxm[ resender: resender, tracker: tracker, newErrorClassifier: newErrorClassifierFunc, + finalizer: finalizer, } if txCfg.ResendAfterThreshold() <= 0 { b.logger.Info("Resender: Disabled") } if txCfg.ReaperThreshold() > 0 && txCfg.ReaperInterval() > 0 { - b.reaper = NewReaper[CHAIN_ID](lggr, b.txStore, cfg, txCfg, chainId) + b.reaper = NewReaper[CHAIN_ID](lggr, b.txStore, txCfg, chainId) } else { b.logger.Info("TxReaper: Disabled") } @@ -199,6 +202,10 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Start(ctx return fmt.Errorf("Txm: Tracker failed to start: %w", err) } + if err := ms.Start(ctx, b.finalizer); err != nil { + return fmt.Errorf("Txm: Finalizer failed to start: %w", err) + } + b.logger.Info("Txm starting runLoop") b.wg.Add(1) go b.runLoop() @@ -293,6 +300,7 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) HealthRepo services.CopyHealth(report, b.broadcaster.HealthReport()) services.CopyHealth(report, b.confirmer.HealthReport()) services.CopyHealth(report, b.txAttemptBuilder.HealthReport()) + services.CopyHealth(report, b.finalizer.HealthReport()) }) if b.txConfig.ForwardersEnabled() { @@ -415,6 +423,7 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) runLoop() case head := <-b.chHeads: b.confirmer.mb.Deliver(head) b.tracker.mb.Deliver(head.BlockNumber()) + b.finalizer.DeliverLatestHead(head) case reset := <-b.reset: // This check prevents the weird edge-case where you can select // into this block after chStop has already been closed and the @@ -446,6 +455,10 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) runLoop() if err != nil && (!errors.Is(err, services.ErrAlreadyStopped) || !errors.Is(err, services.ErrCannotStopUnstarted)) { b.logger.Errorw(fmt.Sprintf("Failed to Close Tracker: %v", err), "err", err) } + err = b.finalizer.Close() + if err != nil && (!errors.Is(err, services.ErrAlreadyStopped) || !errors.Is(err, services.ErrCannotStopUnstarted)) { + b.logger.Errorw(fmt.Sprintf("Failed to Close Finalizer: %v", err), "err", err) + } return case <-keysChanged: // This check prevents the weird edge-case where you can select @@ -641,12 +654,13 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) GetTransac } switch tx.State { case TxUnconfirmed, TxConfirmedMissingReceipt: - // Return unconfirmed for ConfirmedMissingReceipt since a receipt is required to determine if it is finalized - return commontypes.Unconfirmed, nil + // Return pending for ConfirmedMissingReceipt since a receipt is required to consider it as unconfirmed + return commontypes.Pending, nil case TxConfirmed: - // TODO: Check for finality and return finalized status - // Return unconfirmed if tx receipt's block is newer than the latest finalized block + // Return unconfirmed for confirmed transactions because they are not yet finalized return commontypes.Unconfirmed, nil + case TxFinalized: + return commontypes.Finalized, nil case TxFatalError: // Use an ErrorClassifier to determine if the transaction is considered Fatal txErr := b.newErrorClassifier(tx.GetError()) diff --git a/common/txmgr/types/config.go b/common/txmgr/types/config.go index 4d9af5f067..8b11a45d11 100644 --- a/common/txmgr/types/config.go +++ b/common/txmgr/types/config.go @@ -5,7 +5,6 @@ import "time" type TransactionManagerChainConfig interface { BroadcasterChainConfig ConfirmerChainConfig - ReaperChainConfig } type TransactionManagerFeeConfig interface { @@ -74,11 +73,6 @@ type ResenderTransactionsConfig interface { MaxInFlight() uint32 } -// ReaperConfig is the config subset used by the reaper -type ReaperChainConfig interface { - FinalityDepth() uint32 -} - type ReaperTransactionsConfig interface { ReaperInterval() time.Duration ReaperThreshold() time.Duration diff --git a/common/txmgr/types/finalizer.go b/common/txmgr/types/finalizer.go new file mode 100644 index 0000000000..be3c897d0e --- /dev/null +++ b/common/txmgr/types/finalizer.go @@ -0,0 +1,12 @@ +package types + +import ( + "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/common/types" +) + +type Finalizer[BLOCK_HASH types.Hashable, HEAD types.Head[BLOCK_HASH]] interface { + // interfaces for running the underlying estimator + services.Service + DeliverLatestHead(head HEAD) bool +} diff --git a/common/txmgr/types/mocks/reaper_chain_config.go b/common/txmgr/types/mocks/reaper_chain_config.go deleted file mode 100644 index 0531b07170..0000000000 --- a/common/txmgr/types/mocks/reaper_chain_config.go +++ /dev/null @@ -1,77 +0,0 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" - -// ReaperConfig is an autogenerated mock type for the ReaperChainConfig type -type ReaperConfig struct { - mock.Mock -} - -type ReaperConfig_Expecter struct { - mock *mock.Mock -} - -func (_m *ReaperConfig) EXPECT() *ReaperConfig_Expecter { - return &ReaperConfig_Expecter{mock: &_m.Mock} -} - -// FinalityDepth provides a mock function with given fields: -func (_m *ReaperConfig) FinalityDepth() uint32 { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for FinalityDepth") - } - - var r0 uint32 - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - return r0 -} - -// ReaperConfig_FinalityDepth_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FinalityDepth' -type ReaperConfig_FinalityDepth_Call struct { - *mock.Call -} - -// FinalityDepth is a helper method to define mock.On call -func (_e *ReaperConfig_Expecter) FinalityDepth() *ReaperConfig_FinalityDepth_Call { - return &ReaperConfig_FinalityDepth_Call{Call: _e.mock.On("FinalityDepth")} -} - -func (_c *ReaperConfig_FinalityDepth_Call) Run(run func()) *ReaperConfig_FinalityDepth_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *ReaperConfig_FinalityDepth_Call) Return(_a0 uint32) *ReaperConfig_FinalityDepth_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *ReaperConfig_FinalityDepth_Call) RunAndReturn(run func() uint32) *ReaperConfig_FinalityDepth_Call { - _c.Call.Return(run) - return _c -} - -// NewReaperConfig creates a new instance of ReaperConfig. 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 NewReaperConfig(t interface { - mock.TestingT - Cleanup(func()) -}) *ReaperConfig { - mock := &ReaperConfig{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/common/txmgr/types/mocks/tx_store.go b/common/txmgr/types/mocks/tx_store.go index ee166638e3..4467729e16 100644 --- a/common/txmgr/types/mocks/tx_store.go +++ b/common/txmgr/types/mocks/tx_store.go @@ -1760,65 +1760,6 @@ func (_c *TxStore_HasInProgressTransaction_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_H return _c } -// IsTxFinalized provides a mock function with given fields: ctx, blockHeight, txID, chainID -func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) IsTxFinalized(ctx context.Context, blockHeight int64, txID int64, chainID CHAIN_ID) (bool, error) { - ret := _m.Called(ctx, blockHeight, txID, chainID) - - if len(ret) == 0 { - panic("no return value specified for IsTxFinalized") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, int64, int64, CHAIN_ID) (bool, error)); ok { - return rf(ctx, blockHeight, txID, chainID) - } - if rf, ok := ret.Get(0).(func(context.Context, int64, int64, CHAIN_ID) bool); ok { - r0 = rf(ctx, blockHeight, txID, chainID) - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func(context.Context, int64, int64, CHAIN_ID) error); ok { - r1 = rf(ctx, blockHeight, txID, chainID) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// TxStore_IsTxFinalized_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsTxFinalized' -type TxStore_IsTxFinalized_Call[ADDR types.Hashable, CHAIN_ID types.ID, TX_HASH types.Hashable, BLOCK_HASH types.Hashable, R txmgrtypes.ChainReceipt[TX_HASH, BLOCK_HASH], SEQ types.Sequence, FEE feetypes.Fee] struct { - *mock.Call -} - -// IsTxFinalized is a helper method to define mock.On call -// - ctx context.Context -// - blockHeight int64 -// - txID int64 -// - chainID CHAIN_ID -func (_e *TxStore_Expecter[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) IsTxFinalized(ctx interface{}, blockHeight interface{}, txID interface{}, chainID interface{}) *TxStore_IsTxFinalized_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE] { - return &TxStore_IsTxFinalized_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]{Call: _e.mock.On("IsTxFinalized", ctx, blockHeight, txID, chainID)} -} - -func (_c *TxStore_IsTxFinalized_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Run(run func(ctx context.Context, blockHeight int64, txID int64, chainID CHAIN_ID)) *TxStore_IsTxFinalized_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE] { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(int64), args[2].(int64), args[3].(CHAIN_ID)) - }) - return _c -} - -func (_c *TxStore_IsTxFinalized_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Return(finalized bool, err error) *TxStore_IsTxFinalized_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE] { - _c.Call.Return(finalized, err) - return _c -} - -func (_c *TxStore_IsTxFinalized_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) RunAndReturn(run func(context.Context, int64, int64, CHAIN_ID) (bool, error)) *TxStore_IsTxFinalized_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE] { - _c.Call.Return(run) - return _c -} - // LoadTxAttempts provides a mock function with given fields: ctx, etx func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) LoadTxAttempts(ctx context.Context, etx *txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error { ret := _m.Called(ctx, etx) @@ -1913,17 +1854,17 @@ func (_c *TxStore_MarkAllConfirmedMissingReceipt_Call[ADDR, CHAIN_ID, TX_HASH, B return _c } -// MarkOldTxesMissingReceiptAsErrored provides a mock function with given fields: ctx, blockNum, finalityDepth, chainID -func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) MarkOldTxesMissingReceiptAsErrored(ctx context.Context, blockNum int64, finalityDepth uint32, chainID CHAIN_ID) error { - ret := _m.Called(ctx, blockNum, finalityDepth, chainID) +// MarkOldTxesMissingReceiptAsErrored provides a mock function with given fields: ctx, blockNum, latestFinalizedBlockNum, chainID +func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) MarkOldTxesMissingReceiptAsErrored(ctx context.Context, blockNum int64, latestFinalizedBlockNum int64, chainID CHAIN_ID) error { + ret := _m.Called(ctx, blockNum, latestFinalizedBlockNum, chainID) if len(ret) == 0 { panic("no return value specified for MarkOldTxesMissingReceiptAsErrored") } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, int64, uint32, CHAIN_ID) error); ok { - r0 = rf(ctx, blockNum, finalityDepth, chainID) + if rf, ok := ret.Get(0).(func(context.Context, int64, int64, CHAIN_ID) error); ok { + r0 = rf(ctx, blockNum, latestFinalizedBlockNum, chainID) } else { r0 = ret.Error(0) } @@ -1939,15 +1880,15 @@ type TxStore_MarkOldTxesMissingReceiptAsErrored_Call[ADDR types.Hashable, CHAIN_ // MarkOldTxesMissingReceiptAsErrored is a helper method to define mock.On call // - ctx context.Context // - blockNum int64 -// - finalityDepth uint32 +// - latestFinalizedBlockNum int64 // - chainID CHAIN_ID -func (_e *TxStore_Expecter[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) MarkOldTxesMissingReceiptAsErrored(ctx interface{}, blockNum interface{}, finalityDepth interface{}, chainID interface{}) *TxStore_MarkOldTxesMissingReceiptAsErrored_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE] { - return &TxStore_MarkOldTxesMissingReceiptAsErrored_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]{Call: _e.mock.On("MarkOldTxesMissingReceiptAsErrored", ctx, blockNum, finalityDepth, chainID)} +func (_e *TxStore_Expecter[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) MarkOldTxesMissingReceiptAsErrored(ctx interface{}, blockNum interface{}, latestFinalizedBlockNum interface{}, chainID interface{}) *TxStore_MarkOldTxesMissingReceiptAsErrored_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE] { + return &TxStore_MarkOldTxesMissingReceiptAsErrored_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]{Call: _e.mock.On("MarkOldTxesMissingReceiptAsErrored", ctx, blockNum, latestFinalizedBlockNum, chainID)} } -func (_c *TxStore_MarkOldTxesMissingReceiptAsErrored_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Run(run func(ctx context.Context, blockNum int64, finalityDepth uint32, chainID CHAIN_ID)) *TxStore_MarkOldTxesMissingReceiptAsErrored_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE] { +func (_c *TxStore_MarkOldTxesMissingReceiptAsErrored_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Run(run func(ctx context.Context, blockNum int64, latestFinalizedBlockNum int64, chainID CHAIN_ID)) *TxStore_MarkOldTxesMissingReceiptAsErrored_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE] { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(int64), args[2].(uint32), args[3].(CHAIN_ID)) + run(args[0].(context.Context), args[1].(int64), args[2].(int64), args[3].(CHAIN_ID)) }) return _c } @@ -1957,7 +1898,7 @@ func (_c *TxStore_MarkOldTxesMissingReceiptAsErrored_Call[ADDR, CHAIN_ID, TX_HAS return _c } -func (_c *TxStore_MarkOldTxesMissingReceiptAsErrored_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) RunAndReturn(run func(context.Context, int64, uint32, CHAIN_ID) error) *TxStore_MarkOldTxesMissingReceiptAsErrored_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE] { +func (_c *TxStore_MarkOldTxesMissingReceiptAsErrored_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) RunAndReturn(run func(context.Context, int64, int64, CHAIN_ID) error) *TxStore_MarkOldTxesMissingReceiptAsErrored_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE] { _c.Call.Return(run) return _c } @@ -2069,17 +2010,17 @@ func (_c *TxStore_PruneUnstartedTxQueue_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH return _c } -// ReapTxHistory provides a mock function with given fields: ctx, minBlockNumberToKeep, timeThreshold, chainID -func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) ReapTxHistory(ctx context.Context, minBlockNumberToKeep int64, timeThreshold time.Time, chainID CHAIN_ID) error { - ret := _m.Called(ctx, minBlockNumberToKeep, timeThreshold, chainID) +// ReapTxHistory provides a mock function with given fields: ctx, timeThreshold, chainID +func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) ReapTxHistory(ctx context.Context, timeThreshold time.Time, chainID CHAIN_ID) error { + ret := _m.Called(ctx, timeThreshold, chainID) if len(ret) == 0 { panic("no return value specified for ReapTxHistory") } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, int64, time.Time, CHAIN_ID) error); ok { - r0 = rf(ctx, minBlockNumberToKeep, timeThreshold, chainID) + if rf, ok := ret.Get(0).(func(context.Context, time.Time, CHAIN_ID) error); ok { + r0 = rf(ctx, timeThreshold, chainID) } else { r0 = ret.Error(0) } @@ -2094,16 +2035,15 @@ type TxStore_ReapTxHistory_Call[ADDR types.Hashable, CHAIN_ID types.ID, TX_HASH // ReapTxHistory is a helper method to define mock.On call // - ctx context.Context -// - minBlockNumberToKeep int64 // - timeThreshold time.Time // - chainID CHAIN_ID -func (_e *TxStore_Expecter[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) ReapTxHistory(ctx interface{}, minBlockNumberToKeep interface{}, timeThreshold interface{}, chainID interface{}) *TxStore_ReapTxHistory_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE] { - return &TxStore_ReapTxHistory_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]{Call: _e.mock.On("ReapTxHistory", ctx, minBlockNumberToKeep, timeThreshold, chainID)} +func (_e *TxStore_Expecter[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) ReapTxHistory(ctx interface{}, timeThreshold interface{}, chainID interface{}) *TxStore_ReapTxHistory_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE] { + return &TxStore_ReapTxHistory_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]{Call: _e.mock.On("ReapTxHistory", ctx, timeThreshold, chainID)} } -func (_c *TxStore_ReapTxHistory_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Run(run func(ctx context.Context, minBlockNumberToKeep int64, timeThreshold time.Time, chainID CHAIN_ID)) *TxStore_ReapTxHistory_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE] { +func (_c *TxStore_ReapTxHistory_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Run(run func(ctx context.Context, timeThreshold time.Time, chainID CHAIN_ID)) *TxStore_ReapTxHistory_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE] { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(int64), args[2].(time.Time), args[3].(CHAIN_ID)) + run(args[0].(context.Context), args[1].(time.Time), args[2].(CHAIN_ID)) }) return _c } @@ -2113,7 +2053,7 @@ func (_c *TxStore_ReapTxHistory_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ return _c } -func (_c *TxStore_ReapTxHistory_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) RunAndReturn(run func(context.Context, int64, time.Time, CHAIN_ID) error) *TxStore_ReapTxHistory_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE] { +func (_c *TxStore_ReapTxHistory_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) RunAndReturn(run func(context.Context, time.Time, CHAIN_ID) error) *TxStore_ReapTxHistory_Call[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE] { _c.Call.Return(run) return _c } diff --git a/common/txmgr/types/tx_store.go b/common/txmgr/types/tx_store.go index 875339cfba..3d874cc436 100644 --- a/common/txmgr/types/tx_store.go +++ b/common/txmgr/types/tx_store.go @@ -79,6 +79,8 @@ type TransactionStore[ // 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, fromAddress ADDR, chainID CHAIN_ID) (*Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) + + // FindTransactionsConfirmedInBlockRange retrieves tx with attempts and partial receipt values for optimization purpose 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) @@ -89,7 +91,7 @@ type TransactionStore[ HasInProgressTransaction(ctx context.Context, account ADDR, chainID CHAIN_ID) (exists bool, err error) LoadTxAttempts(ctx context.Context, etx *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error MarkAllConfirmedMissingReceipt(ctx context.Context, chainID CHAIN_ID) (err error) - MarkOldTxesMissingReceiptAsErrored(ctx context.Context, blockNum int64, finalityDepth uint32, chainID CHAIN_ID) error + MarkOldTxesMissingReceiptAsErrored(ctx context.Context, blockNum int64, latestFinalizedBlockNum int64, chainID CHAIN_ID) error PreloadTxes(ctx context.Context, attempts []TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error SaveConfirmedMissingReceiptAttempt(ctx context.Context, timeout time.Duration, attempt *TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], broadcastAt time.Time) error SaveInProgressAttempt(ctx context.Context, attempt *TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error @@ -105,11 +107,10 @@ type TransactionStore[ UpdateTxUnstartedToInProgress(ctx context.Context, etx *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], attempt *TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error UpdateTxFatalError(ctx context.Context, etx *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error UpdateTxForRebroadcast(ctx context.Context, etx Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], etxAttempt TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error - IsTxFinalized(ctx context.Context, blockHeight int64, txID int64, chainID CHAIN_ID) (finalized bool, err error) } type TxHistoryReaper[CHAIN_ID types.ID] interface { - ReapTxHistory(ctx context.Context, minBlockNumberToKeep int64, timeThreshold time.Time, chainID CHAIN_ID) error + ReapTxHistory(ctx context.Context, timeThreshold time.Time, chainID CHAIN_ID) error } type UnstartedTxQueuePruner interface { diff --git a/contracts/.changeset/eight-timers-sip.md b/contracts/.changeset/eight-timers-sip.md new file mode 100644 index 0000000000..3f81544e34 --- /dev/null +++ b/contracts/.changeset/eight-timers-sip.md @@ -0,0 +1,5 @@ +--- +'@chainlink/contracts': patch +--- + +Add new channel definitions config store contract for parallel compositions #added diff --git a/contracts/.changeset/eighty-ways-vanish.md b/contracts/.changeset/eighty-ways-vanish.md new file mode 100644 index 0000000000..3a48ca4e71 --- /dev/null +++ b/contracts/.changeset/eighty-ways-vanish.md @@ -0,0 +1,5 @@ +--- +'@chainlink/contracts': patch +--- + +Publish a comment in PR mentioning the actor and informing her about avilability of Slither reports. diff --git a/contracts/.changeset/itchy-turtles-agree.md b/contracts/.changeset/itchy-turtles-agree.md new file mode 100644 index 0000000000..930ab850d9 --- /dev/null +++ b/contracts/.changeset/itchy-turtles-agree.md @@ -0,0 +1,5 @@ +--- +'@chainlink/contracts': minor +--- + +#internal Add an event with indexed topics that get hashed to Chain Reader Tester contract. diff --git a/contracts/.changeset/loud-lobsters-guess.md b/contracts/.changeset/loud-lobsters-guess.md new file mode 100644 index 0000000000..e470267e4e --- /dev/null +++ b/contracts/.changeset/loud-lobsters-guess.md @@ -0,0 +1,5 @@ +--- +'@chainlink/contracts': patch +--- + +auto: create a replication from v2_3 to v2_3_zksync diff --git a/contracts/.changeset/nasty-llamas-prove.md b/contracts/.changeset/nasty-llamas-prove.md new file mode 100644 index 0000000000..c3b26c9be3 --- /dev/null +++ b/contracts/.changeset/nasty-llamas-prove.md @@ -0,0 +1,5 @@ +--- +'@chainlink/contracts': patch +--- + +DEVSVCS-147: add a reentrancy guard for balance monitor diff --git a/contracts/.changeset/polite-masks-jog.md b/contracts/.changeset/polite-masks-jog.md new file mode 100644 index 0000000000..93fba83b55 --- /dev/null +++ b/contracts/.changeset/polite-masks-jog.md @@ -0,0 +1,5 @@ +--- +'@chainlink/contracts': patch +--- + +#internal diff --git a/contracts/.changeset/seven-donkeys-live.md b/contracts/.changeset/seven-donkeys-live.md new file mode 100644 index 0000000000..141588f5b9 --- /dev/null +++ b/contracts/.changeset/seven-donkeys-live.md @@ -0,0 +1,5 @@ +--- +'@chainlink/contracts': patch +--- + +improve cron contracts imports diff --git a/contracts/.changeset/silver-pots-cover.md b/contracts/.changeset/silver-pots-cover.md new file mode 100644 index 0000000000..93fba83b55 --- /dev/null +++ b/contracts/.changeset/silver-pots-cover.md @@ -0,0 +1,5 @@ +--- +'@chainlink/contracts': patch +--- + +#internal diff --git a/contracts/.changeset/slimy-pens-listen.md b/contracts/.changeset/slimy-pens-listen.md new file mode 100644 index 0000000000..ff81d22237 --- /dev/null +++ b/contracts/.changeset/slimy-pens-listen.md @@ -0,0 +1,5 @@ +--- +'@chainlink/contracts': patch +--- + +#internal prevent editing whether or not a DON accepts workflows diff --git a/contracts/.changeset/tender-comics-check.md b/contracts/.changeset/tender-comics-check.md new file mode 100644 index 0000000000..6ea48d92e4 --- /dev/null +++ b/contracts/.changeset/tender-comics-check.md @@ -0,0 +1,5 @@ +--- +'@chainlink/contracts': patch +--- + +#internal prevent reentrancy when configuring DON in capabilities registry diff --git a/contracts/.changeset/thirty-lamps-reply.md b/contracts/.changeset/thirty-lamps-reply.md new file mode 100644 index 0000000000..d8bcf8d4e8 --- /dev/null +++ b/contracts/.changeset/thirty-lamps-reply.md @@ -0,0 +1,5 @@ +--- +'@chainlink/contracts': patch +--- + +implement an auto registry for zksync with no forwarder interface change diff --git a/contracts/.changeset/three-stingrays-compete.md b/contracts/.changeset/three-stingrays-compete.md new file mode 100644 index 0000000000..613b278465 --- /dev/null +++ b/contracts/.changeset/three-stingrays-compete.md @@ -0,0 +1,5 @@ +--- +'@chainlink/contracts': minor +--- + +add ccip contracts to the repo diff --git a/contracts/.changeset/unlucky-rocks-marry.md b/contracts/.changeset/unlucky-rocks-marry.md new file mode 100644 index 0000000000..723bb1e130 --- /dev/null +++ b/contracts/.changeset/unlucky-rocks-marry.md @@ -0,0 +1,5 @@ +--- +'@chainlink/contracts': patch +--- + +#internal use ERC165Checker diff --git a/contracts/.gas-snapshot b/contracts/.gas-snapshot new file mode 100644 index 0000000000..3a0354d539 --- /dev/null +++ b/contracts/.gas-snapshot @@ -0,0 +1,112 @@ +CapabilitiesRegistry_AddCapabilitiesTest:test_AddCapability_NoConfigurationContract() (gas: 154832) +CapabilitiesRegistry_AddCapabilitiesTest:test_AddCapability_WithConfiguration() (gas: 178813) +CapabilitiesRegistry_AddCapabilitiesTest:test_RevertWhen_CalledByNonAdmin() (gas: 24723) +CapabilitiesRegistry_AddCapabilitiesTest:test_RevertWhen_CapabilityExists() (gas: 145703) +CapabilitiesRegistry_AddCapabilitiesTest:test_RevertWhen_ConfigurationContractDoesNotMatchInterface() (gas: 94606) +CapabilitiesRegistry_AddCapabilitiesTest:test_RevertWhen_ConfigurationContractNotDeployed() (gas: 92961) +CapabilitiesRegistry_AddDONTest:test_AddDON() (gas: 372302) +CapabilitiesRegistry_AddDONTest:test_RevertWhen_CalledByNonAdmin() (gas: 19273) +CapabilitiesRegistry_AddDONTest:test_RevertWhen_CapabilityDoesNotExist() (gas: 169752) +CapabilitiesRegistry_AddDONTest:test_RevertWhen_DeprecatedCapabilityAdded() (gas: 239789) +CapabilitiesRegistry_AddDONTest:test_RevertWhen_DuplicateCapabilityAdded() (gas: 249596) +CapabilitiesRegistry_AddDONTest:test_RevertWhen_DuplicateNodeAdded() (gas: 116890) +CapabilitiesRegistry_AddDONTest:test_RevertWhen_FaultToleranceIsZero() (gas: 43358) +CapabilitiesRegistry_AddDONTest:test_RevertWhen_NodeAlreadyBelongsToWorkflowDON() (gas: 343924) +CapabilitiesRegistry_AddDONTest:test_RevertWhen_NodeDoesNotSupportCapability() (gas: 180150) +CapabilitiesRegistry_AddNodeOperatorsTest:test_AddNodeOperators() (gas: 184135) +CapabilitiesRegistry_AddNodeOperatorsTest:test_RevertWhen_CalledByNonAdmin() (gas: 17602) +CapabilitiesRegistry_AddNodeOperatorsTest:test_RevertWhen_NodeOperatorAdminAddressZero() (gas: 18498) +CapabilitiesRegistry_AddNodesTest:test_AddsNodeParams() (gas: 358448) +CapabilitiesRegistry_AddNodesTest:test_OwnerCanAddNodes() (gas: 358414) +CapabilitiesRegistry_AddNodesTest:test_RevertWhen_AddingDuplicateP2PId() (gas: 301229) +CapabilitiesRegistry_AddNodesTest:test_RevertWhen_AddingNodeWithInvalidCapability() (gas: 55174) +CapabilitiesRegistry_AddNodesTest:test_RevertWhen_AddingNodeWithInvalidNodeOperator() (gas: 24895) +CapabilitiesRegistry_AddNodesTest:test_RevertWhen_AddingNodeWithoutCapabilities() (gas: 27669) +CapabilitiesRegistry_AddNodesTest:test_RevertWhen_CalledByNonNodeOperatorAdminAndNonOwner() (gas: 25108) +CapabilitiesRegistry_AddNodesTest:test_RevertWhen_P2PIDEmpty() (gas: 27408) +CapabilitiesRegistry_AddNodesTest:test_RevertWhen_SignerAddressEmpty() (gas: 27047) +CapabilitiesRegistry_AddNodesTest:test_RevertWhen_SignerAddressNotUnique() (gas: 309679) +CapabilitiesRegistry_DeprecateCapabilitiesTest:test_DeprecatesCapability() (gas: 89807) +CapabilitiesRegistry_DeprecateCapabilitiesTest:test_EmitsEvent() (gas: 89935) +CapabilitiesRegistry_DeprecateCapabilitiesTest:test_RevertWhen_CalledByNonAdmin() (gas: 22944) +CapabilitiesRegistry_DeprecateCapabilitiesTest:test_RevertWhen_CapabilityDoesNotExist() (gas: 16231) +CapabilitiesRegistry_DeprecateCapabilitiesTest:test_RevertWhen_CapabilityIsDeprecated() (gas: 91264) +CapabilitiesRegistry_GetCapabilitiesTest:test_ReturnsCapabilities() (gas: 135553) +CapabilitiesRegistry_GetDONsTest:test_CorrectlyFetchesDONs() (gas: 65468) +CapabilitiesRegistry_GetDONsTest:test_DoesNotIncludeRemovedDONs() (gas: 64924) +CapabilitiesRegistry_GetHashedCapabilityTest:test_CorrectlyGeneratesHashedCapabilityId() (gas: 11428) +CapabilitiesRegistry_GetHashedCapabilityTest:test_DoesNotCauseIncorrectClashes() (gas: 13087) +CapabilitiesRegistry_GetNodeOperatorsTest:test_CorrectlyFetchesNodeOperators() (gas: 36407) +CapabilitiesRegistry_GetNodeOperatorsTest:test_DoesNotIncludeRemovedNodeOperators() (gas: 38692) +CapabilitiesRegistry_GetNodesTest:test_CorrectlyFetchesNodes() (gas: 65288) +CapabilitiesRegistry_GetNodesTest:test_DoesNotIncludeRemovedNodes() (gas: 73533) +CapabilitiesRegistry_RemoveDONsTest:test_RemovesDON() (gas: 54761) +CapabilitiesRegistry_RemoveDONsTest:test_RevertWhen_CalledByNonAdmin() (gas: 15647) +CapabilitiesRegistry_RemoveDONsTest:test_RevertWhen_DONDoesNotExist() (gas: 16550) +CapabilitiesRegistry_RemoveNodeOperatorsTest:test_RemovesNodeOperator() (gas: 36122) +CapabilitiesRegistry_RemoveNodeOperatorsTest:test_RevertWhen_CalledByNonOwner() (gas: 15816) +CapabilitiesRegistry_RemoveNodesTest:test_CanAddNodeWithSameSignerAddressAfterRemoving() (gas: 115151) +CapabilitiesRegistry_RemoveNodesTest:test_CanRemoveWhenDONDeleted() (gas: 287716) +CapabilitiesRegistry_RemoveNodesTest:test_CanRemoveWhenNodeNoLongerPartOfDON() (gas: 561023) +CapabilitiesRegistry_RemoveNodesTest:test_OwnerCanRemoveNodes() (gas: 73376) +CapabilitiesRegistry_RemoveNodesTest:test_RemovesNode() (gas: 75211) +CapabilitiesRegistry_RemoveNodesTest:test_RevertWhen_CalledByNonNodeOperatorAdminAndNonOwner() (gas: 25053) +CapabilitiesRegistry_RemoveNodesTest:test_RevertWhen_NodeDoesNotExist() (gas: 18418) +CapabilitiesRegistry_RemoveNodesTest:test_RevertWhen_NodePartOfCapabilitiesDON() (gas: 385369) +CapabilitiesRegistry_RemoveNodesTest:test_RevertWhen_P2PIDEmpty() (gas: 18408) +CapabilitiesRegistry_TypeAndVersionTest:test_TypeAndVersion() (gas: 9796) +CapabilitiesRegistry_UpdateDONTest:test_RevertWhen_CalledByNonAdmin() (gas: 19415) +CapabilitiesRegistry_UpdateDONTest:test_RevertWhen_CapabilityDoesNotExist() (gas: 152914) +CapabilitiesRegistry_UpdateDONTest:test_RevertWhen_DONDoesNotExist() (gas: 17835) +CapabilitiesRegistry_UpdateDONTest:test_RevertWhen_DeprecatedCapabilityAdded() (gas: 222996) +CapabilitiesRegistry_UpdateDONTest:test_RevertWhen_DuplicateCapabilityAdded() (gas: 232804) +CapabilitiesRegistry_UpdateDONTest:test_RevertWhen_DuplicateNodeAdded() (gas: 107643) +CapabilitiesRegistry_UpdateDONTest:test_RevertWhen_NodeDoesNotSupportCapability() (gas: 163357) +CapabilitiesRegistry_UpdateDONTest:test_UpdatesDON() (gas: 371909) +CapabilitiesRegistry_UpdateNodeOperatorTest:test_RevertWhen_CalledByNonAdminAndNonOwner() (gas: 20728) +CapabilitiesRegistry_UpdateNodeOperatorTest:test_RevertWhen_NodeOperatorAdminIsZeroAddress() (gas: 20052) +CapabilitiesRegistry_UpdateNodeOperatorTest:test_RevertWhen_NodeOperatorDoesNotExist() (gas: 19790) +CapabilitiesRegistry_UpdateNodeOperatorTest:test_RevertWhen_NodeOperatorIdAndParamLengthsMismatch() (gas: 15430) +CapabilitiesRegistry_UpdateNodeOperatorTest:test_UpdatesNodeOperator() (gas: 37034) +CapabilitiesRegistry_UpdateNodesTest:test_CanUpdateParamsIfNodeSignerAddressNoLongerUsed() (gas: 256371) +CapabilitiesRegistry_UpdateNodesTest:test_OwnerCanUpdateNodes() (gas: 162166) +CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_AddingNodeWithInvalidCapability() (gas: 35873) +CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_CalledByAnotherNodeOperatorAdmin() (gas: 29200) +CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_CalledByNonNodeOperatorAdminAndNonOwner() (gas: 29377) +CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_NodeDoesNotExist() (gas: 29199) +CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_NodeSignerAlreadyAssignedToAnotherNode() (gas: 31326) +CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_P2PIDEmpty() (gas: 29165) +CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_RemovingCapabilityRequiredByCapabilityDON() (gas: 470910) +CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_RemovingCapabilityRequiredByWorkflowDON() (gas: 341191) +CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_SignerAddressEmpty() (gas: 29058) +CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_UpdatingNodeWithoutCapabilities() (gas: 27587) +CapabilitiesRegistry_UpdateNodesTest:test_UpdatesNodeParams() (gas: 162220) +KeystoneForwarder_ReportTest:test_Report_ConfigVersion() (gas: 2002057) +KeystoneForwarder_ReportTest:test_Report_FailedDeliveryWhenReceiverInterfaceNotSupported() (gas: 128934) +KeystoneForwarder_ReportTest:test_Report_FailedDeliveryWhenReceiverNotContract() (gas: 130621) +KeystoneForwarder_ReportTest:test_Report_SuccessfulDelivery() (gas: 359123) +KeystoneForwarder_ReportTest:test_Report_SuccessfulRetryWithMoreGas() (gas: 423982) +KeystoneForwarder_ReportTest:test_RevertWhen_AnySignatureIsInvalid() (gas: 86348) +KeystoneForwarder_ReportTest:test_RevertWhen_AnySignerIsInvalid() (gas: 118486) +KeystoneForwarder_ReportTest:test_RevertWhen_ReportHasDuplicateSignatures() (gas: 94516) +KeystoneForwarder_ReportTest:test_RevertWhen_ReportHasIncorrectDON() (gas: 75930) +KeystoneForwarder_ReportTest:test_RevertWhen_ReportHasInexistentConfigVersion() (gas: 76320) +KeystoneForwarder_ReportTest:test_RevertWhen_ReportIsMalformed() (gas: 45585) +KeystoneForwarder_ReportTest:test_RevertWhen_RetryingInvalidContractTransmission() (gas: 143354) +KeystoneForwarder_ReportTest:test_RevertWhen_RetryingSuccessfulTransmission() (gas: 353272) +KeystoneForwarder_ReportTest:test_RevertWhen_TooFewSignatures() (gas: 55292) +KeystoneForwarder_ReportTest:test_RevertWhen_TooManySignatures() (gas: 56050) +KeystoneForwarder_SetConfigTest:test_RevertWhen_ExcessSigners() (gas: 20184) +KeystoneForwarder_SetConfigTest:test_RevertWhen_FaultToleranceIsZero() (gas: 88057) +KeystoneForwarder_SetConfigTest:test_RevertWhen_InsufficientSigners() (gas: 14533) +KeystoneForwarder_SetConfigTest:test_RevertWhen_NotOwner() (gas: 88766) +KeystoneForwarder_SetConfigTest:test_RevertWhen_ProvidingDuplicateSigners() (gas: 114570) +KeystoneForwarder_SetConfigTest:test_RevertWhen_ProvidingZeroAddressSigner() (gas: 114225) +KeystoneForwarder_SetConfigTest:test_SetConfig_FirstTime() (gas: 1540541) +KeystoneForwarder_SetConfigTest:test_SetConfig_WhenSignersAreRemoved() (gas: 1535211) +KeystoneForwarder_TypeAndVersionTest:test_TypeAndVersion() (gas: 9641) +KeystoneRouter_SetConfigTest:test_AddForwarder_RevertWhen_NotOwner() (gas: 10978) +KeystoneRouter_SetConfigTest:test_RemoveForwarder_RevertWhen_NotOwner() (gas: 10923) +KeystoneRouter_SetConfigTest:test_RemoveForwarder_Success() (gas: 17599) +KeystoneRouter_SetConfigTest:test_Route_RevertWhen_UnauthorizedForwarder() (gas: 18552) +KeystoneRouter_SetConfigTest:test_Route_Success() (gas: 76407) \ No newline at end of file diff --git a/contracts/.prettierignore b/contracts/.prettierignore index 4b0b1887fd..440cf95afa 100644 --- a/contracts/.prettierignore +++ b/contracts/.prettierignore @@ -37,4 +37,3 @@ venv/ src/v0.8/mocks/FunctionsOracleEventsMock.sol src/v0.8/mocks/FunctionsBillingRegistryEventsMock.sol - diff --git a/contracts/README.md b/contracts/README.md index bcb07cf906..5e0b19c0cd 100644 --- a/contracts/README.md +++ b/contracts/README.md @@ -44,6 +44,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## License +Most of the contracts are licensed under the [MIT](https://choosealicense.com/licenses/mit/) license. +An exception to this is the ccip folder, which defaults to be licensed under the [BUSL-1.1](./src/v0.8/ccip/LICENSE.md) license, however, there are a few exceptions The CCIP repo is licensed under the [BUSL-1.1](./src/v0.8/ccip/LICENSE.md) license, however, there are a few exceptions diff --git a/contracts/foundry.toml b/contracts/foundry.toml index 501390eb80..4dbb8c0ead 100644 --- a/contracts/foundry.toml +++ b/contracts/foundry.toml @@ -26,7 +26,7 @@ single_line_statement_blocks = "preserve" solc_version = '0.8.24' src = 'src/v0.8/ccip' test = 'src/v0.8/ccip/test' -optimizer_runs = 800 +optimizer_runs = 800 evm_version = 'paris' [profile.functions] diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index a984ecd919..06859f3d2b 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -111,13 +111,13 @@ CommitStore_verify:test_Paused_Revert() (gas: 18568) CommitStore_verify:test_TooManyLeaves_Revert() (gas: 36848) DefensiveExampleTest:test_HappyPath_Success() (gas: 200200) DefensiveExampleTest:test_Recovery() (gas: 424476) -E2E:test_E2E_3MessagesSuccess_gas() (gas: 1106985) +E2E:test_E2E_3MessagesSuccess_gas() (gas: 1140477) EVM2EVMOffRamp__releaseOrMintToken:test__releaseOrMintToken_NotACompatiblePool_Revert() (gas: 38322) EVM2EVMOffRamp__releaseOrMintToken:test__releaseOrMintToken_Success() (gas: 104438) -EVM2EVMOffRamp__releaseOrMintToken:test__releaseOrMintToken_TokenHandlingError_transfer_Revert() (gas: 86026) +EVM2EVMOffRamp__releaseOrMintToken:test__releaseOrMintToken_TokenHandlingError_transfer_Revert() (gas: 83526) EVM2EVMOffRamp__releaseOrMintToken:test_releaseOrMintToken_InvalidDataLength_Revert() (gas: 37365) EVM2EVMOffRamp__releaseOrMintToken:test_releaseOrMintToken_ReleaseOrMintBalanceMismatch_Revert() (gas: 95013) -EVM2EVMOffRamp__releaseOrMintToken:test_releaseOrMintToken_TokenHandlingError_BalanceOf_Revert() (gas: 40341) +EVM2EVMOffRamp__releaseOrMintToken:test_releaseOrMintToken_TokenHandlingError_BalanceOf_Revert() (gas: 37841) EVM2EVMOffRamp__releaseOrMintToken:test_releaseOrMintToken_skip_ReleaseOrMintBalanceMismatch_if_pool_Revert() (gas: 87189) EVM2EVMOffRamp__releaseOrMintTokens:test_OverValueWithARLOff_Success() (gas: 381594) EVM2EVMOffRamp__releaseOrMintTokens:test_PriceNotFoundForToken_Reverts() (gas: 140568) @@ -508,7 +508,7 @@ MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithRateLimitExce MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithRateLimitReset_Success() (gas: 76561) MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithTokensOnDifferentChains_Success() (gas: 308233) MultiAggregateRateLimiter_onInboundMessage:test_ValidateMessageWithTokens_Success() (gas: 50558) -MultiAggregateRateLimiter_onOutboundMessage:test_RateLimitValueDifferentLanes_Success() (gas: 1073669578) +MultiAggregateRateLimiter_onOutboundMessage:test_RateLimitValueDifferentLanes_Success() (gas: 51181) MultiAggregateRateLimiter_onOutboundMessage:test_ValidateMessageWithNoTokens_Success() (gas: 19302) MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageFromUnauthorizedCaller_Revert() (gas: 15913) MultiAggregateRateLimiter_onOutboundMessage:test_onOutboundMessage_ValidateMessageWithDifferentTokensOnDifferentChains_Success() (gas: 209885) @@ -562,7 +562,7 @@ MultiOCR3Base_transmit:test_UnauthorizedSigner_Revert() (gas: 61275) MultiOCR3Base_transmit:test_UnconfiguredPlugin_Revert() (gas: 39933) MultiOCR3Base_transmit:test_ZeroSignatures_Revert() (gas: 33049) MultiOnRampTokenPoolReentrancy:test_OnRampTokenPoolReentrancy_Success() (gas: 233701) -MultiRampsE2E:test_E2E_3MessagesMMultiOffRampSuccess_gas() (gas: 1500580) +MultiRampsE2E:test_E2E_3MessagesMMultiOffRampSuccess_gas() (gas: 1517326) NonceManager_NonceIncrementation:test_getIncrementedOutboundNonce_Success() (gas: 37934) NonceManager_NonceIncrementation:test_incrementInboundNonce_Skip() (gas: 23706) NonceManager_NonceIncrementation:test_incrementInboundNonce_Success() (gas: 38778) @@ -630,7 +630,7 @@ OffRamp_batchExecute:test_Unhealthy_Success() (gas: 554208) OffRamp_batchExecute:test_ZeroReports_Revert() (gas: 10600) OffRamp_ccipReceive:test_Reverts() (gas: 15385) OffRamp_commit:test_CommitOnRampMismatch_Revert() (gas: 92905) -OffRamp_commit:test_FailedRMNVerification_Reverts() (gas: 64099) +OffRamp_commit:test_FailedRMNVerification_Reverts() (gas: 61599) OffRamp_commit:test_InvalidIntervalMinLargerThanMax_Revert() (gas: 68124) OffRamp_commit:test_InvalidInterval_Revert() (gas: 64291) OffRamp_commit:test_InvalidRootRevert() (gas: 63356) @@ -724,10 +724,10 @@ OffRamp_manuallyExecute:test_manuallyExecute_WithMultiReportGasOverride_Success( OffRamp_manuallyExecute:test_manuallyExecute_WithPartialMessages_Success() (gas: 347431) OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken_NotACompatiblePool_Revert() (gas: 37634) OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken_Success() (gas: 104404) -OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken_TokenHandlingError_transfer_Revert() (gas: 85342) +OffRamp_releaseOrMintSingleToken:test__releaseOrMintSingleToken_TokenHandlingError_transfer_Revert() (gas: 82842) OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_InvalidDataLength_Revert() (gas: 36752) OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_ReleaseOrMintBalanceMismatch_Revert() (gas: 94382) -OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_TokenHandlingError_BalanceOf_Revert() (gas: 39741) +OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_TokenHandlingError_BalanceOf_Revert() (gas: 37241) OffRamp_releaseOrMintSingleToken:test_releaseOrMintToken_skip_ReleaseOrMintBalanceMismatch_if_pool_Revert() (gas: 86516) OffRamp_releaseOrMintTokens:test_TokenHandlingError_Reverts() (gas: 162381) OffRamp_releaseOrMintTokens:test__releaseOrMintTokens_PoolIsNotAPool_Reverts() (gas: 23903) diff --git a/contracts/gas-snapshots/keystone.gas-snapshot b/contracts/gas-snapshots/keystone.gas-snapshot index 2880e4c0e3..ad8bc2798e 100644 --- a/contracts/gas-snapshots/keystone.gas-snapshot +++ b/contracts/gas-snapshots/keystone.gas-snapshot @@ -1,107 +1,114 @@ -CapabilitiesRegistry_AddCapabilitiesTest:test_AddCapability_NoConfigurationContract() (gas: 154832) -CapabilitiesRegistry_AddCapabilitiesTest:test_AddCapability_WithConfiguration() (gas: 178813) -CapabilitiesRegistry_AddCapabilitiesTest:test_RevertWhen_CalledByNonAdmin() (gas: 24723) -CapabilitiesRegistry_AddCapabilitiesTest:test_RevertWhen_CapabilityExists() (gas: 145703) -CapabilitiesRegistry_AddCapabilitiesTest:test_RevertWhen_ConfigurationContractDoesNotMatchInterface() (gas: 94606) -CapabilitiesRegistry_AddCapabilitiesTest:test_RevertWhen_ConfigurationContractNotDeployed() (gas: 92961) -CapabilitiesRegistry_AddDONTest:test_AddDON() (gas: 372302) -CapabilitiesRegistry_AddDONTest:test_RevertWhen_CalledByNonAdmin() (gas: 19273) -CapabilitiesRegistry_AddDONTest:test_RevertWhen_CapabilityDoesNotExist() (gas: 169752) -CapabilitiesRegistry_AddDONTest:test_RevertWhen_DeprecatedCapabilityAdded() (gas: 239789) -CapabilitiesRegistry_AddDONTest:test_RevertWhen_DuplicateCapabilityAdded() (gas: 249596) -CapabilitiesRegistry_AddDONTest:test_RevertWhen_DuplicateNodeAdded() (gas: 116890) -CapabilitiesRegistry_AddDONTest:test_RevertWhen_FaultToleranceIsZero() (gas: 43358) -CapabilitiesRegistry_AddDONTest:test_RevertWhen_NodeAlreadyBelongsToWorkflowDON() (gas: 343924) -CapabilitiesRegistry_AddDONTest:test_RevertWhen_NodeDoesNotSupportCapability() (gas: 180150) -CapabilitiesRegistry_AddNodeOperatorsTest:test_AddNodeOperators() (gas: 184135) -CapabilitiesRegistry_AddNodeOperatorsTest:test_RevertWhen_CalledByNonAdmin() (gas: 17602) -CapabilitiesRegistry_AddNodeOperatorsTest:test_RevertWhen_NodeOperatorAdminAddressZero() (gas: 18498) -CapabilitiesRegistry_AddNodesTest:test_AddsNodeParams() (gas: 358448) -CapabilitiesRegistry_AddNodesTest:test_OwnerCanAddNodes() (gas: 358414) -CapabilitiesRegistry_AddNodesTest:test_RevertWhen_AddingDuplicateP2PId() (gas: 301229) -CapabilitiesRegistry_AddNodesTest:test_RevertWhen_AddingNodeWithInvalidCapability() (gas: 55174) -CapabilitiesRegistry_AddNodesTest:test_RevertWhen_AddingNodeWithInvalidNodeOperator() (gas: 24895) -CapabilitiesRegistry_AddNodesTest:test_RevertWhen_AddingNodeWithoutCapabilities() (gas: 27669) -CapabilitiesRegistry_AddNodesTest:test_RevertWhen_CalledByNonNodeOperatorAdminAndNonOwner() (gas: 25108) -CapabilitiesRegistry_AddNodesTest:test_RevertWhen_P2PIDEmpty() (gas: 27408) -CapabilitiesRegistry_AddNodesTest:test_RevertWhen_SignerAddressEmpty() (gas: 27047) -CapabilitiesRegistry_AddNodesTest:test_RevertWhen_SignerAddressNotUnique() (gas: 309679) -CapabilitiesRegistry_DeprecateCapabilitiesTest:test_DeprecatesCapability() (gas: 89807) -CapabilitiesRegistry_DeprecateCapabilitiesTest:test_EmitsEvent() (gas: 89935) -CapabilitiesRegistry_DeprecateCapabilitiesTest:test_RevertWhen_CalledByNonAdmin() (gas: 22944) -CapabilitiesRegistry_DeprecateCapabilitiesTest:test_RevertWhen_CapabilityDoesNotExist() (gas: 16231) -CapabilitiesRegistry_DeprecateCapabilitiesTest:test_RevertWhen_CapabilityIsDeprecated() (gas: 91264) -CapabilitiesRegistry_GetCapabilitiesTest:test_ReturnsCapabilities() (gas: 135553) +CapabilitiesRegistry_AddCapabilitiesTest:test_AddCapability_NoConfigurationContract() (gas: 154809) +CapabilitiesRegistry_AddCapabilitiesTest:test_AddCapability_WithConfiguration() (gas: 180379) +CapabilitiesRegistry_AddCapabilitiesTest:test_RevertWhen_CalledByNonAdmin() (gas: 24678) +CapabilitiesRegistry_AddCapabilitiesTest:test_RevertWhen_CapabilityExists() (gas: 145613) +CapabilitiesRegistry_AddCapabilitiesTest:test_RevertWhen_ConfigurationContractDoesNotMatchInterface() (gas: 94543) +CapabilitiesRegistry_AddCapabilitiesTest:test_RevertWhen_ConfigurationContractNotDeployed() (gas: 96326) +CapabilitiesRegistry_AddDONTest:test_AddDON() (gas: 373700) +CapabilitiesRegistry_AddDONTest:test_RevertWhen_CalledByNonAdmin() (gas: 19288) +CapabilitiesRegistry_AddDONTest:test_RevertWhen_CapabilityDoesNotExist() (gas: 169767) +CapabilitiesRegistry_AddDONTest:test_RevertWhen_DeprecatedCapabilityAdded() (gas: 239739) +CapabilitiesRegistry_AddDONTest:test_RevertWhen_DuplicateCapabilityAdded() (gas: 250950) +CapabilitiesRegistry_AddDONTest:test_RevertWhen_DuplicateNodeAdded() (gas: 116905) +CapabilitiesRegistry_AddDONTest:test_RevertWhen_FaultToleranceIsZero() (gas: 43373) +CapabilitiesRegistry_AddDONTest:test_RevertWhen_NodeAlreadyBelongsToWorkflowDON() (gas: 343954) +CapabilitiesRegistry_AddDONTest:test_RevertWhen_NodeDoesNotSupportCapability() (gas: 180165) +CapabilitiesRegistry_AddDONTest_WhenMaliciousCapabilityConfigurationConfigured:test_RevertWhen_MaliciousCapabilitiesConfigContractTriesToRemoveCapabilitiesFromDONNodes() (gas: 340514) +CapabilitiesRegistry_AddNodeOperatorsTest:test_AddNodeOperators() (gas: 184157) +CapabilitiesRegistry_AddNodeOperatorsTest:test_RevertWhen_CalledByNonAdmin() (gas: 17624) +CapabilitiesRegistry_AddNodeOperatorsTest:test_RevertWhen_NodeOperatorAdminAddressZero() (gas: 18520) +CapabilitiesRegistry_AddNodesTest:test_AddsNodeParams() (gas: 358492) +CapabilitiesRegistry_AddNodesTest:test_OwnerCanAddNodes() (gas: 358458) +CapabilitiesRegistry_AddNodesTest:test_RevertWhen_AddingDuplicateP2PId() (gas: 301273) +CapabilitiesRegistry_AddNodesTest:test_RevertWhen_AddingNodeWithInvalidCapability() (gas: 55196) +CapabilitiesRegistry_AddNodesTest:test_RevertWhen_AddingNodeWithInvalidNodeOperator() (gas: 24917) +CapabilitiesRegistry_AddNodesTest:test_RevertWhen_AddingNodeWithoutCapabilities() (gas: 27691) +CapabilitiesRegistry_AddNodesTest:test_RevertWhen_CalledByNonNodeOperatorAdminAndNonOwner() (gas: 25130) +CapabilitiesRegistry_AddNodesTest:test_RevertWhen_P2PIDEmpty() (gas: 27430) +CapabilitiesRegistry_AddNodesTest:test_RevertWhen_SignerAddressEmpty() (gas: 27069) +CapabilitiesRegistry_AddNodesTest:test_RevertWhen_SignerAddressNotUnique() (gas: 309723) +CapabilitiesRegistry_DeprecateCapabilitiesTest:test_DeprecatesCapability() (gas: 89742) +CapabilitiesRegistry_DeprecateCapabilitiesTest:test_EmitsEvent() (gas: 89870) +CapabilitiesRegistry_DeprecateCapabilitiesTest:test_RevertWhen_CalledByNonAdmin() (gas: 22879) +CapabilitiesRegistry_DeprecateCapabilitiesTest:test_RevertWhen_CapabilityDoesNotExist() (gas: 16166) +CapabilitiesRegistry_DeprecateCapabilitiesTest:test_RevertWhen_CapabilityIsDeprecated() (gas: 91134) +CapabilitiesRegistry_GetCapabilitiesTest:test_ReturnsCapabilities() (gas: 135488) CapabilitiesRegistry_GetDONsTest:test_CorrectlyFetchesDONs() (gas: 65468) CapabilitiesRegistry_GetDONsTest:test_DoesNotIncludeRemovedDONs() (gas: 64924) CapabilitiesRegistry_GetHashedCapabilityTest:test_CorrectlyGeneratesHashedCapabilityId() (gas: 11428) CapabilitiesRegistry_GetHashedCapabilityTest:test_DoesNotCauseIncorrectClashes() (gas: 13087) -CapabilitiesRegistry_GetNodeOperatorsTest:test_CorrectlyFetchesNodeOperators() (gas: 36407) -CapabilitiesRegistry_GetNodeOperatorsTest:test_DoesNotIncludeRemovedNodeOperators() (gas: 38692) +CapabilitiesRegistry_GetNodeOperatorsTest:test_CorrectlyFetchesNodeOperators() (gas: 36429) +CapabilitiesRegistry_GetNodeOperatorsTest:test_DoesNotIncludeRemovedNodeOperators() (gas: 38714) CapabilitiesRegistry_GetNodesTest:test_CorrectlyFetchesNodes() (gas: 65288) -CapabilitiesRegistry_GetNodesTest:test_DoesNotIncludeRemovedNodes() (gas: 73533) -CapabilitiesRegistry_RemoveDONsTest:test_RemovesDON() (gas: 54761) +CapabilitiesRegistry_GetNodesTest:test_DoesNotIncludeRemovedNodes() (gas: 73497) +CapabilitiesRegistry_RemoveDONsTest:test_RemovesDON() (gas: 54783) CapabilitiesRegistry_RemoveDONsTest:test_RevertWhen_CalledByNonAdmin() (gas: 15647) CapabilitiesRegistry_RemoveDONsTest:test_RevertWhen_DONDoesNotExist() (gas: 16550) CapabilitiesRegistry_RemoveNodeOperatorsTest:test_RemovesNodeOperator() (gas: 36122) CapabilitiesRegistry_RemoveNodeOperatorsTest:test_RevertWhen_CalledByNonOwner() (gas: 15816) -CapabilitiesRegistry_RemoveNodesTest:test_CanAddNodeWithSameSignerAddressAfterRemoving() (gas: 115151) -CapabilitiesRegistry_RemoveNodesTest:test_CanRemoveWhenDONDeleted() (gas: 287716) -CapabilitiesRegistry_RemoveNodesTest:test_CanRemoveWhenNodeNoLongerPartOfDON() (gas: 561023) -CapabilitiesRegistry_RemoveNodesTest:test_OwnerCanRemoveNodes() (gas: 73376) -CapabilitiesRegistry_RemoveNodesTest:test_RemovesNode() (gas: 75211) -CapabilitiesRegistry_RemoveNodesTest:test_RevertWhen_CalledByNonNodeOperatorAdminAndNonOwner() (gas: 25053) -CapabilitiesRegistry_RemoveNodesTest:test_RevertWhen_NodeDoesNotExist() (gas: 18418) -CapabilitiesRegistry_RemoveNodesTest:test_RevertWhen_NodePartOfCapabilitiesDON() (gas: 385369) -CapabilitiesRegistry_RemoveNodesTest:test_RevertWhen_P2PIDEmpty() (gas: 18408) +CapabilitiesRegistry_RemoveNodesTest:test_CanAddNodeWithSameSignerAddressAfterRemoving() (gas: 115150) +CapabilitiesRegistry_RemoveNodesTest:test_CanRemoveWhenDONDeleted() (gas: 287663) +CapabilitiesRegistry_RemoveNodesTest:test_CanRemoveWhenNodeNoLongerPartOfDON() (gas: 561017) +CapabilitiesRegistry_RemoveNodesTest:test_OwnerCanRemoveNodes() (gas: 73358) +CapabilitiesRegistry_RemoveNodesTest:test_RemovesNode() (gas: 75192) +CapabilitiesRegistry_RemoveNodesTest:test_RevertWhen_CalledByNonNodeOperatorAdminAndNonOwner() (gas: 25008) +CapabilitiesRegistry_RemoveNodesTest:test_RevertWhen_NodeDoesNotExist() (gas: 18373) +CapabilitiesRegistry_RemoveNodesTest:test_RevertWhen_NodePartOfCapabilitiesDON() (gas: 385339) +CapabilitiesRegistry_RemoveNodesTest:test_RevertWhen_P2PIDEmpty() (gas: 18363) CapabilitiesRegistry_TypeAndVersionTest:test_TypeAndVersion() (gas: 9796) -CapabilitiesRegistry_UpdateDONTest:test_RevertWhen_CalledByNonAdmin() (gas: 19415) -CapabilitiesRegistry_UpdateDONTest:test_RevertWhen_CapabilityDoesNotExist() (gas: 152914) -CapabilitiesRegistry_UpdateDONTest:test_RevertWhen_DONDoesNotExist() (gas: 17835) -CapabilitiesRegistry_UpdateDONTest:test_RevertWhen_DeprecatedCapabilityAdded() (gas: 222996) -CapabilitiesRegistry_UpdateDONTest:test_RevertWhen_DuplicateCapabilityAdded() (gas: 232804) -CapabilitiesRegistry_UpdateDONTest:test_RevertWhen_DuplicateNodeAdded() (gas: 107643) -CapabilitiesRegistry_UpdateDONTest:test_RevertWhen_NodeDoesNotSupportCapability() (gas: 163357) -CapabilitiesRegistry_UpdateDONTest:test_UpdatesDON() (gas: 371909) -CapabilitiesRegistry_UpdateNodeOperatorTest:test_RevertWhen_CalledByNonAdminAndNonOwner() (gas: 20631) -CapabilitiesRegistry_UpdateNodeOperatorTest:test_RevertWhen_NodeOperatorAdminIsZeroAddress() (gas: 20052) -CapabilitiesRegistry_UpdateNodeOperatorTest:test_RevertWhen_NodeOperatorDoesNotExist() (gas: 19790) -CapabilitiesRegistry_UpdateNodeOperatorTest:test_RevertWhen_NodeOperatorIdAndParamLengthsMismatch() (gas: 15430) -CapabilitiesRegistry_UpdateNodeOperatorTest:test_UpdatesNodeOperator() (gas: 36937) -CapabilitiesRegistry_UpdateNodesTest:test_CanUpdateParamsIfNodeSignerAddressNoLongerUsed() (gas: 256157) -CapabilitiesRegistry_UpdateNodesTest:test_OwnerCanUpdateNodes() (gas: 162059) -CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_AddingNodeWithInvalidCapability() (gas: 35766) -CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_CalledByNonNodeOperatorAdminAndNonOwner() (gas: 25069) -CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_NodeDoesNotExist() (gas: 27308) -CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_NodeSignerAlreadyAssignedToAnotherNode() (gas: 29219) -CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_P2PIDEmpty() (gas: 27296) -CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_RemovingCapabilityRequiredByCapabilityDON() (gas: 470803) -CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_RemovingCapabilityRequiredByWorkflowDON() (gas: 341084) -CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_SignerAddressEmpty() (gas: 26951) -CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_UpdatingNodeWithoutCapabilities() (gas: 25480) -CapabilitiesRegistry_UpdateNodesTest:test_UpdatesNodeParams() (gas: 162113) -KeystoneForwarder_ReportTest:test_Report_ConfigVersion() (gas: 1797755) -KeystoneForwarder_ReportTest:test_Report_FailedDeliveryWhenReceiverInterfaceNotSupported() (gas: 125910) -KeystoneForwarder_ReportTest:test_Report_FailedDeliveryWhenReceiverNotContract() (gas: 127403) -KeystoneForwarder_ReportTest:test_Report_SuccessfulDelivery() (gas: 155928) -KeystoneForwarder_ReportTest:test_RevertWhen_AlreadyAttempted() (gas: 152358) -KeystoneForwarder_ReportTest:test_RevertWhen_AnySignatureIsInvalid() (gas: 86348) -KeystoneForwarder_ReportTest:test_RevertWhen_AnySignerIsInvalid() (gas: 118486) +CapabilitiesRegistry_UpdateDONTest:test_RevertWhen_CalledByNonAdmin() (gas: 19323) +CapabilitiesRegistry_UpdateDONTest:test_RevertWhen_CapabilityDoesNotExist() (gas: 152958) +CapabilitiesRegistry_UpdateDONTest:test_RevertWhen_DONDoesNotExist() (gas: 17749) +CapabilitiesRegistry_UpdateDONTest:test_RevertWhen_DeprecatedCapabilityAdded() (gas: 222975) +CapabilitiesRegistry_UpdateDONTest:test_RevertWhen_DuplicateCapabilityAdded() (gas: 236986) +CapabilitiesRegistry_UpdateDONTest:test_RevertWhen_DuplicateNodeAdded() (gas: 107687) +CapabilitiesRegistry_UpdateDONTest:test_RevertWhen_NodeDoesNotSupportCapability() (gas: 163401) +CapabilitiesRegistry_UpdateDONTest:test_UpdatesDON() (gas: 373317) +CapabilitiesRegistry_UpdateNodeOperatorTest:test_RevertWhen_CalledByNonAdminAndNonOwner() (gas: 20684) +CapabilitiesRegistry_UpdateNodeOperatorTest:test_RevertWhen_NodeOperatorAdminIsZeroAddress() (gas: 20008) +CapabilitiesRegistry_UpdateNodeOperatorTest:test_RevertWhen_NodeOperatorDoesNotExist() (gas: 19746) +CapabilitiesRegistry_UpdateNodeOperatorTest:test_RevertWhen_NodeOperatorIdAndParamLengthsMismatch() (gas: 15386) +CapabilitiesRegistry_UpdateNodeOperatorTest:test_UpdatesNodeOperator() (gas: 36990) +CapabilitiesRegistry_UpdateNodesTest:test_CanUpdateParamsIfNodeSignerAddressNoLongerUsed() (gas: 256437) +CapabilitiesRegistry_UpdateNodesTest:test_OwnerCanUpdateNodes() (gas: 162210) +CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_AddingNodeWithInvalidCapability() (gas: 35895) +CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_CalledByAnotherNodeOperatorAdmin() (gas: 29222) +CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_CalledByNonNodeOperatorAdminAndNonOwner() (gas: 29399) +CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_NodeDoesNotExist() (gas: 29221) +CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_NodeSignerAlreadyAssignedToAnotherNode() (gas: 31348) +CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_P2PIDEmpty() (gas: 29187) +CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_RemovingCapabilityRequiredByCapabilityDON() (gas: 470947) +CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_RemovingCapabilityRequiredByWorkflowDON() (gas: 341228) +CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_SignerAddressEmpty() (gas: 29080) +CapabilitiesRegistry_UpdateNodesTest:test_RevertWhen_UpdatingNodeWithoutCapabilities() (gas: 27609) +CapabilitiesRegistry_UpdateNodesTest:test_UpdatesNodeParams() (gas: 162264) +KeystoneForwarder_ReportTest:test_Report_ConfigVersion() (gas: 2003568) +KeystoneForwarder_ReportTest:test_Report_FailedDeliveryWhenReceiverInterfaceNotSupported() (gas: 124908) +KeystoneForwarder_ReportTest:test_Report_FailedDeliveryWhenReceiverNotContract() (gas: 126927) +KeystoneForwarder_ReportTest:test_Report_SuccessfulDelivery() (gas: 361243) +KeystoneForwarder_ReportTest:test_Report_SuccessfulRetryWithMoreGas() (gas: 501084) +KeystoneForwarder_ReportTest:test_RevertWhen_AnySignatureIsInvalid() (gas: 86326) +KeystoneForwarder_ReportTest:test_RevertWhen_AnySignerIsInvalid() (gas: 118521) +KeystoneForwarder_ReportTest:test_RevertWhen_AttemptingTransmissionWithInsufficientGas() (gas: 96279) KeystoneForwarder_ReportTest:test_RevertWhen_ReportHasDuplicateSignatures() (gas: 94516) KeystoneForwarder_ReportTest:test_RevertWhen_ReportHasIncorrectDON() (gas: 75930) KeystoneForwarder_ReportTest:test_RevertWhen_ReportHasInexistentConfigVersion() (gas: 76298) -KeystoneForwarder_ReportTest:test_RevertWhen_ReportIsMalformed() (gas: 45585) +KeystoneForwarder_ReportTest:test_RevertWhen_ReportIsMalformed() (gas: 45563) +KeystoneForwarder_ReportTest:test_RevertWhen_RetryingInvalidContractTransmission() (gas: 143591) +KeystoneForwarder_ReportTest:test_RevertWhen_RetryingSuccessfulTransmission() (gas: 354042) KeystoneForwarder_ReportTest:test_RevertWhen_TooFewSignatures() (gas: 55292) KeystoneForwarder_ReportTest:test_RevertWhen_TooManySignatures() (gas: 56050) KeystoneForwarder_SetConfigTest:test_RevertWhen_ExcessSigners() (gas: 20184) KeystoneForwarder_SetConfigTest:test_RevertWhen_FaultToleranceIsZero() (gas: 88057) KeystoneForwarder_SetConfigTest:test_RevertWhen_InsufficientSigners() (gas: 14533) -KeystoneForwarder_SetConfigTest:test_RevertWhen_NotOwner() (gas: 88788) -KeystoneForwarder_SetConfigTest:test_RevertWhen_ProvidingDuplicateSigners() (gas: 114507) -KeystoneForwarder_SetConfigTest:test_SetConfig_FirstTime() (gas: 1539921) -KeystoneForwarder_SetConfigTest:test_SetConfig_WhenSignersAreRemoved() (gas: 1534476) +KeystoneForwarder_SetConfigTest:test_RevertWhen_NotOwner() (gas: 88766) +KeystoneForwarder_SetConfigTest:test_RevertWhen_ProvidingDuplicateSigners() (gas: 114570) +KeystoneForwarder_SetConfigTest:test_RevertWhen_ProvidingZeroAddressSigner() (gas: 114225) +KeystoneForwarder_SetConfigTest:test_SetConfig_FirstTime() (gas: 1540541) +KeystoneForwarder_SetConfigTest:test_SetConfig_WhenSignersAreRemoved() (gas: 1535211) KeystoneForwarder_TypeAndVersionTest:test_TypeAndVersion() (gas: 9641) KeystoneRouter_SetConfigTest:test_AddForwarder_RevertWhen_NotOwner() (gas: 10978) KeystoneRouter_SetConfigTest:test_RemoveForwarder_RevertWhen_NotOwner() (gas: 10923) -KeystoneRouter_SetConfigTest:test_Route_RevertWhen_UnauthorizedForwarder() (gas: 18553) -KeystoneRouter_SetConfigTest:test_Route_Success() (gas: 75629) \ No newline at end of file +KeystoneRouter_SetConfigTest:test_RemoveForwarder_Success() (gas: 17599) +KeystoneRouter_SetConfigTest:test_Route_RevertWhen_UnauthorizedForwarder() (gas: 18552) +KeystoneRouter_SetConfigTest:test_Route_Success() (gas: 79379) \ No newline at end of file diff --git a/contracts/gas-snapshots/liquiditymanager.gas-snapshot b/contracts/gas-snapshots/liquiditymanager.gas-snapshot index 6f67684cfc..4966013617 100644 --- a/contracts/gas-snapshots/liquiditymanager.gas-snapshot +++ b/contracts/gas-snapshots/liquiditymanager.gas-snapshot @@ -3,9 +3,9 @@ LiquidityManager_addLiquidity:test_addLiquiditySuccess() (gas: 279154) LiquidityManager_rebalanceLiquidity:test_InsufficientLiquidityReverts() (gas: 206745) LiquidityManager_rebalanceLiquidity:test_InvalidRemoteChainReverts() (gas: 192319) LiquidityManager_rebalanceLiquidity:test_rebalanceBetweenPoolsSuccess() (gas: 9141768) -LiquidityManager_rebalanceLiquidity:test_rebalanceBetweenPoolsSuccess_AlreadyFinalized() (gas: 8960797) -LiquidityManager_rebalanceLiquidity:test_rebalanceBetweenPools_MultiStageFinalization() (gas: 8956003) -LiquidityManager_rebalanceLiquidity:test_rebalanceBetweenPools_NativeRewrap() (gas: 8883801) +LiquidityManager_rebalanceLiquidity:test_rebalanceBetweenPoolsSuccess_AlreadyFinalized() (gas: 8898695) +LiquidityManager_rebalanceLiquidity:test_rebalanceBetweenPools_MultiStageFinalization() (gas: 8893901) +LiquidityManager_rebalanceLiquidity:test_rebalanceBetweenPools_NativeRewrap() (gas: 8821699) LiquidityManager_rebalanceLiquidity:test_rebalanceLiquiditySuccess() (gas: 382897) LiquidityManager_receive:test_receive_success() (gas: 21182) LiquidityManager_removeLiquidity:test_InsufficientLiquidityReverts() (gas: 184869) @@ -19,7 +19,7 @@ LiquidityManager_setFinanceRole:test_OnlyOwnerReverts() (gas: 10987) LiquidityManager_setFinanceRole:test_setFinanceRoleSuccess() (gas: 21836) LiquidityManager_setLocalLiquidityContainer:test_OnlyOwnerReverts() (gas: 11052) LiquidityManager_setLocalLiquidityContainer:test_ReverstWhen_CalledWithTheZeroAddress() (gas: 10643) -LiquidityManager_setLocalLiquidityContainer:test_setLocalLiquidityContainerSuccess() (gas: 3498806) +LiquidityManager_setLocalLiquidityContainer:test_setLocalLiquidityContainerSuccess() (gas: 3436651) LiquidityManager_setMinimumLiquidity:test_OnlyOwnerReverts() (gas: 10925) LiquidityManager_setMinimumLiquidity:test_setMinimumLiquiditySuccess() (gas: 36389) LiquidityManager_withdrawERC20:test_withdrawERC20Reverts() (gas: 180359) diff --git a/contracts/gas-snapshots/llo-feeds.gas-snapshot b/contracts/gas-snapshots/llo-feeds.gas-snapshot index 187d3cd89f..68f3c016f6 100644 --- a/contracts/gas-snapshots/llo-feeds.gas-snapshot +++ b/contracts/gas-snapshots/llo-feeds.gas-snapshot @@ -18,6 +18,223 @@ ByteUtilTest:test_readUint32MultiWord() (gas: 3393) ByteUtilTest:test_readUint32WithEmptyArray() (gas: 3253) ByteUtilTest:test_readUint32WithNotEnoughBytes() (gas: 3272) ByteUtilTest:test_readZeroAddress() (gas: 3365) +ChannelConfigStoreTest:testSetChannelDefinitions() (gas: 46927) +ChannelConfigStoreTest:testSupportsInterface() (gas: 8367) +ChannelConfigStoreTest:testTypeAndVersion() (gas: 9621) +DestinationFeeManagerProcessFeeTest:test_DiscountIsAppliedForNative() (gas: 52669) +DestinationFeeManagerProcessFeeTest:test_DiscountIsReturnedForNative() (gas: 52685) +DestinationFeeManagerProcessFeeTest:test_DiscountIsReturnedForNativeWithSurcharge() (gas: 78876) +DestinationFeeManagerProcessFeeTest:test_V1PayloadVerifies() (gas: 29324) +DestinationFeeManagerProcessFeeTest:test_V1PayloadVerifiesAndReturnsChange() (gas: 61187) +DestinationFeeManagerProcessFeeTest:test_V2PayloadVerifies() (gas: 121137) +DestinationFeeManagerProcessFeeTest:test_V2PayloadWithoutQuoteFails() (gas: 29669) +DestinationFeeManagerProcessFeeTest:test_V2PayloadWithoutZeroFee() (gas: 74797) +DestinationFeeManagerProcessFeeTest:test_WithdrawERC20() (gas: 72796) +DestinationFeeManagerProcessFeeTest:test_WithdrawNonAdminAddr() (gas: 56334) +DestinationFeeManagerProcessFeeTest:test_WithdrawUnwrappedNative() (gas: 26411) +DestinationFeeManagerProcessFeeTest:test_addVerifier() (gas: 131079) +DestinationFeeManagerProcessFeeTest:test_addVerifierExistingAddress() (gas: 34148) +DestinationFeeManagerProcessFeeTest:test_baseFeeIsAppliedForLink() (gas: 17214) +DestinationFeeManagerProcessFeeTest:test_baseFeeIsAppliedForNative() (gas: 20152) +DestinationFeeManagerProcessFeeTest:test_correctDiscountIsAppliedWhenBothTokensAreDiscounted() (gas: 91103) +DestinationFeeManagerProcessFeeTest:test_discountAIsNotAppliedWhenSetForOtherUsers() (gas: 56580) +DestinationFeeManagerProcessFeeTest:test_discountFeeRoundsDownWhenUneven() (gas: 52871) +DestinationFeeManagerProcessFeeTest:test_discountIsAppliedForLink() (gas: 49682) +DestinationFeeManagerProcessFeeTest:test_discountIsAppliedWith100PercentSurcharge() (gas: 78949) +DestinationFeeManagerProcessFeeTest:test_discountIsNoLongerAppliedAfterRemoving() (gas: 46567) +DestinationFeeManagerProcessFeeTest:test_discountIsNotAppliedForInvalidTokenAddress() (gas: 17582) +DestinationFeeManagerProcessFeeTest:test_discountIsNotAppliedToOtherFeeds() (gas: 54628) +DestinationFeeManagerProcessFeeTest:test_discountIsReturnedForLink() (gas: 49654) +DestinationFeeManagerProcessFeeTest:test_emptyQuoteRevertsWithError() (gas: 12231) +DestinationFeeManagerProcessFeeTest:test_eventIsEmittedAfterSurchargeIsSet() (gas: 41424) +DestinationFeeManagerProcessFeeTest:test_eventIsEmittedIfNotEnoughLink() (gas: 179229) +DestinationFeeManagerProcessFeeTest:test_eventIsEmittedUponWithdraw() (gas: 69057) +DestinationFeeManagerProcessFeeTest:test_feeIsUpdatedAfterDiscountIsRemoved() (gas: 49831) +DestinationFeeManagerProcessFeeTest:test_feeIsUpdatedAfterNewDiscountIsApplied() (gas: 67769) +DestinationFeeManagerProcessFeeTest:test_feeIsUpdatedAfterNewSurchargeIsApplied() (gas: 64460) +DestinationFeeManagerProcessFeeTest:test_feeIsZeroWith100PercentDiscount() (gas: 52091) +DestinationFeeManagerProcessFeeTest:test_getBaseRewardWithLinkQuote() (gas: 17231) +DestinationFeeManagerProcessFeeTest:test_getLinkFeeIsRoundedUp() (gas: 49853) +DestinationFeeManagerProcessFeeTest:test_getLinkRewardIsSameAsFee() (gas: 55711) +DestinationFeeManagerProcessFeeTest:test_getLinkRewardWithNativeQuoteAndSurchargeWithLinkDiscount() (gas: 82833) +DestinationFeeManagerProcessFeeTest:test_getRewardWithLinkDiscount() (gas: 49700) +DestinationFeeManagerProcessFeeTest:test_getRewardWithLinkQuoteAndLinkDiscount() (gas: 49681) +DestinationFeeManagerProcessFeeTest:test_getRewardWithNativeQuote() (gas: 20150) +DestinationFeeManagerProcessFeeTest:test_getRewardWithNativeQuoteAndSurcharge() (gas: 50885) +DestinationFeeManagerProcessFeeTest:test_linkAvailableForPaymentReturnsLinkBalance() (gas: 53172) +DestinationFeeManagerProcessFeeTest:test_nativeSurcharge0Percent() (gas: 30937) +DestinationFeeManagerProcessFeeTest:test_nativeSurcharge100Percent() (gas: 50887) +DestinationFeeManagerProcessFeeTest:test_nativeSurchargeCannotExceed100Percent() (gas: 17220) +DestinationFeeManagerProcessFeeTest:test_nativeSurchargeEventIsEmittedOnUpdate() (gas: 41402) +DestinationFeeManagerProcessFeeTest:test_noFeeIsAppliedWhenReportHasZeroFee() (gas: 51914) +DestinationFeeManagerProcessFeeTest:test_noFeeIsAppliedWhenReportHasZeroFeeAndDiscountAndSurchargeIsSet() (gas: 78172) +DestinationFeeManagerProcessFeeTest:test_nonAdminProxyUserCannotProcessFee() (gas: 24185) +DestinationFeeManagerProcessFeeTest:test_nonAdminUserCanNotSetDiscount() (gas: 19871) +DestinationFeeManagerProcessFeeTest:test_onlyCallableByOwnerReverts() (gas: 15453) +DestinationFeeManagerProcessFeeTest:test_payLinkDeficit() (gas: 198072) +DestinationFeeManagerProcessFeeTest:test_payLinkDeficitOnlyCallableByAdmin() (gas: 17415) +DestinationFeeManagerProcessFeeTest:test_payLinkDeficitPaysAllFeesProcessed() (gas: 218691) +DestinationFeeManagerProcessFeeTest:test_payLinkDeficitTwice() (gas: 202446) +DestinationFeeManagerProcessFeeTest:test_poolIdsCannotBeZeroAddress() (gas: 115317) +DestinationFeeManagerProcessFeeTest:test_processFeeAsProxy() (gas: 121475) +DestinationFeeManagerProcessFeeTest:test_processFeeDefaultReportsStillVerifiesWithEmptyQuote() (gas: 29745) +DestinationFeeManagerProcessFeeTest:test_processFeeEmitsEventIfNotEnoughLink() (gas: 165393) +DestinationFeeManagerProcessFeeTest:test_processFeeIfSubscriberIsSelf() (gas: 32563) +DestinationFeeManagerProcessFeeTest:test_processFeeNative() (gas: 178204) +DestinationFeeManagerProcessFeeTest:test_processFeeUsesCorrectDigest() (gas: 122766) +DestinationFeeManagerProcessFeeTest:test_processFeeWithDefaultReportPayloadAndQuoteStillVerifies() (gas: 31822) +DestinationFeeManagerProcessFeeTest:test_processFeeWithDiscountEmitsEvent() (gas: 245890) +DestinationFeeManagerProcessFeeTest:test_processFeeWithInvalidReportVersionFailsToDecode() (gas: 30770) +DestinationFeeManagerProcessFeeTest:test_processFeeWithNoDiscountDoesNotEmitEvent() (gas: 171109) +DestinationFeeManagerProcessFeeTest:test_processFeeWithUnwrappedNative() (gas: 186069) +DestinationFeeManagerProcessFeeTest:test_processFeeWithUnwrappedNativeLinkAddress() (gas: 135874) +DestinationFeeManagerProcessFeeTest:test_processFeeWithUnwrappedNativeLinkAddressExcessiveFee() (gas: 161459) +DestinationFeeManagerProcessFeeTest:test_processFeeWithUnwrappedNativeShortFunds() (gas: 94841) +DestinationFeeManagerProcessFeeTest:test_processFeeWithUnwrappedNativeWithExcessiveFee() (gas: 193032) +DestinationFeeManagerProcessFeeTest:test_processFeeWithWithCorruptQuotePayload() (gas: 75084) +DestinationFeeManagerProcessFeeTest:test_processFeeWithWithEmptyQuotePayload() (gas: 30006) +DestinationFeeManagerProcessFeeTest:test_processFeeWithWithZeroQuotePayload() (gas: 30056) +DestinationFeeManagerProcessFeeTest:test_processFeeWithZeroLinkNonZeroNativeWithLinkQuote() (gas: 35320) +DestinationFeeManagerProcessFeeTest:test_processFeeWithZeroLinkNonZeroNativeWithNativeQuote() (gas: 158081) +DestinationFeeManagerProcessFeeTest:test_processFeeWithZeroNativeNonZeroLinkReturnsChange() (gas: 56059) +DestinationFeeManagerProcessFeeTest:test_processFeeWithZeroNativeNonZeroLinkWithLinkQuote() (gas: 121473) +DestinationFeeManagerProcessFeeTest:test_processFeeWithZeroNativeNonZeroLinkWithNativeQuote() (gas: 38024) +DestinationFeeManagerProcessFeeTest:test_processMultipleLinkReports() (gas: 230434) +DestinationFeeManagerProcessFeeTest:test_processMultipleUnwrappedNativeReports() (gas: 264223) +DestinationFeeManagerProcessFeeTest:test_processMultipleV1Reports() (gas: 81155) +DestinationFeeManagerProcessFeeTest:test_processMultipleWrappedNativeReports() (gas: 247072) +DestinationFeeManagerProcessFeeTest:test_processPoolIdsPassedMismatched() (gas: 98793) +DestinationFeeManagerProcessFeeTest:test_processV1V2V3Reports() (gas: 215445) +DestinationFeeManagerProcessFeeTest:test_processV1V2V3ReportsWithUnwrapped() (gas: 257087) +DestinationFeeManagerProcessFeeTest:test_removeVerifierNonExistentAddress() (gas: 12822) +DestinationFeeManagerProcessFeeTest:test_removeVerifierZeroAaddress() (gas: 10678) +DestinationFeeManagerProcessFeeTest:test_reportWithNoExpiryOrFeeReturnsZero() (gas: 13615) +DestinationFeeManagerProcessFeeTest:test_revertOnSettingAnAddressZeroVerifier() (gas: 10614) +DestinationFeeManagerProcessFeeTest:test_rewardsAreCorrectlySentToEachAssociatedPoolWhenVerifyingInBulk() (gas: 263181) +DestinationFeeManagerProcessFeeTest:test_setDiscountOver100Percent() (gas: 19562) +DestinationFeeManagerProcessFeeTest:test_setRewardManagerZeroAddress() (gas: 10626) +DestinationFeeManagerProcessFeeTest:test_subscriberDiscountEventIsEmittedOnUpdate() (gas: 46329) +DestinationFeeManagerProcessFeeTest:test_surchargeFeeRoundsUpWhenUneven() (gas: 51261) +DestinationFeeManagerProcessFeeTest:test_surchargeIsApplied() (gas: 51165) +DestinationFeeManagerProcessFeeTest:test_surchargeIsAppliedForNativeFeeWithDiscount() (gas: 79356) +DestinationFeeManagerProcessFeeTest:test_surchargeIsNoLongerAppliedAfterRemoving() (gas: 47132) +DestinationFeeManagerProcessFeeTest:test_surchargeIsNotAppliedForLinkFee() (gas: 49962) +DestinationFeeManagerProcessFeeTest:test_surchargeIsNotAppliedWith100PercentDiscount() (gas: 78352) +DestinationFeeManagerProcessFeeTest:test_testRevertIfReportHasExpired() (gas: 14943) +DestinationRewardManagerClaimTest:test_claimAllRecipients() (gas: 277191) +DestinationRewardManagerClaimTest:test_claimMultipleRecipients() (gas: 154371) +DestinationRewardManagerClaimTest:test_claimRewardsWithDuplicatePoolIdsDoesNotPayoutTwice() (gas: 330208) +DestinationRewardManagerClaimTest:test_claimSingleRecipient() (gas: 89039) +DestinationRewardManagerClaimTest:test_claimUnevenAmountRoundsDown() (gas: 315411) +DestinationRewardManagerClaimTest:test_claimUnregisteredPoolId() (gas: 35164) +DestinationRewardManagerClaimTest:test_claimUnregisteredRecipient() (gas: 41201) +DestinationRewardManagerClaimTest:test_eventIsEmittedUponClaim() (gas: 86084) +DestinationRewardManagerClaimTest:test_eventIsNotEmittedUponUnsuccessfulClaim() (gas: 25050) +DestinationRewardManagerClaimTest:test_recipientsClaimMultipleDeposits() (gas: 386857) +DestinationRewardManagerClaimTest:test_singleRecipientClaimMultipleDeposits() (gas: 137777) +DestinationRewardManagerNoRecipientSet:test_claimAllRecipientsAfterRecipientsSet() (gas: 492227) +DestinationRewardManagerPayRecipientsTest:test_addFundsToPoolAsNonOwnerOrFeeManager() (gas: 11503) +DestinationRewardManagerPayRecipientsTest:test_addFundsToPoolAsOwner() (gas: 53944) +DestinationRewardManagerPayRecipientsTest:test_payAllRecipients() (gas: 250829) +DestinationRewardManagerPayRecipientsTest:test_payAllRecipientsFromNonAdminUser() (gas: 20496) +DestinationRewardManagerPayRecipientsTest:test_payAllRecipientsFromRecipientInPool() (gas: 251075) +DestinationRewardManagerPayRecipientsTest:test_payAllRecipientsWithAdditionalInvalidRecipient() (gas: 262275) +DestinationRewardManagerPayRecipientsTest:test_payAllRecipientsWithAdditionalUnregisteredRecipient() (gas: 265760) +DestinationRewardManagerPayRecipientsTest:test_payRecipientWithInvalidPool() (gas: 28908) +DestinationRewardManagerPayRecipientsTest:test_payRecipientsEmptyRecipientList() (gas: 25333) +DestinationRewardManagerPayRecipientsTest:test_payRecipientsWithInvalidPoolId() (gas: 31402) +DestinationRewardManagerPayRecipientsTest:test_paySingleRecipient() (gas: 84709) +DestinationRewardManagerPayRecipientsTest:test_paySubsetOfRecipientsInPool() (gas: 198474) +DestinationRewardManagerRecipientClaimDifferentWeightsTest:test_allRecipientsClaimingReceiveExpectedAmount() (gas: 280853) +DestinationRewardManagerRecipientClaimMultiplePoolsTest:test_claimAllRecipientsMultiplePools() (gas: 512489) +DestinationRewardManagerRecipientClaimMultiplePoolsTest:test_claimAllRecipientsSinglePool() (gas: 283649) +DestinationRewardManagerRecipientClaimMultiplePoolsTest:test_claimEmptyPoolWhenSecondPoolContainsFunds() (gas: 293497) +DestinationRewardManagerRecipientClaimMultiplePoolsTest:test_claimMultipleRecipientsMultiplePools() (gas: 263075) +DestinationRewardManagerRecipientClaimMultiplePoolsTest:test_claimMultipleRecipientsSinglePool() (gas: 154537) +DestinationRewardManagerRecipientClaimMultiplePoolsTest:test_claimSingleRecipientMultiplePools() (gas: 132653) +DestinationRewardManagerRecipientClaimMultiplePoolsTest:test_claimSingleUniqueRecipient() (gas: 106056) +DestinationRewardManagerRecipientClaimMultiplePoolsTest:test_claimUnevenAmountRoundsDown() (gas: 579776) +DestinationRewardManagerRecipientClaimMultiplePoolsTest:test_claimUnregisteredRecipient() (gas: 64664) +DestinationRewardManagerRecipientClaimMultiplePoolsTest:test_getAvailableRewardsCursorAndTotalPoolsEqual() (gas: 13074) +DestinationRewardManagerRecipientClaimMultiplePoolsTest:test_getAvailableRewardsCursorCannotBeGreaterThanTotalPools() (gas: 12703) +DestinationRewardManagerRecipientClaimMultiplePoolsTest:test_getAvailableRewardsCursorSingleResult() (gas: 22471) +DestinationRewardManagerRecipientClaimMultiplePoolsTest:test_getRewardsAvailableToRecipientInBothPools() (gas: 32248) +DestinationRewardManagerRecipientClaimMultiplePoolsTest:test_getRewardsAvailableToRecipientInBothPoolsWhereAlreadyClaimed() (gas: 148629) +DestinationRewardManagerRecipientClaimMultiplePoolsTest:test_getRewardsAvailableToRecipientInNoPools() (gas: 21728) +DestinationRewardManagerRecipientClaimMultiplePoolsTest:test_getRewardsAvailableToRecipientInSinglePool() (gas: 27765) +DestinationRewardManagerRecipientClaimMultiplePoolsTest:test_recipientsClaimMultipleDeposits() (gas: 391427) +DestinationRewardManagerRecipientClaimMultiplePoolsTest:test_singleRecipientClaimMultipleDeposits() (gas: 137862) +DestinationRewardManagerRecipientClaimUnevenWeightTest:test_allRecipientsClaimingReceiveExpectedAmount() (gas: 199546) +DestinationRewardManagerRecipientClaimUnevenWeightTest:test_allRecipientsClaimingReceiveExpectedAmountWithSmallDeposit() (gas: 219419) +DestinationRewardManagerSetRecipientsTest:test_eventIsEmittedUponSetRecipients() (gas: 191707) +DestinationRewardManagerSetRecipientsTest:test_setRecipientContainsDuplicateRecipients() (gas: 126060) +DestinationRewardManagerSetRecipientsTest:test_setRewardRecipientFromManagerAddress() (gas: 214117) +DestinationRewardManagerSetRecipientsTest:test_setRewardRecipientFromNonOwnerOrFeeManagerAddress() (gas: 21496) +DestinationRewardManagerSetRecipientsTest:test_setRewardRecipientTwice() (gas: 193280) +DestinationRewardManagerSetRecipientsTest:test_setRewardRecipientWeights() (gas: 180608) +DestinationRewardManagerSetRecipientsTest:test_setRewardRecipientWithZeroAddress() (gas: 90202) +DestinationRewardManagerSetRecipientsTest:test_setRewardRecipientWithZeroWeight() (gas: 191312) +DestinationRewardManagerSetRecipientsTest:test_setRewardRecipients() (gas: 185567) +DestinationRewardManagerSetRecipientsTest:test_setRewardRecipientsIsEmpty() (gas: 87091) +DestinationRewardManagerSetRecipientsTest:test_setSingleRewardRecipient() (gas: 110349) +DestinationRewardManagerSetupTest:test_addFeeManagerExistingAddress() (gas: 35281) +DestinationRewardManagerSetupTest:test_addFeeManagerZeroAddress() (gas: 10580) +DestinationRewardManagerSetupTest:test_addRemoveFeeManager() (gas: 48248) +DestinationRewardManagerSetupTest:test_eventEmittedUponFeeManagerUpdate() (gas: 41581) +DestinationRewardManagerSetupTest:test_eventEmittedUponFeePaid() (gas: 259172) +DestinationRewardManagerSetupTest:test_rejectsZeroLinkAddressOnConstruction() (gas: 59610) +DestinationRewardManagerSetupTest:test_removeFeeManagerNonExistentAddress() (gas: 12778) +DestinationRewardManagerSetupTest:test_setFeeManagerZeroAddress() (gas: 17084) +DestinationRewardManagerUpdateRewardRecipientsMultiplePoolsTest:test_updatePrimaryRecipientWeights() (gas: 376674) +DestinationRewardManagerUpdateRewardRecipientsTest:test_eventIsEmittedUponUpdateRecipients() (gas: 280411) +DestinationRewardManagerUpdateRewardRecipientsTest:test_onlyAdminCanUpdateRecipients() (gas: 19705) +DestinationRewardManagerUpdateRewardRecipientsTest:test_partialUpdateRecipientWeights() (gas: 221004) +DestinationRewardManagerUpdateRewardRecipientsTest:test_updateAllRecipientsWithSameAddressAndWeight() (gas: 274233) +DestinationRewardManagerUpdateRewardRecipientsTest:test_updatePartialRecipientsToSubset() (gas: 254156) +DestinationRewardManagerUpdateRewardRecipientsTest:test_updatePartialRecipientsWithExcessiveWeight() (gas: 259143) +DestinationRewardManagerUpdateRewardRecipientsTest:test_updatePartialRecipientsWithSameAddressAndWeight() (gas: 149856) +DestinationRewardManagerUpdateRewardRecipientsTest:test_updatePartialRecipientsWithUnderWeightSet() (gas: 259217) +DestinationRewardManagerUpdateRewardRecipientsTest:test_updateRecipientWeights() (gas: 372155) +DestinationRewardManagerUpdateRewardRecipientsTest:test_updateRecipientWithNewZeroAddress() (gas: 270700) +DestinationRewardManagerUpdateRewardRecipientsTest:test_updateRecipientsContainsDuplicateRecipients() (gas: 288483) +DestinationRewardManagerUpdateRewardRecipientsTest:test_updateRecipientsToDifferentLargerSet() (gas: 407780) +DestinationRewardManagerUpdateRewardRecipientsTest:test_updateRecipientsToDifferentPartialSet() (gas: 317945) +DestinationRewardManagerUpdateRewardRecipientsTest:test_updateRecipientsToDifferentSet() (gas: 377692) +DestinationRewardManagerUpdateRewardRecipientsTest:test_updateRecipientsToDifferentSetWithInvalidWeights() (gas: 312038) +DestinationRewardManagerUpdateRewardRecipientsTest:test_updateRecipientsUpdateAndRemoveExistingForLargerSet() (gas: 399603) +DestinationRewardManagerUpdateRewardRecipientsTest:test_updateRecipientsUpdateAndRemoveExistingForSmallerSet() (gas: 289433) +DestinationVerifierBulkVerifyBillingReport:test_verifyWithBulkLink() (gas: 639153) +DestinationVerifierBulkVerifyBillingReport:test_verifyWithBulkNative() (gas: 640232) +DestinationVerifierBulkVerifyBillingReport:test_verifyWithBulkNativeUnwrapped() (gas: 661796) +DestinationVerifierBulkVerifyBillingReport:test_verifyWithBulkNativeUnwrappedReturnsChange() (gas: 661751) +DestinationVerifierConstructorTest:test_falseIfIsNotCorrectInterface() (gas: 8719) +DestinationVerifierConstructorTest:test_revertsIfInitializedWithEmptyVerifierProxy() (gas: 61121) +DestinationVerifierConstructorTest:test_trueIfIsCorrectInterface() (gas: 8604) +DestinationVerifierConstructorTest:test_typeAndVersion() (gas: 2818189) +DestinationVerifierProxyInitializeVerifierTest:test_correctlySetsTheOwner() (gas: 1035181) +DestinationVerifierProxyInitializeVerifierTest:test_correctlySetsVersion() (gas: 9841) +DestinationVerifierProxyInitializeVerifierTest:test_setVerifierCalledByNoOwner() (gas: 17483) +DestinationVerifierProxyInitializeVerifierTest:test_setVerifierOk() (gas: 30622) +DestinationVerifierProxyInitializeVerifierTest:test_setVerifierWhichDoesntHonourInterface() (gas: 16851) +DestinationVerifierSetAccessControllerTest:test_emitsTheCorrectEvent() (gas: 35391) +DestinationVerifierSetAccessControllerTest:test_revertsIfCalledByNonOwner() (gas: 15089) +DestinationVerifierSetAccessControllerTest:test_successfullySetsNewAccessController() (gas: 34885) +DestinationVerifierSetAccessControllerTest:test_successfullySetsNewAccessControllerIsEmpty() (gas: 15007) +DestinationVerifierSetConfigTest:test_NoDonConfigAlreadyExists() (gas: 2874492) +DestinationVerifierSetConfigTest:test_addressesAndWeightsDoNotProduceSideEffectsInDonConfigIds() (gas: 1323177) +DestinationVerifierSetConfigTest:test_donConfigIdIsSameForSignersInDifferentOrder() (gas: 1290374) +DestinationVerifierSetConfigTest:test_removeLatestConfig() (gas: 786275) +DestinationVerifierSetConfigTest:test_removeLatestConfigWhenNoConfigShouldFail() (gas: 12870) +DestinationVerifierSetConfigTest:test_revertsIfCalledByNonOwner() (gas: 174936) +DestinationVerifierSetConfigTest:test_revertsIfDuplicateSigners() (gas: 171604) +DestinationVerifierSetConfigTest:test_revertsIfFaultToleranceIsZero() (gas: 168484) +DestinationVerifierSetConfigTest:test_revertsIfNotEnoughSigners() (gas: 11571) +DestinationVerifierSetConfigTest:test_revertsIfSetWithTooManySigners() (gas: 17943) +DestinationVerifierSetConfigTest:test_revertsIfSignerContainsZeroAddress() (gas: 324333) +DestinationVerifierSetConfigTest:test_setConfigActiveUnknownDonConfigId() (gas: 13102) +DestinationVerifierSetConfigTest:test_setConfigWithActivationTime() (gas: 1088176) +DestinationVerifierSetConfigTest:test_setConfigWithActivationTimeEarlierThanLatestConfigShouldFail() (gas: 1963414) +DestinationVerifierSetConfigTest:test_setConfigWithActivationTimeNoFutureTimeShouldFail() (gas: 259797) FeeManagerProcessFeeTest:test_DiscountIsAppliedForNative() (gas: 52645) FeeManagerProcessFeeTest:test_DiscountIsReturnedForNative() (gas: 52595) FeeManagerProcessFeeTest:test_DiscountIsReturnedForNativeWithSurcharge() (gas: 78808) @@ -108,6 +325,7 @@ FeeManagerProcessFeeTest:test_surchargeIsNoLongerAppliedAfterRemoving() (gas: 47 FeeManagerProcessFeeTest:test_surchargeIsNotAppliedForLinkFee() (gas: 49938) FeeManagerProcessFeeTest:test_surchargeIsNotAppliedWith100PercentDiscount() (gas: 78261) FeeManagerProcessFeeTest:test_testRevertIfReportHasExpired() (gas: 14919) +MultiVerifierBillingTests:test_multipleFeeManagersAndVerifiers() (gas: 4586990) RewardManagerClaimTest:test_claimAllRecipients() (gas: 277131) RewardManagerClaimTest:test_claimMultipleRecipients() (gas: 154341) RewardManagerClaimTest:test_claimRewardsWithDuplicatePoolIdsDoesNotPayoutTwice() (gas: 330086) @@ -200,6 +418,13 @@ VerifierActivateFeedTest:test_revertsIfNoFeedExistsActivate() (gas: 13179) VerifierActivateFeedTest:test_revertsIfNoFeedExistsDeactivate() (gas: 13157) VerifierActivateFeedTest:test_revertsIfNotOwnerActivateFeed() (gas: 17109) VerifierActivateFeedTest:test_revertsIfNotOwnerDeactivateFeed() (gas: 17164) +VerifierBillingTests:test_rewardsAreDistributedAccordingToWeights() (gas: 1731717) +VerifierBillingTests:test_rewardsAreDistributedAccordingToWeightsMultipleWeigths() (gas: 4460715) +VerifierBillingTests:test_rewardsAreDistributedAccordingToWeightsUsingHistoricalConfigs() (gas: 2098833) +VerifierBillingTests:test_verifyWithLinkV3Report() (gas: 1591346) +VerifierBillingTests:test_verifyWithNativeERC20() (gas: 1467256) +VerifierBillingTests:test_verifyWithNativeUnwrapped() (gas: 1376447) +VerifierBillingTests:test_verifyWithNativeUnwrappedReturnsChange() (gas: 1383493) VerifierBulkVerifyBillingReport:test_verifyMultiVersions() (gas: 476595) VerifierBulkVerifyBillingReport:test_verifyMultiVersionsReturnsVerifiedReports() (gas: 474853) VerifierBulkVerifyBillingReport:test_verifyWithBulkLink() (gas: 557541) @@ -212,6 +437,7 @@ VerifierDeactivateFeedWithVerifyTest:test_currentReportAllowsVerification() (gas VerifierDeactivateFeedWithVerifyTest:test_currentReportFailsVerification() (gas: 113388) VerifierDeactivateFeedWithVerifyTest:test_previousReportAllowsVerification() (gas: 99624) VerifierDeactivateFeedWithVerifyTest:test_previousReportFailsVerification() (gas: 69943) +VerifierInterfacesTest:test_DestinationContractInterfaces() (gas: 623467) VerifierProxyAccessControlledVerificationTest:test_proxiesToTheVerifierIfHasAccess() (gas: 208529) VerifierProxyAccessControlledVerificationTest:test_revertsIfNoAccess() (gas: 112345) VerifierProxyConstructorTest:test_correctlySetsTheCorrectAccessControllerInterface() (gas: 1485359) @@ -236,6 +462,9 @@ VerifierProxyUnsetVerifierWithPreviouslySetVerifierTest:test_correctlyUnsetsVeri VerifierProxyUnsetVerifierWithPreviouslySetVerifierTest:test_emitsAnEventAfterUnsettingVerifier() (gas: 17961) VerifierProxyVerifyTest:test_proxiesToTheCorrectVerifier() (gas: 204342) VerifierProxyVerifyTest:test_revertsIfNoVerifierConfigured() (gas: 117264) +VerifierSetAccessControllerTest:test_revertsIfCalledByNonOwner() (gas: 17196) +VerifierSetAccessControllerTest:test_setFeeManagerWhichDoesntHonourInterface() (gas: 16571) +VerifierSetAccessControllerTest:test_successfullySetsNewFeeManager() (gas: 44855) VerifierSetConfigFromSourceMultipleDigestsTest:test_correctlySetsConfigWhenDigestsAreRemoved() (gas: 542302) VerifierSetConfigFromSourceMultipleDigestsTest:test_correctlyUpdatesDigestsOnMultipleVerifiersInTheProxy() (gas: 967768) VerifierSetConfigFromSourceMultipleDigestsTest:test_correctlyUpdatesTheDigestInTheProxy() (gas: 523251) @@ -256,6 +485,9 @@ VerifierTestBillingReport:test_verifyWithLink() (gas: 275293) VerifierTestBillingReport:test_verifyWithNative() (gas: 316326) VerifierTestBillingReport:test_verifyWithNativeUnwrapped() (gas: 318574) VerifierTestBillingReport:test_verifyWithNativeUnwrappedReturnsChange() (gas: 325642) +VerifierVerifyBulkTest:test_revertsVerifyBulkIfNoAccess() (gas: 112867) +VerifierVerifyBulkTest:test_verifyBulkSingleCaseWithSingleConfig() (gas: 745046) +VerifierVerifyBulkTest:test_verifyBulkWithSingleConfigOneVerifyFails() (gas: 698203) VerifierVerifyMultipleConfigDigestTest:test_canVerifyNewerReportsWithNewerConfigs() (gas: 133961) VerifierVerifyMultipleConfigDigestTest:test_canVerifyOlderReportsWithOlderConfigs() (gas: 189865) VerifierVerifyMultipleConfigDigestTest:test_revertsIfAReportIsVerifiedWithAnExistingButIncorrectDigest() (gas: 88216) @@ -270,6 +502,18 @@ VerifierVerifySingleConfigDigestTest:test_revertsIfVerifiedByNonProxy() (gas: 10 VerifierVerifySingleConfigDigestTest:test_revertsIfVerifiedWithIncorrectAddresses() (gas: 184077) VerifierVerifySingleConfigDigestTest:test_revertsIfWrongNumberOfSigners() (gas: 110042) VerifierVerifySingleConfigDigestTest:test_setsTheCorrectEpoch() (gas: 194592) +VerifierVerifyTest:test_canVerifyNewerReportsWithNewerConfigs() (gas: 861741) +VerifierVerifyTest:test_canVerifyOlderV3ReportsWithOlderConfigs() (gas: 815984) +VerifierVerifyTest:test_failToVerifyReportIfDupSigners() (gas: 450715) +VerifierVerifyTest:test_failToVerifyReportIfNoSigners() (gas: 426492) +VerifierVerifyTest:test_failToVerifyReportIfNotEnoughSigners() (gas: 434814) +VerifierVerifyTest:test_failToVerifyReportIfSignerNotInConfig() (gas: 456866) +VerifierVerifyTest:test_revertsVerifyIfNoAccess() (gas: 109465) +VerifierVerifyTest:test_rollingOutConfiguration() (gas: 1497254) +VerifierVerifyTest:test_scenarioRollingNewChainWithHistoricConfigs() (gas: 976162) +VerifierVerifyTest:test_verifyFailsWhenReportIsOlderThanConfig() (gas: 2303366) +VerifierVerifyTest:test_verifyReport() (gas: 1434811) +VerifierVerifyTest:test_verifyTooglingActiveFlagsDonConfigs() (gas: 1918797) Verifier_accessControlledVerify:testVerifyWithAccessControl_gas() (gas: 212077) Verifier_bulkVerifyWithFee:testBulkVerifyProxyWithLinkFeeSuccess_gas() (gas: 519389) Verifier_bulkVerifyWithFee:testBulkVerifyProxyWithNativeFeeSuccess_gas() (gas: 542808) diff --git a/contracts/gas-snapshots/shared.gas-snapshot b/contracts/gas-snapshots/shared.gas-snapshot index 0848baa098..d7a4e21978 100644 --- a/contracts/gas-snapshots/shared.gas-snapshot +++ b/contracts/gas-snapshots/shared.gas-snapshot @@ -39,10 +39,10 @@ CallWithExactGas__callWithExactGas:test_CallWithExactGasSafeReturnDataExactGas() CallWithExactGas__callWithExactGas:test_NoContractReverts() (gas: 11559) CallWithExactGas__callWithExactGas:test_NoGasForCallExactCheckReverts() (gas: 15788) CallWithExactGas__callWithExactGas:test_NotEnoughGasForCallReverts() (gas: 16241) -CallWithExactGas__callWithExactGas:test_callWithExactGasSuccess(bytes,bytes4) (runs: 257, μ: 15767, ~: 15719) +CallWithExactGas__callWithExactGas:test_callWithExactGasSuccess(bytes,bytes4) (runs: 257, μ: 15766, ~: 15719) CallWithExactGas__callWithExactGasEvenIfTargetIsNoContract:test_CallWithExactGasEvenIfTargetIsNoContractExactGasSuccess() (gas: 20116) CallWithExactGas__callWithExactGasEvenIfTargetIsNoContract:test_CallWithExactGasEvenIfTargetIsNoContractReceiverErrorSuccess() (gas: 67721) -CallWithExactGas__callWithExactGasEvenIfTargetIsNoContract:test_CallWithExactGasEvenIfTargetIsNoContractSuccess(bytes,bytes4) (runs: 257, μ: 16277, ~: 16229) +CallWithExactGas__callWithExactGasEvenIfTargetIsNoContract:test_CallWithExactGasEvenIfTargetIsNoContractSuccess(bytes,bytes4) (runs: 257, μ: 16276, ~: 16229) CallWithExactGas__callWithExactGasEvenIfTargetIsNoContract:test_NoContractSuccess() (gas: 12962) CallWithExactGas__callWithExactGasEvenIfTargetIsNoContract:test_NoGasForCallExactCheckReturnFalseSuccess() (gas: 13005) CallWithExactGas__callWithExactGasEvenIfTargetIsNoContract:test_NotEnoughGasForCallReturnsFalseSuccess() (gas: 13317) diff --git a/contracts/package.json b/contracts/package.json index 8afd81e3a4..cfa8c10ec7 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -112,7 +112,7 @@ "moment": "^2.30.1", "prettier": "^3.3.3", "prettier-plugin-solidity": "^1.3.1", - "solhint": "^5.0.1", + "solhint": "^5.0.3", "solhint-plugin-chainlink-solidity": "git+https://github.com/smartcontractkit/chainlink-solhint-rules.git#v1.2.1", "solhint-plugin-prettier": "^0.1.0", "ts-node": "^10.9.2", diff --git a/contracts/pnpm-lock.yaml b/contracts/pnpm-lock.yaml index 225a0ec34c..c86c31b5ee 100644 --- a/contracts/pnpm-lock.yaml +++ b/contracts/pnpm-lock.yaml @@ -154,8 +154,8 @@ importers: specifier: ^1.3.1 version: 1.3.1(prettier@3.3.3) solhint: - specifier: ^5.0.1 - version: 5.0.1 + specifier: ^5.0.3 + version: 5.0.3 solhint-plugin-chainlink-solidity: specifier: git+https://github.com/smartcontractkit/chainlink-solhint-rules.git#v1.2.1 version: '@chainlink/solhint-plugin-chainlink-solidity@https://codeload.github.com/smartcontractkit/chainlink-solhint-rules/tar.gz/1b4c0c2663fcd983589d4f33a2e73908624ed43c' @@ -1885,6 +1885,7 @@ packages: glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} engines: {node: '>=12'} + deprecated: Glob versions prior to v9 are no longer supported globals@13.24.0: resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} @@ -2031,10 +2032,6 @@ packages: ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - ignore@5.2.4: - resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} - engines: {node: '>= 4'} - ignore@5.3.1: resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} @@ -2292,9 +2289,6 @@ packages: resolution: {integrity: sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ==} engines: {node: '>=10.0.0'} - keyv@4.5.0: - resolution: {integrity: sha512-2YvuMsA+jnFGtBareKqgANOEKe1mk3HKiXu2fRmAfyxG0MJAywNhi5ttWA3PMjl4NmpyjZNbFifR2vNjW1znfA==} - keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -3084,8 +3078,8 @@ packages: prettier: ^3.0.0 prettier-plugin-solidity: ^1.0.0 - solhint@5.0.1: - resolution: {integrity: sha512-QeQLS9HGCnIiibt+xiOa/+MuP7BWz9N7C5+Mj9pLHshdkNhuo3AzCpWmjfWVZBUuwIUO3YyCRVIcYLR3YOKGfg==} + solhint@5.0.3: + resolution: {integrity: sha512-OLCH6qm/mZTCpplTXzXTJGId1zrtNuDYP5c2e6snIv/hdRVxPfBBz/bAlL91bY/Accavkayp2Zp2BaDSrLVXTQ==} hasBin: true solidity-ast@0.4.56: @@ -5130,7 +5124,7 @@ snapshots: clone-response: 1.0.2 get-stream: 5.1.0 http-cache-semantics: 4.0.3 - keyv: 4.5.0 + keyv: 4.5.4 lowercase-keys: 2.0.0 normalize-url: 6.1.0 responselike: 2.0.1 @@ -6362,8 +6356,6 @@ snapshots: ieee754@1.2.1: {} - ignore@5.2.4: {} - ignore@5.3.1: {} immutable@4.1.0: {} @@ -6612,10 +6604,6 @@ snapshots: node-gyp-build: 4.5.0 readable-stream: 3.6.0 - keyv@4.5.0: - dependencies: - json-buffer: 3.0.1 - keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -7441,7 +7429,7 @@ snapshots: prettier-linter-helpers: 1.0.0 prettier-plugin-solidity: 1.3.1(prettier@3.3.3) - solhint@5.0.1: + solhint@5.0.3: dependencies: '@solidity-parser/parser': 0.18.0 ajv: 6.12.6 @@ -7452,7 +7440,7 @@ snapshots: cosmiconfig: 8.2.0 fast-diff: 1.2.0 glob: 8.1.0 - ignore: 5.2.4 + ignore: 5.3.1 js-yaml: 4.1.0 latest-version: 7.0.0 lodash: 4.17.21 diff --git a/contracts/scripts/ci/find_slither_report_diff.sh b/contracts/scripts/ci/find_slither_report_diff.sh new file mode 100755 index 0000000000..d0b5238a1a --- /dev/null +++ b/contracts/scripts/ci/find_slither_report_diff.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash + +set -euo pipefail + +if [[ "$#" -lt 4 ]]; then + >&2 echo "Generates a markdown file with diff in new issues detected by ChatGPT between two Slither reports." + >&2 echo "Usage: $0 [path-to-validation-prompt]" + exit 1 +fi + +if [[ -z "${OPEN_API_KEY+x}" ]]; then + >&2 echo "OPEN_API_KEY is not set." + exit 1 +fi + +first_report_path=$1 +second_report_path=$2 +new_issues_report_path=$3 +report_prompt_path=$4 +if [[ "$#" -eq 5 ]]; then + validation_prompt_path=$5 +else + validation_prompt_path="" +fi + +first_report_content=$(cat "$first_report_path" | sed 's/"//g' | sed -E 's/\\+$//g' | sed -E 's/\\+ //g') +second_report_content=$(cat "$second_report_path" | sed 's/"//g' | sed -E 's/\\+$//g' | sed -E 's/\\+ //g') +openai_prompt=$(cat "$report_prompt_path" | sed 's/"/\\"/g' | sed -E 's/\\+$//g' | sed -E 's/\\+ //g') +openai_model="gpt-4o-2024-05-13" +openai_result=$(echo '{ + "model": "'$openai_model'", + "temperature": 0.01, + "messages": [ + { + "role": "system", + "content": "'$openai_prompt' \nreport1:\n```'$first_report_content'```\nreport2:\n```'$second_report_content'```" + } + ] +}' | envsubst | curl https://api.openai.com/v1/chat/completions \ + -w "%{http_code}" \ + -o prompt_response.json \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $OPEN_API_KEY" \ + -d @- +) + +# throw error openai_result when is not 200 +if [ "$openai_result" != '200' ]; then + echo "::error::OpenAI API call failed with status $openai_result: $(cat prompt_response.json)" + exit 1 +fi + +# replace lines starting with ' -' (1space) with ' -' (2spaces) +response_content=$(cat prompt_response.json | jq -r '.choices[0].message.content') +new_issues_report_content=$(echo "$response_content" | sed -e 's/^ -/ -/g') +echo "$new_issues_report_content" > "$new_issues_report_path" + +if [[ -n "$validation_prompt_path" ]]; then + echo "::debug::Validating the diff report using the validation prompt" + openai_model="gpt-4-turbo-2024-04-09" + report_input=$(echo "$new_issues_report_content" | sed 's/"//g' | sed -E 's/\\+$//g' | sed -E 's/\\+ //g') + validation_prompt_content=$(cat "$validation_prompt_path" | sed 's/"/\\"/g' | sed -E 's/\\+$//g' | sed -E 's/\\+ //g') + validation_result=$(echo '{ + "model": "'$openai_model'", + "temperature": 0.01, + "messages": [ + { + "role": "system", + "content": "'$validation_prompt_content' \nreport1:\n```'$first_report_content'```\nreport2:\n```'$second_report_content'```\nnew_issues:\n```'$report_input'```" + } + ] + }' | envsubst | curl https://api.openai.com/v1/chat/completions \ + -w "%{http_code}" \ + -o prompt_validation_response.json \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $OPEN_API_KEY" \ + -d @- + ) + + # throw error openai_result when is not 200 + if [ "$validation_result" != '200' ]; then + echo "::error::OpenAI API call failed with status $validation_result: $(cat prompt_validation_response.json)" + exit 1 + fi + + # replace lines starting with ' -' (1space) with ' -' (2spaces) + response_content=$(cat prompt_validation_response.json | jq -r '.choices[0].message.content') + + echo "$response_content" | sed -e 's/^ -/ -/g' >> "$new_issues_report_path" + echo "" >> "$new_issues_report_path" + echo "*Confidence rating presented above is an automatic validation (self-check) of the differences between two reports generated by ChatGPT ${openai_model} model. It has a scale of 1 to 5, where 1 means that all new issues are missing and 5 that all new issues are present*." >> "$new_issues_report_path" + echo "" >> "$new_issues_report_path" + echo "*If confidence rating is low it's advised to look for differences manually by downloading Slither reports for base reference and current commit from job's artifacts*." >> "$new_issues_report_path" +fi diff --git a/contracts/scripts/ci/prompt-difference.md b/contracts/scripts/ci/prompt-difference.md new file mode 100644 index 0000000000..b7603c9748 --- /dev/null +++ b/contracts/scripts/ci/prompt-difference.md @@ -0,0 +1,21 @@ +You are a helpful expert data engineer with expertise in Blockchain and Decentralized Oracle Networks. + +Given two reports generated by Slither - a Solidity static analysis tool - provided at the bottom of the reply, your task is to help create a report for your peers with new issues introduced in the second report in order to decrease noise resulting from irrelevant changes to the report, by focusing on a single topic: **New Issues**. + +First report is provided under Heading 2 (##) called `report1` and is surrounded by triple backticks (```) to indicate the beginning and end of the report. +Second report is provided under Heading 2 (##) called `report2` and is surrounded by triple backticks (```) to indicate the beginning and end of the report. + +First report is report generated by Slither using default branch of the code repository. Second report is report generated by Slither using a feature branch of the code repository. You want to help your peers understand the impact of changes they introduced in the pull request on the codebase and whether they introduced any new issues. + +**New Issues** + +Provide a bullet point summary of new issues that were introduced in the second report. If a given issue is not present in first report, but is present in the second one, it is considered a new issue. If the count for given issue type is higher in the second report than in the first one, it is considered a new issue. +For each issue include original description text from the report together with severity level, issue ID, line number and a link to problematic line in the code. +Group the issues by their type, which is defined as Heading 2 (##). + +Output your response starting from**New Issues** in escaped, markdown text that can be sent as http body to API. Do not wrap output in code blocks. +Extract the name of the file from the first line of the report and title the new report with it in a following way: "# Slither's new issues in: " + +Remember that it might be possible that second report does not introduce any new issues. In such case, provide an empty report. + +Format **New Issues** as Heading 2 using double sharp characters (##). Otherwise, do not include any another preamble and postamble to your answer. diff --git a/contracts/scripts/ci/prompt-validation.md b/contracts/scripts/ci/prompt-validation.md new file mode 100644 index 0000000000..5fcf08e146 --- /dev/null +++ b/contracts/scripts/ci/prompt-validation.md @@ -0,0 +1,33 @@ +You are a helpful expert data engineer with expertise in Blockchain and Decentralized Oracle Networks. + +At the bottom of the reply you will find two reports generated by Slither - a Solidity static analysis tool - and another report that contains new issues found in the second report. +Your task is to evaluate how well that new issues report shows all new issues mentioned in the second Slither report and assert its completeness. +Rate your confidence in the completeness of the new issues report on a scale from 1 to 5, where 1 means it's missing all new issues and 5 means that all new issues are present. + +First report is provided under Heading 2 (##) called `report1` and is surrounded by triple backticks (```) to indicate the beginning and end of the report. +Second report is provided under Heading 2 (##) called `report2` and is surrounded by triple backticks (```) to indicate the beginning and end of the report. +New issues report is provided under Heading 2 (##) called `new_issues` and is surrounded by triple backticks (```) to indicate the beginning and end of the report. + +Use the following steps to evaluate the new issues report: +* each report begins with a summary with types of issues found and number of issues found for each type, called "# Summary for " +* group issues by type and count for each report and calculate the expected difference in number of issues for each type for each report +* exclude all issue types, for which the count for is higher in the first report than in the second one +* for each remaining issue type, compare the number of issues found in the new issues report with the expected difference +* evaluate if the new issues report captures all new issues introduced in the second report + +Do not focus on: +* the quality of the Slither reports themselves, but rather on whether all new issues from the second report are present in the new issues report +* how well the new issues report is structured or written and how well it presents new issues + +It is crucial that you ignore all differences in the reports that are not related to new issues, such as resolved issues or issues, which count has decreased. + +If a given issue is not present in first report, but is present in the second one, it is considered a new issue. Similar behaviour is expected from the new issues report. +If the count for given issue type is higher in the second report than in the first one, it is considered a new issue. + +Your report should include only a single section titled "Confidence level". +Your evaluation of the completeness of the new issues report should be displayed as a Heading 3 using triple sharp characters (###). In a new line a brief explanation of the scale used, with minimum and maximum possible values. + +Remember that it might be possible that second report does not introduce any new issues. In such case, confidence rating should be 5. + +Output your response as escaped, markdown text that can be sent as http body to API. Do not wrap output in code blocks. Do not include any partial results or statistics regarding the number of new and resolved issues in any of the reports. +Format **Confidence level** as Heading 2 using double sharp characters (##). Otherwise, do not include any another preamble and postamble to your answer. diff --git a/contracts/scripts/generate-zksync-automation-master-interface-v2_3.ts b/contracts/scripts/generate-zksync-automation-master-interface-v2_3.ts new file mode 100644 index 0000000000..1b91fd3636 --- /dev/null +++ b/contracts/scripts/generate-zksync-automation-master-interface-v2_3.ts @@ -0,0 +1,58 @@ +/** + * @description this script generates a master interface for interacting with the automation registry + * @notice run this script with pnpm ts-node ./scripts/generate-zksync-automation-master-interface-v2_3.ts + */ +import { ZKSyncAutomationRegistry2_3__factory as Registry } from '../typechain/factories/ZKSyncAutomationRegistry2_3__factory' +import { ZKSyncAutomationRegistryLogicA2_3__factory as RegistryLogicA } from '../typechain/factories/ZKSyncAutomationRegistryLogicA2_3__factory' +import { ZKSyncAutomationRegistryLogicB2_3__factory as RegistryLogicB } from '../typechain/factories/ZKSyncAutomationRegistryLogicB2_3__factory' +import { ZKSyncAutomationRegistryLogicC2_3__factory as RegistryLogicC } from '../typechain/factories/ZKSyncAutomationRegistryLogicC2_3__factory' +import { utils } from 'ethers' +import fs from 'fs' +import { exec } from 'child_process' + +const dest = 'src/v0.8/automation/interfaces/zksync' +const srcDest = `${dest}/IZKSyncAutomationRegistryMaster2_3.sol` +const tmpDest = `${dest}/tmp.txt` + +const combinedABI = [] +const abiSet = new Set() +const abis = [ + Registry.abi, + RegistryLogicA.abi, + RegistryLogicB.abi, + RegistryLogicC.abi, +] + +for (const abi of abis) { + for (const entry of abi) { + const id = utils.id(JSON.stringify(entry)) + if (!abiSet.has(id)) { + abiSet.add(id) + if ( + entry.type === 'function' && + (entry.name === 'checkUpkeep' || + entry.name === 'checkCallback' || + entry.name === 'simulatePerformUpkeep') + ) { + entry.stateMutability = 'view' // override stateMutability for check / callback / simulate functions + } + combinedABI.push(entry) + } + } +} + +const checksum = utils.id(abis.join('')) + +fs.writeFileSync(`${tmpDest}`, JSON.stringify(combinedABI)) + +const cmd = ` +cat ${tmpDest} | pnpm abi-to-sol --solidity-version ^0.8.4 --license MIT > ${srcDest} IZKSyncAutomationRegistryMaster2_3; +echo "// solhint-disable \n// abi-checksum: ${checksum}" | cat - ${srcDest} > ${tmpDest} && mv ${tmpDest} ${srcDest}; +pnpm prettier --write ${srcDest}; +` + +exec(cmd) + +console.log( + 'generated new master interface for zksync automation registry v2_3', +) diff --git a/contracts/scripts/native_solc_compile_all_automation b/contracts/scripts/native_solc_compile_all_automation index f144e4f7dc..e189e78cb0 100755 --- a/contracts/scripts/native_solc_compile_all_automation +++ b/contracts/scripts/native_solc_compile_all_automation @@ -93,7 +93,7 @@ compileContract automation/v2_2/AutomationUtils2_2.sol compileContract automation/interfaces/v2_2/IAutomationRegistryMaster.sol compileContract automation/chains/ArbitrumModule.sol compileContract automation/chains/ChainModuleBase.sol -compileContract automation/chains/OptimismModule.sol +compileContract automation/chains/OptimismModuleV2.sol compileContract automation/chains/ScrollModule.sol compileContract automation/interfaces/IChainModule.sol compileContract automation/interfaces/IAutomationV21PlusCommon.sol @@ -108,4 +108,4 @@ compileContract automation/v2_3/AutomationUtils2_3.sol compileContract automation/interfaces/v2_3/IAutomationRegistryMaster2_3.sol compileContract automation/testhelpers/MockETHUSDAggregator.sol -compileContract automation/test/v2_3/WETH9.sol +compileContract automation/test/WETH9.sol diff --git a/contracts/scripts/native_solc_compile_all_llo-feeds b/contracts/scripts/native_solc_compile_all_llo-feeds index eb17f93b0d..f0224d962c 100755 --- a/contracts/scripts/native_solc_compile_all_llo-feeds +++ b/contracts/scripts/native_solc_compile_all_llo-feeds @@ -28,15 +28,19 @@ compileContract () { "$ROOT"/contracts/src/v0.8/"$1" } -compileContract llo-feeds/Verifier.sol -compileContract llo-feeds/VerifierProxy.sol -compileContract llo-feeds/FeeManager.sol -compileContract llo-feeds/RewardManager.sol +compileContract llo-feeds/v0.3.0/Verifier.sol +compileContract llo-feeds/v0.3.0/VerifierProxy.sol +compileContract llo-feeds/v0.3.0/FeeManager.sol +compileContract llo-feeds/v0.3.0/RewardManager.sol +compileContract llo-feeds/v0.4.0/DestinationVerifier.sol +compileContract llo-feeds/v0.4.0/DestinationVerifierProxy.sol +compileContract llo-feeds/v0.4.0/DestinationFeeManager.sol +compileContract llo-feeds/v0.4.0/DestinationRewardManager.sol # Test | Mocks -compileContract llo-feeds/test/mocks/ErroredVerifier.sol -compileContract llo-feeds/test/mocks/ExposedVerifier.sol +compileContract llo-feeds/v0.3.0/test/mocks/ErroredVerifier.sol +compileContract llo-feeds/v0.3.0/test/mocks/ExposedVerifier.sol # LLO compileContract llo-feeds/dev/ChannelConfigStore.sol diff --git a/contracts/scripts/native_solc_compile_all_vrf b/contracts/scripts/native_solc_compile_all_vrf index 4bfd4ecac7..88e7b3e147 100755 --- a/contracts/scripts/native_solc_compile_all_vrf +++ b/contracts/scripts/native_solc_compile_all_vrf @@ -114,6 +114,7 @@ compileContract vrf/dev/testhelpers/VRFV2PlusWrapperConsumerExample.sol compileContract vrf/dev/testhelpers/VRFV2PlusRevertingExample.sol compileContract vrf/dev/testhelpers/VRFConsumerV2PlusUpgradeableExample.sol compileContract vrf/dev/testhelpers/VRFV2PlusMaliciousMigrator.sol +compileContractAltOpts vrf/dev/testhelpers/VRFCoordinatorTestV2_5.sol 500 compileContract vrf/dev/libraries/VRFV2PlusClient.sol compileContract vrf/dev/testhelpers/VRFCoordinatorV2Plus_V2Example.sol compileContract vrf/dev/TrustedBlockhashStore.sol diff --git a/contracts/src/v0.8/automation/ZKSyncAutomationForwarder.sol b/contracts/src/v0.8/automation/ZKSyncAutomationForwarder.sol new file mode 100644 index 0000000000..fd6eb3dee9 --- /dev/null +++ b/contracts/src/v0.8/automation/ZKSyncAutomationForwarder.sol @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.16; + +import {IAutomationRegistryConsumer} from "./interfaces/IAutomationRegistryConsumer.sol"; +import {GAS_BOUND_CALLER, IGasBoundCaller} from "./interfaces/zksync/IGasBoundCaller.sol"; + +uint256 constant PERFORM_GAS_CUSHION = 50_000; + +/** + * @title ZKSyncAutomationForwarder is a relayer that sits between the registry and the customer's target contract + * @dev The purpose of the forwarder is to give customers a consistent address to authorize against, + * which stays consistent between migrations. The Forwarder also exposes the registry address, so that users who + * want to programmatically interact with the registry (ie top up funds) can do so. + */ +contract ZKSyncAutomationForwarder { + error InvalidCaller(address); + + /// @notice the user's target contract address + address private immutable i_target; + + /// @notice the shared logic address + address private immutable i_logic; + + IAutomationRegistryConsumer private s_registry; + + constructor(address target, address registry, address logic) { + s_registry = IAutomationRegistryConsumer(registry); + i_target = target; + i_logic = logic; + } + + /** + * @notice forward is called by the registry and forwards the call to the target + * @param gasAmount is the amount of gas to use in the call + * @param data is the 4 bytes function selector + arbitrary function data + * @return success indicating whether the target call succeeded or failed + * @return gasUsed the total gas used from this forwarding call + */ + function forward(uint256 gasAmount, bytes memory data) external returns (bool success, uint256 gasUsed) { + if (msg.sender != address(s_registry)) revert InvalidCaller(msg.sender); + + uint256 g1 = gasleft(); + address target = i_target; + + assembly { + let g := gas() + // Compute g -= PERFORM_GAS_CUSHION and check for underflow + if lt(g, PERFORM_GAS_CUSHION) { + revert(0, 0) + } + g := sub(g, PERFORM_GAS_CUSHION) + // if g - g//64 <= gasAmount, revert + // (we subtract g//64 because of EIP-150) + if iszero(gt(sub(g, div(g, 64)), gasAmount)) { + revert(0, 0) + } + // solidity calls check that a contract actually exists at the destination, so we do the same + if iszero(extcodesize(target)) { + revert(0, 0) + } + } + + bytes memory returnData; + // solhint-disable-next-line avoid-low-level-calls + (success, returnData) = GAS_BOUND_CALLER.delegatecall{gas: gasAmount}( + abi.encodeWithSelector(IGasBoundCaller.gasBoundCall.selector, target, gasAmount, data) + ); + uint256 pubdataGasSpent; + if (success) { + (, pubdataGasSpent) = abi.decode(returnData, (bytes, uint256)); + } + gasUsed = g1 - gasleft() + pubdataGasSpent; + return (success, gasUsed); + } + + function getTarget() external view returns (address) { + return i_target; + } + + // solhint-disable-next-line no-complex-fallback + fallback() external payable { + // copy to memory for assembly access + address logic = i_logic; + // copied directly from OZ's Proxy contract + assembly { + // Copy msg.data. We take full control of memory in this inline assembly + // block because it will not return to Solidity code. We overwrite the + // Solidity scratch pad at memory position 0. + calldatacopy(0, 0, calldatasize()) + + // out and outsize are 0 because we don't know the size yet. + let result := delegatecall(gas(), logic, 0, calldatasize(), 0, 0) + + // Copy the returned data. + returndatacopy(0, 0, returndatasize()) + + switch result + // delegatecall returns 0 on error. + case 0 { + revert(0, returndatasize()) + } + default { + return(0, returndatasize()) + } + } + } +} diff --git a/contracts/src/v0.8/automation/chains/ArbitrumModule.sol b/contracts/src/v0.8/automation/chains/ArbitrumModule.sol index e27a0809b7..2ad6fdddc8 100644 --- a/contracts/src/v0.8/automation/chains/ArbitrumModule.sol +++ b/contracts/src/v0.8/automation/chains/ArbitrumModule.sol @@ -31,7 +31,7 @@ contract ArbitrumModule is ChainModuleBase { return ARB_SYS.arbBlockNumber(); } - function getCurrentL1Fee() external view override returns (uint256) { + function getCurrentL1Fee(uint256) external view override returns (uint256) { return ARB_GAS.getCurrentTxL1GasFees(); } diff --git a/contracts/src/v0.8/automation/chains/ChainModuleBase.sol b/contracts/src/v0.8/automation/chains/ChainModuleBase.sol index e9b082063b..c595dbd640 100644 --- a/contracts/src/v0.8/automation/chains/ChainModuleBase.sol +++ b/contracts/src/v0.8/automation/chains/ChainModuleBase.sol @@ -18,11 +18,11 @@ contract ChainModuleBase is IChainModule { return blockhash(n); } - function getCurrentL1Fee() external view virtual returns (uint256) { + function getCurrentL1Fee(uint256) external view virtual returns (uint256 l1Fee) { return 0; } - function getMaxL1Fee(uint256) external view virtual returns (uint256) { + function getMaxL1Fee(uint256) external view virtual returns (uint256 maxL1Fee) { return 0; } diff --git a/contracts/src/v0.8/automation/chains/OptimismModule.sol b/contracts/src/v0.8/automation/chains/OptimismModule.sol index 91c1c0ed96..7a46429496 100644 --- a/contracts/src/v0.8/automation/chains/OptimismModule.sol +++ b/contracts/src/v0.8/automation/chains/OptimismModule.sol @@ -4,6 +4,10 @@ pragma solidity 0.8.19; import {OVM_GasPriceOracle} from "../../vendor/@eth-optimism/contracts/v0.8.9/contracts/L2/predeploys/OVM_GasPriceOracle.sol"; import {ChainModuleBase} from "./ChainModuleBase.sol"; +/** + * @notice This contract is deprecated. Please use OptimismModuleV2 which utilizes the most recent offerings from OP + * and can estimate L1 fee with much lower cost. + */ contract OptimismModule is ChainModuleBase { /// @dev OP_L1_DATA_FEE_PADDING includes 80 bytes for L1 data padding for Optimism and BASE bytes private constant OP_L1_DATA_FEE_PADDING = @@ -16,11 +20,22 @@ contract OptimismModule is ChainModuleBase { uint256 private constant FIXED_GAS_OVERHEAD = 60_000; uint256 private constant PER_CALLDATA_BYTE_GAS_OVERHEAD = 270; - function getCurrentL1Fee() external view override returns (uint256) { - return OVM_GASPRICEORACLE.getL1Fee(bytes.concat(msg.data, OP_L1_DATA_FEE_PADDING)); + // @dev This will be updated to use the new function introduced by OP team + function getCurrentL1Fee(uint256 dataSize) external view override returns (uint256) { + return _getL1Fee(dataSize); } function getMaxL1Fee(uint256 dataSize) external view override returns (uint256) { + return _getL1Fee(dataSize); + } + + /* @notice this function provides an estimation for L1 fee incurred by calldata of a certain size + * @dev this function uses the getL1Fee function in OP gas price oracle. it estimates the exact L1 fee but it costs + * a lot of gas to call. + * @param dataSize the size of calldata + * @return l1Fee the L1 fee + */ + function _getL1Fee(uint256 dataSize) internal view returns (uint256) { // fee is 4 per 0 byte, 16 per non-zero byte. Worst case we can have all non zero-bytes. // Instead of setting bytes to non-zero, we initialize 'new bytes' of length 4*dataSize to cover for zero bytes. bytes memory txCallData = new bytes(4 * dataSize); diff --git a/contracts/src/v0.8/automation/chains/OptimismModuleV2.sol b/contracts/src/v0.8/automation/chains/OptimismModuleV2.sol new file mode 100644 index 0000000000..772b66cdf9 --- /dev/null +++ b/contracts/src/v0.8/automation/chains/OptimismModuleV2.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.19; + +import {GasPriceOracle as OVM_GasPriceOracle} from "../../vendor/@eth-optimism/contracts-bedrock/v0.17.3/src/L2/GasPriceOracle.sol"; +import {ChainModuleBase} from "./ChainModuleBase.sol"; +import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; + +/** + * @notice OptimismModuleV2 provides a cost-efficient way to get L1 fee on OP stack. + * After EIP-4844 is implemented in OP stack, the new OP upgrade includes a new function getL1FeeUpperBound to estimate + * the upper bound of current transaction's L1 fee. + */ +contract OptimismModuleV2 is ChainModuleBase, ConfirmedOwner { + error InvalidL1FeeCoefficient(uint8 coefficient); + event L1FeeCoefficientSet(uint8 coefficient); + + /// @dev OVM_GASPRICEORACLE_ADDR is the address of the OVM_GasPriceOracle precompile on Optimism. + /// @dev reference: https://community.optimism.io/docs/developers/build/transaction-fees/#estimating-the-l1-data-fee + address private constant OVM_GASPRICEORACLE_ADDR = address(0x420000000000000000000000000000000000000F); + OVM_GasPriceOracle private constant OVM_GASPRICEORACLE = OVM_GasPriceOracle(OVM_GASPRICEORACLE_ADDR); + + /// @dev L1 fee coefficient is used to account for the impact of data compression on the l1 fee + /// getL1FeeUpperBound returns the upper bound of l1 fee so this configurable coefficient will help + /// charge a predefined percentage of the upper bound. + uint8 private s_l1FeeCoefficient = 100; + uint256 private constant FIXED_GAS_OVERHEAD = 28_000; + uint256 private constant PER_CALLDATA_BYTE_GAS_OVERHEAD = 0; + + constructor() ConfirmedOwner(msg.sender) {} + + function getCurrentL1Fee(uint256 dataSize) external view override returns (uint256) { + return (s_l1FeeCoefficient * _getL1Fee(dataSize)) / 100; + } + + function getMaxL1Fee(uint256 dataSize) external view override returns (uint256) { + return _getL1Fee(dataSize); + } + + /* @notice this function provides an estimation for L1 fee incurred by calldata of a certain size + * @dev this function uses the newly provided getL1FeeUpperBound function in OP gas price oracle. this helps + * estimate L1 fee with much lower cost + * @param dataSize the size of calldata + * @return l1Fee the L1 fee + */ + function _getL1Fee(uint256 dataSize) internal view returns (uint256) { + return OVM_GASPRICEORACLE.getL1FeeUpperBound(dataSize); + } + + function getGasOverhead() + external + pure + override + returns (uint256 chainModuleFixedOverhead, uint256 chainModulePerByteOverhead) + { + return (FIXED_GAS_OVERHEAD, PER_CALLDATA_BYTE_GAS_OVERHEAD); + } + + /* @notice this function sets a new coefficient for L1 fee estimation. + * @dev this function can only be invoked by contract owner + * @param coefficient the new coefficient + */ + function setL1FeeCalculation(uint8 coefficient) external onlyOwner { + if (coefficient > 100) { + revert InvalidL1FeeCoefficient(coefficient); + } + + s_l1FeeCoefficient = coefficient; + + emit L1FeeCoefficientSet(coefficient); + } + + /* @notice this function returns the s_l1FeeCoefficient + * @return coefficient the current s_l1FeeCoefficient in effect + */ + function getL1FeeCoefficient() public view returns (uint256 coefficient) { + return s_l1FeeCoefficient; + } +} diff --git a/contracts/src/v0.8/automation/chains/ScrollModule.sol b/contracts/src/v0.8/automation/chains/ScrollModule.sol index 1e41ed3805..30fdc908d4 100644 --- a/contracts/src/v0.8/automation/chains/ScrollModule.sol +++ b/contracts/src/v0.8/automation/chains/ScrollModule.sol @@ -3,8 +3,12 @@ pragma solidity 0.8.19; import {IScrollL1GasPriceOracle} from "../../vendor/@scroll-tech/contracts/src/L2/predeploys/IScrollL1GasPriceOracle.sol"; import {ChainModuleBase} from "./ChainModuleBase.sol"; +import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; + +contract ScrollModule is ChainModuleBase, ConfirmedOwner { + error InvalidL1FeeCoefficient(uint8 coefficient); + event L1FeeCoefficientSet(uint8 coefficient); -contract ScrollModule is ChainModuleBase { /// @dev SCROLL_L1_FEE_DATA_PADDING includes 140 bytes for L1 data padding for Scroll /// @dev according to testing, this padding allows automation registry to properly estimates L1 data fee with 3-5% buffer /// @dev this MAY NOT work for a different product and this may get out of date if transmit function is changed @@ -15,14 +19,26 @@ contract ScrollModule is ChainModuleBase { address private constant SCROLL_ORACLE_ADDR = 0x5300000000000000000000000000000000000002; IScrollL1GasPriceOracle private constant SCROLL_ORACLE = IScrollL1GasPriceOracle(SCROLL_ORACLE_ADDR); + /// @dev L1 fee coefficient can be applied to reduce possibly inflated gas cost + uint8 private s_l1FeeCoefficient = 100; uint256 private constant FIXED_GAS_OVERHEAD = 45_000; uint256 private constant PER_CALLDATA_BYTE_GAS_OVERHEAD = 170; - function getCurrentL1Fee() external view override returns (uint256) { - return SCROLL_ORACLE.getL1Fee(bytes.concat(msg.data, SCROLL_L1_FEE_DATA_PADDING)); + constructor() ConfirmedOwner(msg.sender) {} + + function getCurrentL1Fee(uint256 dataSize) external view override returns (uint256) { + return (s_l1FeeCoefficient * _getL1Fee(dataSize)) / 100; } function getMaxL1Fee(uint256 dataSize) external view override returns (uint256) { + return _getL1Fee(dataSize); + } + + /* @notice this function provides an estimation for L1 fee incurred by calldata of a certain size + * @param dataSize the size of calldata + * @return l1Fee the L1 fee + */ + function _getL1Fee(uint256 dataSize) internal view returns (uint256 l1Fee) { // fee is 4 per 0 byte, 16 per non-zero byte. Worst case we can have all non zero-bytes. // Instead of setting bytes to non-zero, we initialize 'new bytes' of length 4*dataSize to cover for zero bytes. // this is the same as OP. @@ -38,4 +54,25 @@ contract ScrollModule is ChainModuleBase { { return (FIXED_GAS_OVERHEAD, PER_CALLDATA_BYTE_GAS_OVERHEAD); } + + /* @notice this function sets a new coefficient for L1 fee estimation. + * @dev this function can only be invoked by contract owner + * @param coefficient the new coefficient + */ + function setL1FeeCalculation(uint8 coefficient) external onlyOwner { + if (coefficient > 100) { + revert InvalidL1FeeCoefficient(coefficient); + } + + s_l1FeeCoefficient = coefficient; + + emit L1FeeCoefficientSet(coefficient); + } + + /* @notice this function returns the s_l1FeeCoefficient + * @return coefficient the current s_l1FeeCoefficient in effect + */ + function getL1FeeCoefficient() public view returns (uint256 coefficient) { + return s_l1FeeCoefficient; + } } diff --git a/contracts/src/v0.8/automation/interfaces/IChainModule.sol b/contracts/src/v0.8/automation/interfaces/IChainModule.sol index e3a4b32c9b..15e84f7984 100644 --- a/contracts/src/v0.8/automation/interfaces/IChainModule.sol +++ b/contracts/src/v0.8/automation/interfaces/IChainModule.sol @@ -2,26 +2,39 @@ pragma solidity ^0.8.0; interface IChainModule { - // retrieve the native block number of a chain. e.g. L2 block number on Arbitrum - function blockNumber() external view returns (uint256); + /* @notice this function provides the block number of current chain. + * @dev certain chains have its own function to retrieve block number, e.g. Arbitrum + * @return blockNumber the block number of the current chain. + */ + function blockNumber() external view returns (uint256 blockNumber); - // retrieve the native block hash of a chain. - function blockHash(uint256) external view returns (bytes32); + /* @notice this function provides the block hash of a block number. + * @dev this function can usually look back 256 blocks at most, unless otherwise specified + * @param blockNumber the block number + * @return blockHash the block hash of the input block number + */ + function blockHash(uint256 blockNumber) external view returns (bytes32 blockHash); - // retrieve the L1 data fee for a L2 transaction. it should return 0 for L1 chains and - // L2 chains which don't have L1 fee component. it uses msg.data to estimate L1 data so - // it must be used with a transaction. Return value in wei. - function getCurrentL1Fee() external view returns (uint256); + /* @notice this function provides the L1 fee of current transaction. + * @dev retrieve the L1 data fee for a L2 transaction. it should return 0 for L1 chains. it should + * return 0 for L2 chains if they don't have L1 fee component. + * @param dataSize the calldata size of the current transaction + * @return l1Fee the L1 fee in wei incurred by calldata of this data size + */ + function getCurrentL1Fee(uint256 dataSize) external view returns (uint256 l1Fee); - // retrieve the L1 data fee for a L2 simulation. it should return 0 for L1 chains and - // L2 chains which don't have L1 fee component. Return value in wei. - function getMaxL1Fee(uint256 dataSize) external view returns (uint256); + /* @notice this function provides the max possible L1 fee of current transaction. + * @dev retrieve the max possible L1 data fee for a L2 transaction. it should return 0 for L1 chains. it should + * return 0 for L2 chains if they don't have L1 fee component. + * @param dataSize the calldata size of the current transaction + * @return maxL1Fee the max possible L1 fee in wei incurred by calldata of this data size + */ + function getMaxL1Fee(uint256 dataSize) external view returns (uint256 maxL1Fee); - // Returns an upper bound on execution gas cost for one invocation of blockNumber(), - // one invocation of blockHash() and one invocation of getCurrentL1Fee(). - // Returns two values, first value indicates a fixed cost and the second value is - // the cost per msg.data byte (As some chain module's getCurrentL1Fee execution cost - // scales with calldata size) + /* @notice this function provides the overheads of calling this chain module. + * @return chainModuleFixedOverhead the fixed overhead incurred by calling this chain module + * @return chainModulePerByteOverhead the fixed overhead per byte incurred by calling this chain module with calldata + */ function getGasOverhead() external view diff --git a/contracts/src/v0.8/automation/interfaces/zksync/IGasBoundCaller.sol b/contracts/src/v0.8/automation/interfaces/zksync/IGasBoundCaller.sol new file mode 100644 index 0000000000..9edb541f9a --- /dev/null +++ b/contracts/src/v0.8/automation/interfaces/zksync/IGasBoundCaller.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.19; + +address constant GAS_BOUND_CALLER = address(0xc706EC7dfA5D4Dc87f29f859094165E8290530f5); + +interface IGasBoundCaller { + function gasBoundCall(address _to, uint256 _maxTotalGas, bytes calldata _data) external payable; +} diff --git a/contracts/src/v0.8/automation/interfaces/zksync/ISystemContext.sol b/contracts/src/v0.8/automation/interfaces/zksync/ISystemContext.sol new file mode 100644 index 0000000000..c8f480065c --- /dev/null +++ b/contracts/src/v0.8/automation/interfaces/zksync/ISystemContext.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.19; + +ISystemContext constant SYSTEM_CONTEXT_CONTRACT = ISystemContext(address(0x800b)); + +interface ISystemContext { + function gasPrice() external view returns (uint256); + function gasPerPubdataByte() external view returns (uint256 gasPerPubdataByte); + function getCurrentPubdataSpent() external view returns (uint256 currentPubdataSpent); +} diff --git a/contracts/src/v0.8/automation/interfaces/zksync/IZKSyncAutomationRegistryMaster2_3.sol b/contracts/src/v0.8/automation/interfaces/zksync/IZKSyncAutomationRegistryMaster2_3.sol new file mode 100644 index 0000000000..26b8a7d5c5 --- /dev/null +++ b/contracts/src/v0.8/automation/interfaces/zksync/IZKSyncAutomationRegistryMaster2_3.sol @@ -0,0 +1,441 @@ +// solhint-disable +// abi-checksum: 0x5857a77a981fcb60dbdac0700e68420cbe544249b20d9326d51c5ef8584c5dd7 +// SPDX-License-Identifier: MIT +// !! THIS FILE WAS AUTOGENERATED BY abi-to-sol v0.6.6. SEE SOURCE BELOW. !! +pragma solidity ^0.8.4; + +interface IZKSyncAutomationRegistryMaster2_3 { + error ArrayHasNoEntries(); + error CannotCancel(); + error CheckDataExceedsLimit(); + error ConfigDigestMismatch(); + error DuplicateEntry(); + error DuplicateSigners(); + error GasLimitCanOnlyIncrease(); + error GasLimitOutsideRange(); + error IncorrectNumberOfFaultyOracles(); + error IncorrectNumberOfSignatures(); + error IncorrectNumberOfSigners(); + error IndexOutOfRange(); + error InsufficientBalance(uint256 available, uint256 requested); + error InsufficientLinkLiquidity(); + error InvalidDataLength(); + error InvalidFeed(); + error InvalidPayee(); + error InvalidRecipient(); + error InvalidReport(); + error InvalidSigner(); + error InvalidToken(); + error InvalidTransmitter(); + error InvalidTrigger(); + error InvalidTriggerType(); + error MigrationNotPermitted(); + error MustSettleOffchain(); + error MustSettleOnchain(); + error NotAContract(); + error OnlyActiveSigners(); + error OnlyActiveTransmitters(); + error OnlyCallableByAdmin(); + error OnlyCallableByLINKToken(); + error OnlyCallableByOwnerOrAdmin(); + error OnlyCallableByOwnerOrRegistrar(); + error OnlyCallableByPayee(); + error OnlyCallableByProposedAdmin(); + error OnlyCallableByProposedPayee(); + error OnlyCallableByUpkeepPrivilegeManager(); + error OnlyFinanceAdmin(); + error OnlyPausedUpkeep(); + error OnlySimulatedBackend(); + error OnlyUnpausedUpkeep(); + error ParameterLengthError(); + error ReentrantCall(); + error RegistryPaused(); + error RepeatedSigner(); + error RepeatedTransmitter(); + error TargetCheckReverted(bytes reason); + error TooManyOracles(); + error TranscoderNotSet(); + error TransferFailed(); + error UpkeepAlreadyExists(); + error UpkeepCancelled(); + error UpkeepNotCanceled(); + error UpkeepNotNeeded(); + error ValueNotChanged(); + error ZeroAddressNotAllowed(); + event AdminPrivilegeConfigSet(address indexed admin, bytes privilegeConfig); + event BillingConfigOverridden(uint256 indexed id, ZKSyncAutomationRegistryBase2_3.BillingOverrides overrides); + event BillingConfigOverrideRemoved(uint256 indexed id); + event BillingConfigSet(address indexed token, ZKSyncAutomationRegistryBase2_3.BillingConfig config); + event CancelledUpkeepReport(uint256 indexed id, bytes trigger); + event ChainSpecificModuleUpdated(address newModule); + event ConfigSet( + uint32 previousConfigBlockNumber, + bytes32 configDigest, + uint64 configCount, + address[] signers, + address[] transmitters, + uint8 f, + bytes onchainConfig, + uint64 offchainConfigVersion, + bytes offchainConfig + ); + event DedupKeyAdded(bytes32 indexed dedupKey); + event FeesWithdrawn(address indexed assetAddress, address indexed recipient, uint256 amount); + event FundsAdded(uint256 indexed id, address indexed from, uint96 amount); + event FundsWithdrawn(uint256 indexed id, uint256 amount, address to); + event InsufficientFundsUpkeepReport(uint256 indexed id, bytes trigger); + event NOPsSettledOffchain(address[] payees, uint256[] payments); + event OwnershipTransferRequested(address indexed from, address indexed to); + event OwnershipTransferred(address indexed from, address indexed to); + event Paused(address account); + event PayeesUpdated(address[] transmitters, address[] payees); + event PayeeshipTransferRequested(address indexed transmitter, address indexed from, address indexed to); + event PayeeshipTransferred(address indexed transmitter, address indexed from, address indexed to); + event PaymentWithdrawn(address indexed transmitter, uint256 indexed amount, address indexed to, address payee); + event ReorgedUpkeepReport(uint256 indexed id, bytes trigger); + event StaleUpkeepReport(uint256 indexed id, bytes trigger); + event Transmitted(bytes32 configDigest, uint32 epoch); + event Unpaused(address account); + event UpkeepAdminTransferRequested(uint256 indexed id, address indexed from, address indexed to); + event UpkeepAdminTransferred(uint256 indexed id, address indexed from, address indexed to); + event UpkeepCanceled(uint256 indexed id, uint64 indexed atBlockHeight); + event UpkeepCharged(uint256 indexed id, ZKSyncAutomationRegistryBase2_3.PaymentReceipt receipt); + event UpkeepCheckDataSet(uint256 indexed id, bytes newCheckData); + event UpkeepGasLimitSet(uint256 indexed id, uint96 gasLimit); + event UpkeepMigrated(uint256 indexed id, uint256 remainingBalance, address destination); + event UpkeepOffchainConfigSet(uint256 indexed id, bytes offchainConfig); + event UpkeepPaused(uint256 indexed id); + event UpkeepPerformed( + uint256 indexed id, + bool indexed success, + uint96 totalPayment, + uint256 gasUsed, + uint256 gasOverhead, + bytes trigger + ); + event UpkeepPrivilegeConfigSet(uint256 indexed id, bytes privilegeConfig); + event UpkeepReceived(uint256 indexed id, uint256 startingBalance, address importedFrom); + event UpkeepRegistered(uint256 indexed id, uint32 performGas, address admin); + event UpkeepTriggerConfigSet(uint256 indexed id, bytes triggerConfig); + event UpkeepUnpaused(uint256 indexed id); + fallback() external payable; + function acceptOwnership() external; + function fallbackTo() external view returns (address); + function latestConfigDetails() external view returns (uint32 configCount, uint32 blockNumber, bytes32 configDigest); + function latestConfigDigestAndEpoch() external view returns (bool scanLogs, bytes32 configDigest, uint32 epoch); + function owner() external view returns (address); + function setConfig( + address[] memory signers, + address[] memory transmitters, + uint8 f, + bytes memory onchainConfigBytes, + uint64 offchainConfigVersion, + bytes memory offchainConfig + ) external; + function setConfigTypeSafe( + address[] memory signers, + address[] memory transmitters, + uint8 f, + ZKSyncAutomationRegistryBase2_3.OnchainConfig memory onchainConfig, + uint64 offchainConfigVersion, + bytes memory offchainConfig, + address[] memory billingTokens, + ZKSyncAutomationRegistryBase2_3.BillingConfig[] memory billingConfigs + ) external; + function transferOwnership(address to) external; + function transmit( + bytes32[3] memory reportContext, + bytes memory rawReport, + bytes32[] memory rs, + bytes32[] memory ss, + bytes32 rawVs + ) external; + function typeAndVersion() external view returns (string memory); + + function cancelUpkeep(uint256 id) external; + function migrateUpkeeps(uint256[] memory ids, address destination) external; + function onTokenTransfer(address sender, uint256 amount, bytes memory data) external; + function receiveUpkeeps(bytes memory encodedUpkeeps) external; + function registerUpkeep( + address target, + uint32 gasLimit, + address admin, + uint8 triggerType, + address billingToken, + bytes memory checkData, + bytes memory triggerConfig, + bytes memory offchainConfig + ) external returns (uint256 id); + + function acceptUpkeepAdmin(uint256 id) external; + function addFunds(uint256 id, uint96 amount) external payable; + function checkCallback( + uint256 id, + bytes[] memory values, + bytes memory extraData + ) external view returns (bool upkeepNeeded, bytes memory performData, uint8 upkeepFailureReason, uint256 gasUsed); + function checkUpkeep( + uint256 id, + bytes memory triggerData + ) + external + view + returns ( + bool upkeepNeeded, + bytes memory performData, + uint8 upkeepFailureReason, + uint256 gasUsed, + uint256 gasLimit, + uint256 fastGasWei, + uint256 linkUSD + ); + function checkUpkeep( + uint256 id + ) + external + view + returns ( + bool upkeepNeeded, + bytes memory performData, + uint8 upkeepFailureReason, + uint256 gasUsed, + uint256 gasLimit, + uint256 fastGasWei, + uint256 linkUSD + ); + function executeCallback( + uint256 id, + bytes memory payload + ) external returns (bool upkeepNeeded, bytes memory performData, uint8 upkeepFailureReason, uint256 gasUsed); + function pauseUpkeep(uint256 id) external; + function removeBillingOverrides(uint256 id) external; + function setBillingOverrides( + uint256 id, + ZKSyncAutomationRegistryBase2_3.BillingOverrides memory billingOverrides + ) external; + function setUpkeepCheckData(uint256 id, bytes memory newCheckData) external; + function setUpkeepGasLimit(uint256 id, uint32 gasLimit) external; + function setUpkeepOffchainConfig(uint256 id, bytes memory config) external; + function setUpkeepTriggerConfig(uint256 id, bytes memory triggerConfig) external; + function simulatePerformUpkeep( + uint256 id, + bytes memory performData + ) external view returns (bool success, uint256 gasUsed); + function transferUpkeepAdmin(uint256 id, address proposed) external; + function unpauseUpkeep(uint256 id) external; + function withdrawERC20Fees(address asset, address to, uint256 amount) external; + function withdrawFunds(uint256 id, address to) external; + function withdrawLink(address to, uint256 amount) external; + + function acceptPayeeship(address transmitter) external; + function disableOffchainPayments() external; + function getActiveUpkeepIDs(uint256 startIndex, uint256 maxCount) external view returns (uint256[] memory); + function getAdminPrivilegeConfig(address admin) external view returns (bytes memory); + function getAllowedReadOnlyAddress() external view returns (address); + function getAutomationForwarderLogic() external view returns (address); + function getAvailableERC20ForPayment(address billingToken) external view returns (uint256); + function getBalance(uint256 id) external view returns (uint96 balance); + function getBillingConfig( + address billingToken + ) external view returns (ZKSyncAutomationRegistryBase2_3.BillingConfig memory); + function getBillingOverrides( + uint256 upkeepID + ) external view returns (ZKSyncAutomationRegistryBase2_3.BillingOverrides memory); + function getBillingOverridesEnabled(uint256 upkeepID) external view returns (bool); + function getBillingToken(uint256 upkeepID) external view returns (address); + function getBillingTokenConfig( + address token + ) external view returns (ZKSyncAutomationRegistryBase2_3.BillingConfig memory); + function getBillingTokens() external view returns (address[] memory); + function getCancellationDelay() external pure returns (uint256); + function getChainModule() external view returns (address chainModule); + function getConditionalGasOverhead() external pure returns (uint256); + function getConfig() external view returns (ZKSyncAutomationRegistryBase2_3.OnchainConfig memory); + function getFallbackNativePrice() external view returns (uint256); + function getFastGasFeedAddress() external view returns (address); + function getForwarder(uint256 upkeepID) external view returns (address); + function getHotVars() external view returns (ZKSyncAutomationRegistryBase2_3.HotVars memory); + function getLinkAddress() external view returns (address); + function getLinkUSDFeedAddress() external view returns (address); + function getLogGasOverhead() external pure returns (uint256); + function getMaxPaymentForGas( + uint256 id, + uint8 triggerType, + uint32 gasLimit, + address billingToken + ) external view returns (uint96 maxPayment); + function getMinBalance(uint256 id) external view returns (uint96); + function getMinBalanceForUpkeep(uint256 id) external view returns (uint96 minBalance); + function getNativeUSDFeedAddress() external view returns (address); + function getNumUpkeeps() external view returns (uint256); + function getPayoutMode() external view returns (uint8); + function getPeerRegistryMigrationPermission(address peer) external view returns (uint8); + function getPerSignerGasOverhead() external pure returns (uint256); + function getReorgProtectionEnabled() external view returns (bool reorgProtectionEnabled); + function getReserveAmount(address billingToken) external view returns (uint256); + function getSignerInfo(address query) external view returns (bool active, uint8 index); + function getState() + external + view + returns ( + IAutomationV21PlusCommon.StateLegacy memory state, + IAutomationV21PlusCommon.OnchainConfigLegacy memory config, + address[] memory signers, + address[] memory transmitters, + uint8 f + ); + function getStorage() external view returns (ZKSyncAutomationRegistryBase2_3.Storage memory); + function getTransmitterInfo( + address query + ) external view returns (bool active, uint8 index, uint96 balance, uint96 lastCollected, address payee); + function getTransmittersWithPayees() + external + view + returns (ZKSyncAutomationRegistryBase2_3.TransmitterPayeeInfo[] memory); + function getTriggerType(uint256 upkeepId) external pure returns (uint8); + function getUpkeep(uint256 id) external view returns (IAutomationV21PlusCommon.UpkeepInfoLegacy memory upkeepInfo); + function getUpkeepPrivilegeConfig(uint256 upkeepId) external view returns (bytes memory); + function getUpkeepTriggerConfig(uint256 upkeepId) external view returns (bytes memory); + function getWrappedNativeTokenAddress() external view returns (address); + function hasDedupKey(bytes32 dedupKey) external view returns (bool); + function linkAvailableForPayment() external view returns (int256); + function pause() external; + function setAdminPrivilegeConfig(address admin, bytes memory newPrivilegeConfig) external; + function setPayees(address[] memory payees) external; + function setPeerRegistryMigrationPermission(address peer, uint8 permission) external; + function setUpkeepPrivilegeConfig(uint256 upkeepId, bytes memory newPrivilegeConfig) external; + function settleNOPsOffchain() external; + function supportsBillingToken(address token) external view returns (bool); + function transferPayeeship(address transmitter, address proposed) external; + function unpause() external; + function upkeepVersion() external pure returns (uint8); + function withdrawPayment(address from, address to) external; +} + +interface ZKSyncAutomationRegistryBase2_3 { + struct BillingOverrides { + uint32 gasFeePPB; + uint24 flatFeeMilliCents; + } + + struct BillingConfig { + uint32 gasFeePPB; + uint24 flatFeeMilliCents; + address priceFeed; + uint8 decimals; + uint256 fallbackPrice; + uint96 minSpend; + } + + struct PaymentReceipt { + uint96 gasChargeInBillingToken; + uint96 premiumInBillingToken; + uint96 gasReimbursementInJuels; + uint96 premiumInJuels; + address billingToken; + uint96 linkUSD; + uint96 nativeUSD; + uint96 billingUSD; + } + + struct OnchainConfig { + uint32 checkGasLimit; + uint32 maxPerformGas; + uint32 maxCheckDataSize; + address transcoder; + bool reorgProtectionEnabled; + uint24 stalenessSeconds; + uint32 maxPerformDataSize; + uint32 maxRevertDataSize; + address upkeepPrivilegeManager; + uint16 gasCeilingMultiplier; + address financeAdmin; + uint256 fallbackGasPrice; + uint256 fallbackLinkPrice; + uint256 fallbackNativePrice; + address[] registrars; + address chainModule; + } + + struct HotVars { + uint96 totalPremium; + uint32 latestEpoch; + uint24 stalenessSeconds; + uint16 gasCeilingMultiplier; + uint8 f; + bool paused; + bool reentrancyGuard; + bool reorgProtectionEnabled; + address chainModule; + } + + struct Storage { + address transcoder; + uint32 checkGasLimit; + uint32 maxPerformGas; + uint32 nonce; + address upkeepPrivilegeManager; + uint32 configCount; + uint32 latestConfigBlockNumber; + uint32 maxCheckDataSize; + address financeAdmin; + uint32 maxPerformDataSize; + uint32 maxRevertDataSize; + } + + struct TransmitterPayeeInfo { + address transmitterAddress; + address payeeAddress; + } +} + +interface IAutomationV21PlusCommon { + struct StateLegacy { + uint32 nonce; + uint96 ownerLinkBalance; + uint256 expectedLinkBalance; + uint96 totalPremium; + uint256 numUpkeeps; + uint32 configCount; + uint32 latestConfigBlockNumber; + bytes32 latestConfigDigest; + uint32 latestEpoch; + bool paused; + } + + struct OnchainConfigLegacy { + uint32 paymentPremiumPPB; + uint32 flatFeeMicroLink; + uint32 checkGasLimit; + uint24 stalenessSeconds; + uint16 gasCeilingMultiplier; + uint96 minUpkeepSpend; + uint32 maxPerformGas; + uint32 maxCheckDataSize; + uint32 maxPerformDataSize; + uint32 maxRevertDataSize; + uint256 fallbackGasPrice; + uint256 fallbackLinkPrice; + address transcoder; + address[] registrars; + address upkeepPrivilegeManager; + } + + struct UpkeepInfoLegacy { + address target; + uint32 performGas; + bytes checkData; + uint96 balance; + address admin; + uint64 maxValidBlocknumber; + uint32 lastPerformedBlockNumber; + uint96 amountSpent; + bool paused; + bytes offchainConfig; + } +} + +// THIS FILE WAS AUTOGENERATED FROM THE FOLLOWING ABI JSON: +/* +[{"inputs":[{"internalType":"contract ZKSyncAutomationRegistryLogicA2_3","name":"logicA","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ArrayHasNoEntries","type":"error"},{"inputs":[],"name":"CannotCancel","type":"error"},{"inputs":[],"name":"CheckDataExceedsLimit","type":"error"},{"inputs":[],"name":"ConfigDigestMismatch","type":"error"},{"inputs":[],"name":"DuplicateEntry","type":"error"},{"inputs":[],"name":"DuplicateSigners","type":"error"},{"inputs":[],"name":"GasLimitCanOnlyIncrease","type":"error"},{"inputs":[],"name":"GasLimitOutsideRange","type":"error"},{"inputs":[],"name":"IncorrectNumberOfFaultyOracles","type":"error"},{"inputs":[],"name":"IncorrectNumberOfSignatures","type":"error"},{"inputs":[],"name":"IncorrectNumberOfSigners","type":"error"},{"inputs":[],"name":"IndexOutOfRange","type":"error"},{"inputs":[{"internalType":"uint256","name":"available","type":"uint256"},{"internalType":"uint256","name":"requested","type":"uint256"}],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InsufficientLinkLiquidity","type":"error"},{"inputs":[],"name":"InvalidDataLength","type":"error"},{"inputs":[],"name":"InvalidFeed","type":"error"},{"inputs":[],"name":"InvalidPayee","type":"error"},{"inputs":[],"name":"InvalidRecipient","type":"error"},{"inputs":[],"name":"InvalidReport","type":"error"},{"inputs":[],"name":"InvalidSigner","type":"error"},{"inputs":[],"name":"InvalidToken","type":"error"},{"inputs":[],"name":"InvalidTransmitter","type":"error"},{"inputs":[],"name":"InvalidTrigger","type":"error"},{"inputs":[],"name":"InvalidTriggerType","type":"error"},{"inputs":[],"name":"MigrationNotPermitted","type":"error"},{"inputs":[],"name":"MustSettleOffchain","type":"error"},{"inputs":[],"name":"MustSettleOnchain","type":"error"},{"inputs":[],"name":"NotAContract","type":"error"},{"inputs":[],"name":"OnlyActiveSigners","type":"error"},{"inputs":[],"name":"OnlyActiveTransmitters","type":"error"},{"inputs":[],"name":"OnlyCallableByAdmin","type":"error"},{"inputs":[],"name":"OnlyCallableByLINKToken","type":"error"},{"inputs":[],"name":"OnlyCallableByOwnerOrAdmin","type":"error"},{"inputs":[],"name":"OnlyCallableByOwnerOrRegistrar","type":"error"},{"inputs":[],"name":"OnlyCallableByPayee","type":"error"},{"inputs":[],"name":"OnlyCallableByProposedAdmin","type":"error"},{"inputs":[],"name":"OnlyCallableByProposedPayee","type":"error"},{"inputs":[],"name":"OnlyCallableByUpkeepPrivilegeManager","type":"error"},{"inputs":[],"name":"OnlyFinanceAdmin","type":"error"},{"inputs":[],"name":"OnlyPausedUpkeep","type":"error"},{"inputs":[],"name":"OnlySimulatedBackend","type":"error"},{"inputs":[],"name":"OnlyUnpausedUpkeep","type":"error"},{"inputs":[],"name":"ParameterLengthError","type":"error"},{"inputs":[],"name":"ReentrantCall","type":"error"},{"inputs":[],"name":"RegistryPaused","type":"error"},{"inputs":[],"name":"RepeatedSigner","type":"error"},{"inputs":[],"name":"RepeatedTransmitter","type":"error"},{"inputs":[{"internalType":"bytes","name":"reason","type":"bytes"}],"name":"TargetCheckReverted","type":"error"},{"inputs":[],"name":"TooManyOracles","type":"error"},{"inputs":[],"name":"TranscoderNotSet","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[],"name":"UpkeepAlreadyExists","type":"error"},{"inputs":[],"name":"UpkeepCancelled","type":"error"},{"inputs":[],"name":"UpkeepNotCanceled","type":"error"},{"inputs":[],"name":"UpkeepNotNeeded","type":"error"},{"inputs":[],"name":"ValueNotChanged","type":"error"},{"inputs":[],"name":"ZeroAddressNotAllowed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"bytes","name":"privilegeConfig","type":"bytes"}],"name":"AdminPrivilegeConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"components":[{"internalType":"uint32","name":"gasFeePPB","type":"uint32"},{"internalType":"uint24","name":"flatFeeMilliCents","type":"uint24"}],"indexed":false,"internalType":"struct ZKSyncAutomationRegistryBase2_3.BillingOverrides","name":"overrides","type":"tuple"}],"name":"BillingConfigOverridden","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"BillingConfigOverrideRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20Metadata","name":"token","type":"address"},{"components":[{"internalType":"uint32","name":"gasFeePPB","type":"uint32"},{"internalType":"uint24","name":"flatFeeMilliCents","type":"uint24"},{"internalType":"contract AggregatorV3Interface","name":"priceFeed","type":"address"},{"internalType":"uint8","name":"decimals","type":"uint8"},{"internalType":"uint256","name":"fallbackPrice","type":"uint256"},{"internalType":"uint96","name":"minSpend","type":"uint96"}],"indexed":false,"internalType":"struct ZKSyncAutomationRegistryBase2_3.BillingConfig","name":"config","type":"tuple"}],"name":"BillingConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"CancelledUpkeepReport","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newModule","type":"address"}],"name":"ChainSpecificModuleUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"previousConfigBlockNumber","type":"uint32"},{"indexed":false,"internalType":"bytes32","name":"configDigest","type":"bytes32"},{"indexed":false,"internalType":"uint64","name":"configCount","type":"uint64"},{"indexed":false,"internalType":"address[]","name":"signers","type":"address[]"},{"indexed":false,"internalType":"address[]","name":"transmitters","type":"address[]"},{"indexed":false,"internalType":"uint8","name":"f","type":"uint8"},{"indexed":false,"internalType":"bytes","name":"onchainConfig","type":"bytes"},{"indexed":false,"internalType":"uint64","name":"offchainConfigVersion","type":"uint64"},{"indexed":false,"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"ConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"dedupKey","type":"bytes32"}],"name":"DedupKeyAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"assetAddress","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"FeesWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint96","name":"amount","type":"uint96"}],"name":"FundsAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"to","type":"address"}],"name":"FundsWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"InsufficientFundsUpkeepReport","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"payees","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"payments","type":"uint256[]"}],"name":"NOPsSettledOffchain","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"transmitters","type":"address[]"},{"indexed":false,"internalType":"address[]","name":"payees","type":"address[]"}],"name":"PayeesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"transmitter","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"PayeeshipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"transmitter","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"PayeeshipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"transmitter","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address","name":"payee","type":"address"}],"name":"PaymentWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"ReorgedUpkeepReport","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"StaleUpkeepReport","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"configDigest","type":"bytes32"},{"indexed":false,"internalType":"uint32","name":"epoch","type":"uint32"}],"name":"Transmitted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"UpkeepAdminTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"UpkeepAdminTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"uint64","name":"atBlockHeight","type":"uint64"}],"name":"UpkeepCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"components":[{"internalType":"uint96","name":"gasChargeInBillingToken","type":"uint96"},{"internalType":"uint96","name":"premiumInBillingToken","type":"uint96"},{"internalType":"uint96","name":"gasReimbursementInJuels","type":"uint96"},{"internalType":"uint96","name":"premiumInJuels","type":"uint96"},{"internalType":"contract IERC20Metadata","name":"billingToken","type":"address"},{"internalType":"uint96","name":"linkUSD","type":"uint96"},{"internalType":"uint96","name":"nativeUSD","type":"uint96"},{"internalType":"uint96","name":"billingUSD","type":"uint96"}],"indexed":false,"internalType":"struct ZKSyncAutomationRegistryBase2_3.PaymentReceipt","name":"receipt","type":"tuple"}],"name":"UpkeepCharged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"newCheckData","type":"bytes"}],"name":"UpkeepCheckDataSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint96","name":"gasLimit","type":"uint96"}],"name":"UpkeepGasLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"remainingBalance","type":"uint256"},{"indexed":false,"internalType":"address","name":"destination","type":"address"}],"name":"UpkeepMigrated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"UpkeepOffchainConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"UpkeepPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"bool","name":"success","type":"bool"},{"indexed":false,"internalType":"uint96","name":"totalPayment","type":"uint96"},{"indexed":false,"internalType":"uint256","name":"gasUsed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"gasOverhead","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"trigger","type":"bytes"}],"name":"UpkeepPerformed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"privilegeConfig","type":"bytes"}],"name":"UpkeepPrivilegeConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"startingBalance","type":"uint256"},{"indexed":false,"internalType":"address","name":"importedFrom","type":"address"}],"name":"UpkeepReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"performGas","type":"uint32"},{"indexed":false,"internalType":"address","name":"admin","type":"address"}],"name":"UpkeepRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"triggerConfig","type":"bytes"}],"name":"UpkeepTriggerConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"UpkeepUnpaused","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fallbackTo","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestConfigDetails","outputs":[{"internalType":"uint32","name":"configCount","type":"uint32"},{"internalType":"uint32","name":"blockNumber","type":"uint32"},{"internalType":"bytes32","name":"configDigest","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestConfigDigestAndEpoch","outputs":[{"internalType":"bool","name":"scanLogs","type":"bool"},{"internalType":"bytes32","name":"configDigest","type":"bytes32"},{"internalType":"uint32","name":"epoch","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"address[]","name":"transmitters","type":"address[]"},{"internalType":"uint8","name":"f","type":"uint8"},{"internalType":"bytes","name":"onchainConfigBytes","type":"bytes"},{"internalType":"uint64","name":"offchainConfigVersion","type":"uint64"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"setConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"address[]","name":"transmitters","type":"address[]"},{"internalType":"uint8","name":"f","type":"uint8"},{"components":[{"internalType":"uint32","name":"checkGasLimit","type":"uint32"},{"internalType":"uint32","name":"maxPerformGas","type":"uint32"},{"internalType":"uint32","name":"maxCheckDataSize","type":"uint32"},{"internalType":"address","name":"transcoder","type":"address"},{"internalType":"bool","name":"reorgProtectionEnabled","type":"bool"},{"internalType":"uint24","name":"stalenessSeconds","type":"uint24"},{"internalType":"uint32","name":"maxPerformDataSize","type":"uint32"},{"internalType":"uint32","name":"maxRevertDataSize","type":"uint32"},{"internalType":"address","name":"upkeepPrivilegeManager","type":"address"},{"internalType":"uint16","name":"gasCeilingMultiplier","type":"uint16"},{"internalType":"address","name":"financeAdmin","type":"address"},{"internalType":"uint256","name":"fallbackGasPrice","type":"uint256"},{"internalType":"uint256","name":"fallbackLinkPrice","type":"uint256"},{"internalType":"uint256","name":"fallbackNativePrice","type":"uint256"},{"internalType":"address[]","name":"registrars","type":"address[]"},{"internalType":"contract IChainModule","name":"chainModule","type":"address"}],"internalType":"struct ZKSyncAutomationRegistryBase2_3.OnchainConfig","name":"onchainConfig","type":"tuple"},{"internalType":"uint64","name":"offchainConfigVersion","type":"uint64"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"},{"internalType":"contract IERC20Metadata[]","name":"billingTokens","type":"address[]"},{"components":[{"internalType":"uint32","name":"gasFeePPB","type":"uint32"},{"internalType":"uint24","name":"flatFeeMilliCents","type":"uint24"},{"internalType":"contract AggregatorV3Interface","name":"priceFeed","type":"address"},{"internalType":"uint8","name":"decimals","type":"uint8"},{"internalType":"uint256","name":"fallbackPrice","type":"uint256"},{"internalType":"uint96","name":"minSpend","type":"uint96"}],"internalType":"struct ZKSyncAutomationRegistryBase2_3.BillingConfig[]","name":"billingConfigs","type":"tuple[]"}],"name":"setConfigTypeSafe","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[3]","name":"reportContext","type":"bytes32[3]"},{"internalType":"bytes","name":"rawReport","type":"bytes"},{"internalType":"bytes32[]","name":"rs","type":"bytes32[]"},{"internalType":"bytes32[]","name":"ss","type":"bytes32[]"},{"internalType":"bytes32","name":"rawVs","type":"bytes32"}],"name":"transmit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"typeAndVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ZKSyncAutomationRegistryLogicB2_3","name":"logicB","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"cancelUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"address","name":"destination","type":"address"}],"name":"migrateUpkeeps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onTokenTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"encodedUpkeeps","type":"bytes"}],"name":"receiveUpkeeps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint32","name":"gasLimit","type":"uint32"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"enum ZKSyncAutomationRegistryBase2_3.Trigger","name":"triggerType","type":"uint8"},{"internalType":"contract IERC20Metadata","name":"billingToken","type":"address"},{"internalType":"bytes","name":"checkData","type":"bytes"},{"internalType":"bytes","name":"triggerConfig","type":"bytes"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"name":"registerUpkeep","outputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ZKSyncAutomationRegistryLogicC2_3","name":"logicC","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"acceptUpkeepAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint96","name":"amount","type":"uint96"}],"name":"addFunds","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes[]","name":"values","type":"bytes[]"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"name":"checkCallback","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum ZKSyncAutomationRegistryBase2_3.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"triggerData","type":"bytes"}],"name":"checkUpkeep","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum ZKSyncAutomationRegistryBase2_3.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"fastGasWei","type":"uint256"},{"internalType":"uint256","name":"linkUSD","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"checkUpkeep","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum ZKSyncAutomationRegistryBase2_3.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"fastGasWei","type":"uint256"},{"internalType":"uint256","name":"linkUSD","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"payload","type":"bytes"}],"name":"executeCallback","outputs":[{"internalType":"bool","name":"upkeepNeeded","type":"bool"},{"internalType":"bytes","name":"performData","type":"bytes"},{"internalType":"enum ZKSyncAutomationRegistryBase2_3.UpkeepFailureReason","name":"upkeepFailureReason","type":"uint8"},{"internalType":"uint256","name":"gasUsed","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"pauseUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"removeBillingOverrides","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"components":[{"internalType":"uint32","name":"gasFeePPB","type":"uint32"},{"internalType":"uint24","name":"flatFeeMilliCents","type":"uint24"}],"internalType":"struct ZKSyncAutomationRegistryBase2_3.BillingOverrides","name":"billingOverrides","type":"tuple"}],"name":"setBillingOverrides","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"newCheckData","type":"bytes"}],"name":"setUpkeepCheckData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint32","name":"gasLimit","type":"uint32"}],"name":"setUpkeepGasLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"config","type":"bytes"}],"name":"setUpkeepOffchainConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"triggerConfig","type":"bytes"}],"name":"setUpkeepTriggerConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"performData","type":"bytes"}],"name":"simulatePerformUpkeep","outputs":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"uint256","name":"gasUsed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address","name":"proposed","type":"address"}],"name":"transferUpkeepAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"unpauseUpkeep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20Metadata","name":"asset","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawERC20Fees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"withdrawFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawLink","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"link","type":"address"},{"internalType":"address","name":"linkUSDFeed","type":"address"},{"internalType":"address","name":"nativeUSDFeed","type":"address"},{"internalType":"address","name":"fastGasFeed","type":"address"},{"internalType":"address","name":"automationForwarderLogic","type":"address"},{"internalType":"address","name":"allowedReadOnlyAddress","type":"address"},{"internalType":"enum ZKSyncAutomationRegistryBase2_3.PayoutMode","name":"payoutMode","type":"uint8"},{"internalType":"address","name":"wrappedNativeTokenAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"transmitter","type":"address"}],"name":"acceptPayeeship","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"disableOffchainPayments","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"maxCount","type":"uint256"}],"name":"getActiveUpkeepIDs","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"admin","type":"address"}],"name":"getAdminPrivilegeConfig","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllowedReadOnlyAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAutomationForwarderLogic","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20Metadata","name":"billingToken","type":"address"}],"name":"getAvailableERC20ForPayment","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getBalance","outputs":[{"internalType":"uint96","name":"balance","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20Metadata","name":"billingToken","type":"address"}],"name":"getBillingConfig","outputs":[{"components":[{"internalType":"uint32","name":"gasFeePPB","type":"uint32"},{"internalType":"uint24","name":"flatFeeMilliCents","type":"uint24"},{"internalType":"contract AggregatorV3Interface","name":"priceFeed","type":"address"},{"internalType":"uint8","name":"decimals","type":"uint8"},{"internalType":"uint256","name":"fallbackPrice","type":"uint256"},{"internalType":"uint96","name":"minSpend","type":"uint96"}],"internalType":"struct ZKSyncAutomationRegistryBase2_3.BillingConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepID","type":"uint256"}],"name":"getBillingOverrides","outputs":[{"components":[{"internalType":"uint32","name":"gasFeePPB","type":"uint32"},{"internalType":"uint24","name":"flatFeeMilliCents","type":"uint24"}],"internalType":"struct ZKSyncAutomationRegistryBase2_3.BillingOverrides","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepID","type":"uint256"}],"name":"getBillingOverridesEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepID","type":"uint256"}],"name":"getBillingToken","outputs":[{"internalType":"contract IERC20Metadata","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20Metadata","name":"token","type":"address"}],"name":"getBillingTokenConfig","outputs":[{"components":[{"internalType":"uint32","name":"gasFeePPB","type":"uint32"},{"internalType":"uint24","name":"flatFeeMilliCents","type":"uint24"},{"internalType":"contract AggregatorV3Interface","name":"priceFeed","type":"address"},{"internalType":"uint8","name":"decimals","type":"uint8"},{"internalType":"uint256","name":"fallbackPrice","type":"uint256"},{"internalType":"uint96","name":"minSpend","type":"uint96"}],"internalType":"struct ZKSyncAutomationRegistryBase2_3.BillingConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBillingTokens","outputs":[{"internalType":"contract IERC20Metadata[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCancellationDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getChainModule","outputs":[{"internalType":"contract IChainModule","name":"chainModule","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getConditionalGasOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getConfig","outputs":[{"components":[{"internalType":"uint32","name":"checkGasLimit","type":"uint32"},{"internalType":"uint32","name":"maxPerformGas","type":"uint32"},{"internalType":"uint32","name":"maxCheckDataSize","type":"uint32"},{"internalType":"address","name":"transcoder","type":"address"},{"internalType":"bool","name":"reorgProtectionEnabled","type":"bool"},{"internalType":"uint24","name":"stalenessSeconds","type":"uint24"},{"internalType":"uint32","name":"maxPerformDataSize","type":"uint32"},{"internalType":"uint32","name":"maxRevertDataSize","type":"uint32"},{"internalType":"address","name":"upkeepPrivilegeManager","type":"address"},{"internalType":"uint16","name":"gasCeilingMultiplier","type":"uint16"},{"internalType":"address","name":"financeAdmin","type":"address"},{"internalType":"uint256","name":"fallbackGasPrice","type":"uint256"},{"internalType":"uint256","name":"fallbackLinkPrice","type":"uint256"},{"internalType":"uint256","name":"fallbackNativePrice","type":"uint256"},{"internalType":"address[]","name":"registrars","type":"address[]"},{"internalType":"contract IChainModule","name":"chainModule","type":"address"}],"internalType":"struct ZKSyncAutomationRegistryBase2_3.OnchainConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFallbackNativePrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFastGasFeedAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepID","type":"uint256"}],"name":"getForwarder","outputs":[{"internalType":"contract IAutomationForwarder","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getHotVars","outputs":[{"components":[{"internalType":"uint96","name":"totalPremium","type":"uint96"},{"internalType":"uint32","name":"latestEpoch","type":"uint32"},{"internalType":"uint24","name":"stalenessSeconds","type":"uint24"},{"internalType":"uint16","name":"gasCeilingMultiplier","type":"uint16"},{"internalType":"uint8","name":"f","type":"uint8"},{"internalType":"bool","name":"paused","type":"bool"},{"internalType":"bool","name":"reentrancyGuard","type":"bool"},{"internalType":"bool","name":"reorgProtectionEnabled","type":"bool"},{"internalType":"contract IChainModule","name":"chainModule","type":"address"}],"internalType":"struct ZKSyncAutomationRegistryBase2_3.HotVars","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLinkAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLinkUSDFeedAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLogGasOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"enum ZKSyncAutomationRegistryBase2_3.Trigger","name":"triggerType","type":"uint8"},{"internalType":"uint32","name":"gasLimit","type":"uint32"},{"internalType":"contract IERC20Metadata","name":"billingToken","type":"address"}],"name":"getMaxPaymentForGas","outputs":[{"internalType":"uint96","name":"maxPayment","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getMinBalance","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getMinBalanceForUpkeep","outputs":[{"internalType":"uint96","name":"minBalance","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNativeUSDFeedAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNumUpkeeps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPayoutMode","outputs":[{"internalType":"enum ZKSyncAutomationRegistryBase2_3.PayoutMode","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"peer","type":"address"}],"name":"getPeerRegistryMigrationPermission","outputs":[{"internalType":"enum ZKSyncAutomationRegistryBase2_3.MigrationPermission","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPerSignerGasOverhead","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getReorgProtectionEnabled","outputs":[{"internalType":"bool","name":"reorgProtectionEnabled","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20Metadata","name":"billingToken","type":"address"}],"name":"getReserveAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"query","type":"address"}],"name":"getSignerInfo","outputs":[{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint8","name":"index","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getState","outputs":[{"components":[{"internalType":"uint32","name":"nonce","type":"uint32"},{"internalType":"uint96","name":"ownerLinkBalance","type":"uint96"},{"internalType":"uint256","name":"expectedLinkBalance","type":"uint256"},{"internalType":"uint96","name":"totalPremium","type":"uint96"},{"internalType":"uint256","name":"numUpkeeps","type":"uint256"},{"internalType":"uint32","name":"configCount","type":"uint32"},{"internalType":"uint32","name":"latestConfigBlockNumber","type":"uint32"},{"internalType":"bytes32","name":"latestConfigDigest","type":"bytes32"},{"internalType":"uint32","name":"latestEpoch","type":"uint32"},{"internalType":"bool","name":"paused","type":"bool"}],"internalType":"struct IAutomationV21PlusCommon.StateLegacy","name":"state","type":"tuple"},{"components":[{"internalType":"uint32","name":"paymentPremiumPPB","type":"uint32"},{"internalType":"uint32","name":"flatFeeMicroLink","type":"uint32"},{"internalType":"uint32","name":"checkGasLimit","type":"uint32"},{"internalType":"uint24","name":"stalenessSeconds","type":"uint24"},{"internalType":"uint16","name":"gasCeilingMultiplier","type":"uint16"},{"internalType":"uint96","name":"minUpkeepSpend","type":"uint96"},{"internalType":"uint32","name":"maxPerformGas","type":"uint32"},{"internalType":"uint32","name":"maxCheckDataSize","type":"uint32"},{"internalType":"uint32","name":"maxPerformDataSize","type":"uint32"},{"internalType":"uint32","name":"maxRevertDataSize","type":"uint32"},{"internalType":"uint256","name":"fallbackGasPrice","type":"uint256"},{"internalType":"uint256","name":"fallbackLinkPrice","type":"uint256"},{"internalType":"address","name":"transcoder","type":"address"},{"internalType":"address[]","name":"registrars","type":"address[]"},{"internalType":"address","name":"upkeepPrivilegeManager","type":"address"}],"internalType":"struct IAutomationV21PlusCommon.OnchainConfigLegacy","name":"config","type":"tuple"},{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"address[]","name":"transmitters","type":"address[]"},{"internalType":"uint8","name":"f","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStorage","outputs":[{"components":[{"internalType":"address","name":"transcoder","type":"address"},{"internalType":"uint32","name":"checkGasLimit","type":"uint32"},{"internalType":"uint32","name":"maxPerformGas","type":"uint32"},{"internalType":"uint32","name":"nonce","type":"uint32"},{"internalType":"address","name":"upkeepPrivilegeManager","type":"address"},{"internalType":"uint32","name":"configCount","type":"uint32"},{"internalType":"uint32","name":"latestConfigBlockNumber","type":"uint32"},{"internalType":"uint32","name":"maxCheckDataSize","type":"uint32"},{"internalType":"address","name":"financeAdmin","type":"address"},{"internalType":"uint32","name":"maxPerformDataSize","type":"uint32"},{"internalType":"uint32","name":"maxRevertDataSize","type":"uint32"}],"internalType":"struct ZKSyncAutomationRegistryBase2_3.Storage","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"query","type":"address"}],"name":"getTransmitterInfo","outputs":[{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint8","name":"index","type":"uint8"},{"internalType":"uint96","name":"balance","type":"uint96"},{"internalType":"uint96","name":"lastCollected","type":"uint96"},{"internalType":"address","name":"payee","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTransmittersWithPayees","outputs":[{"components":[{"internalType":"address","name":"transmitterAddress","type":"address"},{"internalType":"address","name":"payeeAddress","type":"address"}],"internalType":"struct ZKSyncAutomationRegistryBase2_3.TransmitterPayeeInfo[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"}],"name":"getTriggerType","outputs":[{"internalType":"enum ZKSyncAutomationRegistryBase2_3.Trigger","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getUpkeep","outputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint32","name":"performGas","type":"uint32"},{"internalType":"bytes","name":"checkData","type":"bytes"},{"internalType":"uint96","name":"balance","type":"uint96"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"uint64","name":"maxValidBlocknumber","type":"uint64"},{"internalType":"uint32","name":"lastPerformedBlockNumber","type":"uint32"},{"internalType":"uint96","name":"amountSpent","type":"uint96"},{"internalType":"bool","name":"paused","type":"bool"},{"internalType":"bytes","name":"offchainConfig","type":"bytes"}],"internalType":"struct IAutomationV21PlusCommon.UpkeepInfoLegacy","name":"upkeepInfo","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"}],"name":"getUpkeepPrivilegeConfig","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"}],"name":"getUpkeepTriggerConfig","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWrappedNativeTokenAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"dedupKey","type":"bytes32"}],"name":"hasDedupKey","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"linkAvailableForPayment","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"admin","type":"address"},{"internalType":"bytes","name":"newPrivilegeConfig","type":"bytes"}],"name":"setAdminPrivilegeConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"payees","type":"address[]"}],"name":"setPayees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"peer","type":"address"},{"internalType":"enum ZKSyncAutomationRegistryBase2_3.MigrationPermission","name":"permission","type":"uint8"}],"name":"setPeerRegistryMigrationPermission","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"upkeepId","type":"uint256"},{"internalType":"bytes","name":"newPrivilegeConfig","type":"bytes"}],"name":"setUpkeepPrivilegeConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"settleNOPsOffchain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20Metadata","name":"token","type":"address"}],"name":"supportsBillingToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"transmitter","type":"address"},{"internalType":"address","name":"proposed","type":"address"}],"name":"transferPayeeship","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"upkeepVersion","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"withdrawPayment","outputs":[],"stateMutability":"nonpayable","type":"function"}] +*/ diff --git a/contracts/src/v0.8/automation/test/v2_3/WETH9.sol b/contracts/src/v0.8/automation/test/WETH9.sol similarity index 100% rename from contracts/src/v0.8/automation/test/v2_3/WETH9.sol rename to contracts/src/v0.8/automation/test/WETH9.sol diff --git a/contracts/src/v0.8/automation/test/v2_3/AutomationRegistry2_3.t.sol b/contracts/src/v0.8/automation/test/v2_3/AutomationRegistry2_3.t.sol index dbc0c203c0..41aabf1bbe 100644 --- a/contracts/src/v0.8/automation/test/v2_3/AutomationRegistry2_3.t.sol +++ b/contracts/src/v0.8/automation/test/v2_3/AutomationRegistry2_3.t.sol @@ -22,11 +22,11 @@ contract SetUp is BaseTest { AutomationRegistryBase2_3.OnchainConfig internal config; bytes internal constant offchainConfigBytes = abi.encode(1234, ZERO_ADDRESS); - uint256 linkUpkeepID; - uint256 linkUpkeepID2; // 2 upkeeps use the same billing token (LINK) to test migration scenario - uint256 usdUpkeepID18; // 1 upkeep uses ERC20 token with 18 decimals - uint256 usdUpkeepID6; // 1 upkeep uses ERC20 token with 6 decimals - uint256 nativeUpkeepID; + uint256 internal linkUpkeepID; + uint256 internal linkUpkeepID2; // 2 upkeeps use the same billing token (LINK) to test migration scenario + uint256 internal usdUpkeepID18; // 1 upkeep uses ERC20 token with 18 decimals + uint256 internal usdUpkeepID6; // 1 upkeep uses ERC20 token with 6 decimals + uint256 internal nativeUpkeepID; function setUp() public virtual override { super.setUp(); @@ -790,6 +790,7 @@ contract SetConfig is SetUp { } function testSetConfigOnTransmittersAndPayees() public { + registry.setPayees(PAYEES); AutomationRegistryBase2_3.TransmitterPayeeInfo[] memory transmitterPayeeInfos = registry .getTransmittersWithPayees(); assertEq(transmitterPayeeInfos.length, TRANSMITTERS.length); @@ -975,6 +976,7 @@ contract NOPsSettlement is SetUp { function testSettleNOPsOffchainSuccess() public { // deploy and configure a registry with OFF_CHAIN payout (Registry registry, ) = deployAndConfigureRegistryAndRegistrar(AutoBase.PayoutMode.OFF_CHAIN); + registry.setPayees(PAYEES); uint256[] memory payments = new uint256[](TRANSMITTERS.length); for (uint256 i = 0; i < TRANSMITTERS.length; i++) { @@ -991,6 +993,7 @@ contract NOPsSettlement is SetUp { function testSettleNOPsOffchainSuccessWithERC20MultiSteps() public { // deploy and configure a registry with OFF_CHAIN payout (Registry registry, ) = deployAndConfigureRegistryAndRegistrar(AutoBase.PayoutMode.OFF_CHAIN); + registry.setPayees(PAYEES); // register an upkeep and add funds uint256 id = registry.registerUpkeep(address(TARGET1), 1000000, UPKEEP_ADMIN, 0, address(usdToken18), "", "", ""); @@ -1186,6 +1189,7 @@ contract NOPsSettlement is SetUp { function testSinglePerformAndNodesCanWithdrawOnchain() public { // deploy and configure a registry with OFF_CHAIN payout (Registry registry, ) = deployAndConfigureRegistryAndRegistrar(AutoBase.PayoutMode.OFF_CHAIN); + registry.setPayees(PAYEES); // register an upkeep and add funds uint256 id = registry.registerUpkeep(address(TARGET1), 1000000, UPKEEP_ADMIN, 0, address(usdToken18), "", "", ""); @@ -1224,6 +1228,7 @@ contract NOPsSettlement is SetUp { function testMultiplePerformsAndNodesCanWithdrawOnchain() public { // deploy and configure a registry with OFF_CHAIN payout (Registry registry, ) = deployAndConfigureRegistryAndRegistrar(AutoBase.PayoutMode.OFF_CHAIN); + registry.setPayees(PAYEES); // register an upkeep and add funds uint256 id = registry.registerUpkeep(address(TARGET1), 1000000, UPKEEP_ADMIN, 0, address(usdToken18), "", "", ""); @@ -1977,3 +1982,772 @@ contract MigrateReceive is SetUp { vm.stopPrank(); } } + +contract Pause is SetUp { + function test_RevertsWhen_CalledByNonOwner() external { + vm.expectRevert(bytes("Only callable by owner")); + vm.prank(STRANGER); + registry.pause(); + } + + function test_CalledByOwner_success() external { + vm.startPrank(registry.owner()); + registry.pause(); + + (IAutomationV21PlusCommon.StateLegacy memory state, , , , ) = registry.getState(); + assertTrue(state.paused); + } + + function test_revertsWhen_transmitInPausedRegistry() external { + vm.startPrank(registry.owner()); + registry.pause(); + + _transmitAndExpectRevert(usdUpkeepID18, registry, Registry.RegistryPaused.selector); + } +} + +contract Unpause is SetUp { + function test_RevertsWhen_CalledByNonOwner() external { + vm.startPrank(registry.owner()); + registry.pause(); + + vm.expectRevert(bytes("Only callable by owner")); + vm.startPrank(STRANGER); + registry.unpause(); + } + + function test_CalledByOwner_success() external { + vm.startPrank(registry.owner()); + registry.pause(); + (IAutomationV21PlusCommon.StateLegacy memory state1, , , , ) = registry.getState(); + assertTrue(state1.paused); + + registry.unpause(); + (IAutomationV21PlusCommon.StateLegacy memory state2, , , , ) = registry.getState(); + assertFalse(state2.paused); + } +} + +contract CancelUpkeep is SetUp { + event UpkeepCanceled(uint256 indexed id, uint64 indexed atBlockHeight); + + function test_RevertsWhen_IdIsInvalid_CalledByAdmin() external { + vm.startPrank(UPKEEP_ADMIN); + vm.expectRevert(Registry.CannotCancel.selector); + registry.cancelUpkeep(1111111); + } + + function test_RevertsWhen_IdIsInvalid_CalledByOwner() external { + vm.startPrank(registry.owner()); + vm.expectRevert(Registry.CannotCancel.selector); + registry.cancelUpkeep(1111111); + } + + function test_RevertsWhen_NotCalledByOwnerOrAdmin() external { + vm.startPrank(STRANGER); + vm.expectRevert(Registry.OnlyCallableByOwnerOrAdmin.selector); + registry.cancelUpkeep(linkUpkeepID); + } + + function test_RevertsWhen_UpkeepAlreadyCanceledByAdmin_CalledByOwner() external { + uint256 bn = block.number; + vm.startPrank(UPKEEP_ADMIN); + registry.cancelUpkeep(linkUpkeepID); + + vm.startPrank(registry.owner()); + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.cancelUpkeep(linkUpkeepID); + } + + function test_RevertsWhen_UpkeepAlreadyCanceledByOwner_CalledByAdmin() external { + uint256 bn = block.number; + vm.startPrank(registry.owner()); + registry.cancelUpkeep(linkUpkeepID); + + vm.startPrank(UPKEEP_ADMIN); + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.cancelUpkeep(linkUpkeepID); + } + + function test_RevertsWhen_UpkeepAlreadyCanceledByAdmin_CalledByAdmin() external { + uint256 bn = block.number; + vm.startPrank(UPKEEP_ADMIN); + registry.cancelUpkeep(linkUpkeepID); + + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.cancelUpkeep(linkUpkeepID); + } + + function test_RevertsWhen_UpkeepAlreadyCanceledByOwner_CalledByOwner() external { + uint256 bn = block.number; + vm.startPrank(registry.owner()); + registry.cancelUpkeep(linkUpkeepID); + + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.cancelUpkeep(linkUpkeepID); + } + + function test_CancelUpkeep_SetMaxValidBlockNumber_CalledByAdmin() external { + uint256 bn = block.number; + vm.startPrank(UPKEEP_ADMIN); + registry.cancelUpkeep(linkUpkeepID); + + uint256 maxValidBlocknumber = uint256(registry.getUpkeep(linkUpkeepID).maxValidBlocknumber); + + // 50 is the cancellation delay + assertEq(bn + 50, maxValidBlocknumber); + } + + function test_CancelUpkeep_SetMaxValidBlockNumber_CalledByOwner() external { + uint256 bn = block.number; + vm.startPrank(registry.owner()); + registry.cancelUpkeep(linkUpkeepID); + + uint256 maxValidBlocknumber = uint256(registry.getUpkeep(linkUpkeepID).maxValidBlocknumber); + + // cancellation by registry owner is immediate and no cancellation delay is applied + assertEq(bn, maxValidBlocknumber); + } + + function test_CancelUpkeep_EmitEvent_CalledByAdmin() external { + uint256 bn = block.number; + vm.startPrank(UPKEEP_ADMIN); + + vm.expectEmit(); + emit UpkeepCanceled(linkUpkeepID, uint64(bn + 50)); + registry.cancelUpkeep(linkUpkeepID); + } + + function test_CancelUpkeep_EmitEvent_CalledByOwner() external { + uint256 bn = block.number; + vm.startPrank(registry.owner()); + + vm.expectEmit(); + emit UpkeepCanceled(linkUpkeepID, uint64(bn)); + registry.cancelUpkeep(linkUpkeepID); + } +} + +contract SetPeerRegistryMigrationPermission is SetUp { + function test_SetPeerRegistryMigrationPermission_CalledByOwner() external { + address peer = randomAddress(); + vm.startPrank(registry.owner()); + + uint8 permission = registry.getPeerRegistryMigrationPermission(peer); + assertEq(0, permission); + + registry.setPeerRegistryMigrationPermission(peer, 1); + permission = registry.getPeerRegistryMigrationPermission(peer); + assertEq(1, permission); + + registry.setPeerRegistryMigrationPermission(peer, 2); + permission = registry.getPeerRegistryMigrationPermission(peer); + assertEq(2, permission); + + registry.setPeerRegistryMigrationPermission(peer, 0); + permission = registry.getPeerRegistryMigrationPermission(peer); + assertEq(0, permission); + } + + function test_RevertsWhen_InvalidPermission_CalledByOwner() external { + address peer = randomAddress(); + vm.startPrank(registry.owner()); + + vm.expectRevert(); + registry.setPeerRegistryMigrationPermission(peer, 100); + } + + function test_RevertsWhen_CalledByNonOwner() external { + address peer = randomAddress(); + vm.startPrank(STRANGER); + + vm.expectRevert(bytes("Only callable by owner")); + registry.setPeerRegistryMigrationPermission(peer, 1); + } +} + +contract SetUpkeepPrivilegeConfig is SetUp { + function test_RevertsWhen_CalledByNonManager() external { + vm.startPrank(STRANGER); + + vm.expectRevert(Registry.OnlyCallableByUpkeepPrivilegeManager.selector); + registry.setUpkeepPrivilegeConfig(linkUpkeepID, hex"1233"); + } + + function test_UpkeepHasEmptyConfig() external { + bytes memory cfg = registry.getUpkeepPrivilegeConfig(linkUpkeepID); + assertEq(cfg, bytes("")); + } + + function test_SetUpkeepPrivilegeConfig_CalledByManager() external { + vm.startPrank(PRIVILEGE_MANAGER); + + registry.setUpkeepPrivilegeConfig(linkUpkeepID, hex"1233"); + + bytes memory cfg = registry.getUpkeepPrivilegeConfig(linkUpkeepID); + assertEq(cfg, hex"1233"); + } +} + +contract SetAdminPrivilegeConfig is SetUp { + function test_RevertsWhen_CalledByNonManager() external { + vm.startPrank(STRANGER); + + vm.expectRevert(Registry.OnlyCallableByUpkeepPrivilegeManager.selector); + registry.setAdminPrivilegeConfig(randomAddress(), hex"1233"); + } + + function test_UpkeepHasEmptyConfig() external { + bytes memory cfg = registry.getAdminPrivilegeConfig(randomAddress()); + assertEq(cfg, bytes("")); + } + + function test_SetAdminPrivilegeConfig_CalledByManager() external { + vm.startPrank(PRIVILEGE_MANAGER); + address admin = randomAddress(); + + registry.setAdminPrivilegeConfig(admin, hex"1233"); + + bytes memory cfg = registry.getAdminPrivilegeConfig(admin); + assertEq(cfg, hex"1233"); + } +} + +contract TransferUpkeepAdmin is SetUp { + event UpkeepAdminTransferRequested(uint256 indexed id, address indexed from, address indexed to); + + function test_RevertsWhen_NotCalledByAdmin() external { + vm.startPrank(STRANGER); + + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + registry.transferUpkeepAdmin(linkUpkeepID, randomAddress()); + } + + function test_RevertsWhen_TransferToSelf() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectRevert(Registry.ValueNotChanged.selector); + registry.transferUpkeepAdmin(linkUpkeepID, UPKEEP_ADMIN); + } + + function test_RevertsWhen_UpkeepCanceled() external { + vm.startPrank(UPKEEP_ADMIN); + + registry.cancelUpkeep(linkUpkeepID); + + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.transferUpkeepAdmin(linkUpkeepID, randomAddress()); + } + + function test_DoesNotChangeUpkeepAdmin() external { + vm.startPrank(UPKEEP_ADMIN); + registry.transferUpkeepAdmin(linkUpkeepID, randomAddress()); + + assertEq(registry.getUpkeep(linkUpkeepID).admin, UPKEEP_ADMIN); + } + + function test_EmitEvent_CalledByAdmin() external { + vm.startPrank(UPKEEP_ADMIN); + address newAdmin = randomAddress(); + + vm.expectEmit(); + emit UpkeepAdminTransferRequested(linkUpkeepID, UPKEEP_ADMIN, newAdmin); + registry.transferUpkeepAdmin(linkUpkeepID, newAdmin); + + // transferring to the same propose admin won't yield another event + vm.recordLogs(); + registry.transferUpkeepAdmin(linkUpkeepID, newAdmin); + Vm.Log[] memory entries = vm.getRecordedLogs(); + assertEq(0, entries.length); + } + + function test_CancelTransfer_ByTransferToEmptyAddress() external { + vm.startPrank(UPKEEP_ADMIN); + address newAdmin = randomAddress(); + + vm.expectEmit(); + emit UpkeepAdminTransferRequested(linkUpkeepID, UPKEEP_ADMIN, newAdmin); + registry.transferUpkeepAdmin(linkUpkeepID, newAdmin); + + vm.expectEmit(); + emit UpkeepAdminTransferRequested(linkUpkeepID, UPKEEP_ADMIN, address(0)); + registry.transferUpkeepAdmin(linkUpkeepID, address(0)); + } +} + +contract AcceptUpkeepAdmin is SetUp { + event UpkeepAdminTransferred(uint256 indexed id, address indexed from, address indexed to); + + function test_RevertsWhen_NotCalledByProposedAdmin() external { + vm.startPrank(UPKEEP_ADMIN); + address newAdmin = randomAddress(); + registry.transferUpkeepAdmin(linkUpkeepID, newAdmin); + + vm.startPrank(STRANGER); + vm.expectRevert(Registry.OnlyCallableByProposedAdmin.selector); + registry.acceptUpkeepAdmin(linkUpkeepID); + } + + function test_RevertsWhen_UpkeepCanceled() external { + vm.startPrank(UPKEEP_ADMIN); + address newAdmin = randomAddress(); + registry.transferUpkeepAdmin(linkUpkeepID, newAdmin); + + registry.cancelUpkeep(linkUpkeepID); + + vm.startPrank(newAdmin); + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.acceptUpkeepAdmin(linkUpkeepID); + } + + function test_UpkeepAdminChanged() external { + vm.startPrank(UPKEEP_ADMIN); + address newAdmin = randomAddress(); + registry.transferUpkeepAdmin(linkUpkeepID, newAdmin); + + vm.startPrank(newAdmin); + vm.expectEmit(); + emit UpkeepAdminTransferred(linkUpkeepID, UPKEEP_ADMIN, newAdmin); + registry.acceptUpkeepAdmin(linkUpkeepID); + + assertEq(newAdmin, registry.getUpkeep(linkUpkeepID).admin); + } +} + +contract PauseUpkeep is SetUp { + event UpkeepPaused(uint256 indexed id); + + function test_RevertsWhen_NotCalledByUpkeepAdmin() external { + vm.startPrank(STRANGER); + + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + registry.pauseUpkeep(linkUpkeepID); + } + + function test_RevertsWhen_InvalidUpkeepId() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + registry.pauseUpkeep(linkUpkeepID + 1); + } + + function test_RevertsWhen_UpkeepAlreadyCanceled() external { + vm.startPrank(UPKEEP_ADMIN); + registry.cancelUpkeep(linkUpkeepID); + + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.pauseUpkeep(linkUpkeepID); + } + + function test_RevertsWhen_UpkeepAlreadyPaused() external { + vm.startPrank(UPKEEP_ADMIN); + registry.pauseUpkeep(linkUpkeepID); + + vm.expectRevert(Registry.OnlyUnpausedUpkeep.selector); + registry.pauseUpkeep(linkUpkeepID); + } + + function test_EmitEvent_CalledByAdmin() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectEmit(); + emit UpkeepPaused(linkUpkeepID); + registry.pauseUpkeep(linkUpkeepID); + } +} + +contract UnpauseUpkeep is SetUp { + event UpkeepUnpaused(uint256 indexed id); + + function test_RevertsWhen_InvalidUpkeepId() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + registry.unpauseUpkeep(linkUpkeepID + 1); + } + + function test_RevertsWhen_UpkeepIsNotPaused() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectRevert(Registry.OnlyPausedUpkeep.selector); + registry.unpauseUpkeep(linkUpkeepID); + } + + function test_RevertsWhen_UpkeepAlreadyCanceled() external { + vm.startPrank(UPKEEP_ADMIN); + registry.pauseUpkeep(linkUpkeepID); + + registry.cancelUpkeep(linkUpkeepID); + + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.unpauseUpkeep(linkUpkeepID); + } + + function test_RevertsWhen_NotCalledByUpkeepAdmin() external { + vm.startPrank(UPKEEP_ADMIN); + registry.pauseUpkeep(linkUpkeepID); + + vm.startPrank(STRANGER); + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + registry.unpauseUpkeep(linkUpkeepID); + } + + function test_UnpauseUpkeep_CalledByUpkeepAdmin() external { + vm.startPrank(UPKEEP_ADMIN); + registry.pauseUpkeep(linkUpkeepID); + + uint256[] memory ids1 = registry.getActiveUpkeepIDs(0, 0); + + vm.expectEmit(); + emit UpkeepUnpaused(linkUpkeepID); + registry.unpauseUpkeep(linkUpkeepID); + + uint256[] memory ids2 = registry.getActiveUpkeepIDs(0, 0); + assertEq(ids1.length + 1, ids2.length); + } +} + +contract SetUpkeepCheckData is SetUp { + event UpkeepCheckDataSet(uint256 indexed id, bytes newCheckData); + + function test_RevertsWhen_InvalidUpkeepId() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + registry.setUpkeepCheckData(linkUpkeepID + 1, hex"1234"); + } + + function test_RevertsWhen_UpkeepAlreadyCanceled() external { + vm.startPrank(UPKEEP_ADMIN); + registry.cancelUpkeep(linkUpkeepID); + + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.setUpkeepCheckData(linkUpkeepID, hex"1234"); + } + + function test_RevertsWhen_NewCheckDataTooLarge() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectRevert(Registry.CheckDataExceedsLimit.selector); + registry.setUpkeepCheckData(linkUpkeepID, new bytes(10_000)); + } + + function test_RevertsWhen_NotCalledByAdmin() external { + vm.startPrank(STRANGER); + + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + registry.setUpkeepCheckData(linkUpkeepID, hex"1234"); + } + + function test_UpdateOffchainConfig_CalledByAdmin() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectEmit(); + emit UpkeepCheckDataSet(linkUpkeepID, hex"1234"); + registry.setUpkeepCheckData(linkUpkeepID, hex"1234"); + + assertEq(registry.getUpkeep(linkUpkeepID).checkData, hex"1234"); + } + + function test_UpdateOffchainConfigOnPausedUpkeep_CalledByAdmin() external { + vm.startPrank(UPKEEP_ADMIN); + + registry.pauseUpkeep(linkUpkeepID); + + vm.expectEmit(); + emit UpkeepCheckDataSet(linkUpkeepID, hex"1234"); + registry.setUpkeepCheckData(linkUpkeepID, hex"1234"); + + assertTrue(registry.getUpkeep(linkUpkeepID).paused); + assertEq(registry.getUpkeep(linkUpkeepID).checkData, hex"1234"); + } +} + +contract SetUpkeepGasLimit is SetUp { + event UpkeepGasLimitSet(uint256 indexed id, uint96 gasLimit); + + function test_RevertsWhen_InvalidUpkeepId() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + registry.setUpkeepGasLimit(linkUpkeepID + 1, 1230000); + } + + function test_RevertsWhen_UpkeepAlreadyCanceled() external { + vm.startPrank(UPKEEP_ADMIN); + registry.cancelUpkeep(linkUpkeepID); + + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.setUpkeepGasLimit(linkUpkeepID, 1230000); + } + + function test_RevertsWhen_NewGasLimitOutOfRange() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectRevert(Registry.GasLimitOutsideRange.selector); + registry.setUpkeepGasLimit(linkUpkeepID, 300); + + vm.expectRevert(Registry.GasLimitOutsideRange.selector); + registry.setUpkeepGasLimit(linkUpkeepID, 3000000000); + } + + function test_RevertsWhen_NotCalledByAdmin() external { + vm.startPrank(STRANGER); + + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + registry.setUpkeepGasLimit(linkUpkeepID, 1230000); + } + + function test_UpdateGasLimit_CalledByAdmin() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectEmit(); + emit UpkeepGasLimitSet(linkUpkeepID, 1230000); + registry.setUpkeepGasLimit(linkUpkeepID, 1230000); + + assertEq(registry.getUpkeep(linkUpkeepID).performGas, 1230000); + } +} + +contract SetUpkeepOffchainConfig is SetUp { + event UpkeepOffchainConfigSet(uint256 indexed id, bytes offchainConfig); + + function test_RevertsWhen_InvalidUpkeepId() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + registry.setUpkeepOffchainConfig(linkUpkeepID + 1, hex"1233"); + } + + function test_RevertsWhen_UpkeepAlreadyCanceled() external { + vm.startPrank(UPKEEP_ADMIN); + registry.cancelUpkeep(linkUpkeepID); + + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.setUpkeepOffchainConfig(linkUpkeepID, hex"1234"); + } + + function test_RevertsWhen_NotCalledByAdmin() external { + vm.startPrank(STRANGER); + + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + registry.setUpkeepOffchainConfig(linkUpkeepID, hex"1234"); + } + + function test_UpdateOffchainConfig_CalledByAdmin() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectEmit(); + emit UpkeepOffchainConfigSet(linkUpkeepID, hex"1234"); + registry.setUpkeepOffchainConfig(linkUpkeepID, hex"1234"); + + assertEq(registry.getUpkeep(linkUpkeepID).offchainConfig, hex"1234"); + } +} + +contract SetUpkeepTriggerConfig is SetUp { + event UpkeepTriggerConfigSet(uint256 indexed id, bytes triggerConfig); + + function test_RevertsWhen_InvalidUpkeepId() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + registry.setUpkeepTriggerConfig(linkUpkeepID + 1, hex"1233"); + } + + function test_RevertsWhen_UpkeepAlreadyCanceled() external { + vm.startPrank(UPKEEP_ADMIN); + registry.cancelUpkeep(linkUpkeepID); + + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.setUpkeepTriggerConfig(linkUpkeepID, hex"1234"); + } + + function test_RevertsWhen_NotCalledByAdmin() external { + vm.startPrank(STRANGER); + + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + registry.setUpkeepTriggerConfig(linkUpkeepID, hex"1234"); + } + + function test_UpdateTriggerConfig_CalledByAdmin() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectEmit(); + emit UpkeepTriggerConfigSet(linkUpkeepID, hex"1234"); + registry.setUpkeepTriggerConfig(linkUpkeepID, hex"1234"); + + assertEq(registry.getUpkeepTriggerConfig(linkUpkeepID), hex"1234"); + } +} + +contract TransferPayeeship is SetUp { + event PayeeshipTransferRequested(address indexed transmitter, address indexed from, address indexed to); + + function test_RevertsWhen_NotCalledByPayee() external { + vm.startPrank(STRANGER); + + vm.expectRevert(Registry.OnlyCallableByPayee.selector); + registry.transferPayeeship(TRANSMITTERS[0], randomAddress()); + } + + function test_RevertsWhen_TransferToSelf() external { + registry.setPayees(PAYEES); + vm.startPrank(PAYEES[0]); + + vm.expectRevert(Registry.ValueNotChanged.selector); + registry.transferPayeeship(TRANSMITTERS[0], PAYEES[0]); + } + + function test_Transfer_DoesNotChangePayee() external { + registry.setPayees(PAYEES); + + vm.startPrank(PAYEES[0]); + + registry.transferPayeeship(TRANSMITTERS[0], randomAddress()); + + (, , , , address payee) = registry.getTransmitterInfo(TRANSMITTERS[0]); + assertEq(PAYEES[0], payee); + } + + function test_EmitEvent_CalledByPayee() external { + registry.setPayees(PAYEES); + + vm.startPrank(PAYEES[0]); + address newPayee = randomAddress(); + + vm.expectEmit(); + emit PayeeshipTransferRequested(TRANSMITTERS[0], PAYEES[0], newPayee); + registry.transferPayeeship(TRANSMITTERS[0], newPayee); + + // transferring to the same propose payee won't yield another event + vm.recordLogs(); + registry.transferPayeeship(TRANSMITTERS[0], newPayee); + Vm.Log[] memory entries = vm.getRecordedLogs(); + assertEq(0, entries.length); + } +} + +contract AcceptPayeeship is SetUp { + event PayeeshipTransferred(address indexed transmitter, address indexed from, address indexed to); + + function test_RevertsWhen_NotCalledByProposedPayee() external { + registry.setPayees(PAYEES); + + vm.startPrank(PAYEES[0]); + address newPayee = randomAddress(); + registry.transferPayeeship(TRANSMITTERS[0], newPayee); + + vm.startPrank(STRANGER); + vm.expectRevert(Registry.OnlyCallableByProposedPayee.selector); + registry.acceptPayeeship(TRANSMITTERS[0]); + } + + function test_PayeeChanged() external { + registry.setPayees(PAYEES); + + vm.startPrank(PAYEES[0]); + address newPayee = randomAddress(); + registry.transferPayeeship(TRANSMITTERS[0], newPayee); + + vm.startPrank(newPayee); + vm.expectEmit(); + emit PayeeshipTransferred(TRANSMITTERS[0], PAYEES[0], newPayee); + registry.acceptPayeeship(TRANSMITTERS[0]); + + (, , , , address payee) = registry.getTransmitterInfo(TRANSMITTERS[0]); + assertEq(newPayee, payee); + } +} + +contract SetPayees is SetUp { + event PayeesUpdated(address[] transmitters, address[] payees); + + function test_RevertsWhen_NotCalledByOwner() external { + vm.startPrank(STRANGER); + + vm.expectRevert(bytes("Only callable by owner")); + registry.setPayees(NEW_PAYEES); + } + + function test_RevertsWhen_PayeesLengthError() external { + vm.startPrank(registry.owner()); + + address[] memory payees = new address[](5); + vm.expectRevert(Registry.ParameterLengthError.selector); + registry.setPayees(payees); + } + + function test_RevertsWhen_InvalidPayee() external { + vm.startPrank(registry.owner()); + + NEW_PAYEES[0] = address(0); + vm.expectRevert(Registry.InvalidPayee.selector); + registry.setPayees(NEW_PAYEES); + } + + function test_SetPayees_WhenExistingPayeesAreEmpty() external { + (registry, ) = deployAndConfigureRegistryAndRegistrar(AutoBase.PayoutMode.ON_CHAIN); + + for (uint256 i = 0; i < TRANSMITTERS.length; i++) { + (, , , , address payee) = registry.getTransmitterInfo(TRANSMITTERS[i]); + assertEq(address(0), payee); + } + + vm.startPrank(registry.owner()); + + vm.expectEmit(); + emit PayeesUpdated(TRANSMITTERS, PAYEES); + registry.setPayees(PAYEES); + for (uint256 i = 0; i < TRANSMITTERS.length; i++) { + (bool active, , , , address payee) = registry.getTransmitterInfo(TRANSMITTERS[i]); + assertTrue(active); + assertEq(PAYEES[i], payee); + } + } + + function test_DotNotSetPayeesToIgnoredAddress() external { + address IGNORE_ADDRESS = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF; + (registry, ) = deployAndConfigureRegistryAndRegistrar(AutoBase.PayoutMode.ON_CHAIN); + PAYEES[0] = IGNORE_ADDRESS; + + registry.setPayees(PAYEES); + (bool active, , , , address payee) = registry.getTransmitterInfo(TRANSMITTERS[0]); + assertTrue(active); + assertEq(address(0), payee); + + (active, , , , payee) = registry.getTransmitterInfo(TRANSMITTERS[1]); + assertTrue(active); + assertEq(PAYEES[1], payee); + } +} + +contract GetActiveUpkeepIDs is SetUp { + function test_RevertsWhen_IndexOutOfRange() external { + vm.expectRevert(Registry.IndexOutOfRange.selector); + registry.getActiveUpkeepIDs(5, 0); + + vm.expectRevert(Registry.IndexOutOfRange.selector); + registry.getActiveUpkeepIDs(6, 0); + } + + function test_ReturnsAllUpkeeps_WhenMaxCountIsZero() external { + uint256[] memory uids = registry.getActiveUpkeepIDs(0, 0); + assertEq(5, uids.length); + + uids = registry.getActiveUpkeepIDs(2, 0); + assertEq(3, uids.length); + } + + function test_ReturnsAllRemainingUpkeeps_WhenMaxCountIsTooLarge() external { + uint256[] memory uids = registry.getActiveUpkeepIDs(2, 20); + assertEq(3, uids.length); + } + + function test_ReturnsUpkeeps_BoundByMaxCount() external { + uint256[] memory uids = registry.getActiveUpkeepIDs(1, 2); + assertEq(2, uids.length); + assertEq(uids[0], linkUpkeepID2); + assertEq(uids[1], usdUpkeepID18); + } +} diff --git a/contracts/src/v0.8/automation/test/v2_3/BaseTest.t.sol b/contracts/src/v0.8/automation/test/v2_3/BaseTest.t.sol index 9016f52c55..e0d15daab6 100644 --- a/contracts/src/v0.8/automation/test/v2_3/BaseTest.t.sol +++ b/contracts/src/v0.8/automation/test/v2_3/BaseTest.t.sol @@ -20,14 +20,14 @@ import {ChainModuleBase} from "../../chains/ChainModuleBase.sol"; import {IERC20Metadata as IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {MockUpkeep} from "../../mocks/MockUpkeep.sol"; import {IWrappedNative} from "../../interfaces/v2_3/IWrappedNative.sol"; -import {WETH9} from "./WETH9.sol"; +import {WETH9} from "../WETH9.sol"; /** * @title BaseTest provides basic test setup procedures and dependencies for use by other * unit tests */ contract BaseTest is Test { - // test state (not exposed to derrived tests) + // test state (not exposed to derived tests) uint256 private nonce; // constants @@ -283,7 +283,6 @@ contract BaseTest is Test { billingTokenAddresses, billingTokenConfigs ); - registry.setPayees(PAYEES); return (registry, registrar); } @@ -356,40 +355,58 @@ contract BaseTest is Test { ); } + // tests single upkeep, expects success function _transmit(uint256 id, Registry registry) internal { uint256[] memory ids = new uint256[](1); ids[0] = id; - _transmit(ids, registry); + _handleTransmit(ids, registry, bytes4(0)); } + // tests multiple upkeeps, expects success function _transmit(uint256[] memory ids, Registry registry) internal { - uint256[] memory upkeepIds = new uint256[](ids.length); - uint256[] memory gasLimits = new uint256[](ids.length); - bytes[] memory performDatas = new bytes[](ids.length); - bytes[] memory triggers = new bytes[](ids.length); - for (uint256 i = 0; i < ids.length; i++) { - upkeepIds[i] = ids[i]; - gasLimits[i] = registry.getUpkeep(ids[i]).performGas; - performDatas[i] = new bytes(0); - uint8 triggerType = registry.getTriggerType(ids[i]); - if (triggerType == 0) { - triggers[i] = _encodeConditionalTrigger( - AutoBase.ConditionalTrigger(uint32(block.number - 1), blockhash(block.number - 1)) - ); - } else { - revert("not implemented"); + _handleTransmit(ids, registry, bytes4(0)); + } + + // tests single upkeep, expects revert + function _transmitAndExpectRevert(uint256 id, Registry registry, bytes4 selector) internal { + uint256[] memory ids = new uint256[](1); + ids[0] = id; + _handleTransmit(ids, registry, selector); + } + + // private function not exposed to actual testing contract + function _handleTransmit(uint256[] memory ids, Registry registry, bytes4 selector) private { + bytes memory reportBytes; + { + uint256[] memory upkeepIds = new uint256[](ids.length); + uint256[] memory gasLimits = new uint256[](ids.length); + bytes[] memory performDatas = new bytes[](ids.length); + bytes[] memory triggers = new bytes[](ids.length); + for (uint256 i = 0; i < ids.length; i++) { + upkeepIds[i] = ids[i]; + gasLimits[i] = registry.getUpkeep(ids[i]).performGas; + performDatas[i] = new bytes(0); + uint8 triggerType = registry.getTriggerType(ids[i]); + if (triggerType == 0) { + triggers[i] = _encodeConditionalTrigger( + AutoBase.ConditionalTrigger(uint32(block.number - 1), blockhash(block.number - 1)) + ); + } else { + revert("not implemented"); + } } - } - AutoBase.Report memory report = AutoBase.Report( - uint256(1000000000), - uint256(2000000000), - upkeepIds, - gasLimits, - triggers, - performDatas - ); - bytes memory reportBytes = _encodeReport(report); + AutoBase.Report memory report = AutoBase.Report( + uint256(1000000000), + uint256(2000000000), + upkeepIds, + gasLimits, + triggers, + performDatas + ); + + reportBytes = _encodeReport(report); + } (, , bytes32 configDigest) = registry.latestConfigDetails(); bytes32[3] memory reportContext = [configDigest, configDigest, configDigest]; uint256[] memory signerPKs = new uint256[](2); @@ -398,6 +415,9 @@ contract BaseTest is Test { (bytes32[] memory rs, bytes32[] memory ss, bytes32 vs) = _signReport(reportBytes, reportContext, signerPKs); vm.startPrank(TRANSMITTERS[0]); + if (selector != bytes4(0)) { + vm.expectRevert(selector); + } registry.transmit(reportContext, reportBytes, rs, ss, vs); vm.stopPrank(); } diff --git a/contracts/src/v0.8/automation/test/v2_3_zksync/BaseTest.t.sol b/contracts/src/v0.8/automation/test/v2_3_zksync/BaseTest.t.sol new file mode 100644 index 0000000000..cde05ab3a2 --- /dev/null +++ b/contracts/src/v0.8/automation/test/v2_3_zksync/BaseTest.t.sol @@ -0,0 +1,500 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import "forge-std/Test.sol"; + +import {LinkToken} from "../../../shared/token/ERC677/LinkToken.sol"; +import {ERC20Mock} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/mocks/ERC20Mock.sol"; +import {ERC20Mock6Decimals} from "../../mocks/ERC20Mock6Decimals.sol"; +import {MockV3Aggregator} from "../../../tests/MockV3Aggregator.sol"; +import {AutomationForwarderLogic} from "../../AutomationForwarderLogic.sol"; +import {UpkeepTranscoder5_0 as Transcoder} from "../../v2_3/UpkeepTranscoder5_0.sol"; +import {ZKSyncAutomationRegistry2_3} from "../../v2_3_zksync/ZKSyncAutomationRegistry2_3.sol"; +import {ZKSyncAutomationRegistryLogicA2_3} from "../../v2_3_zksync/ZKSyncAutomationRegistryLogicA2_3.sol"; +import {ZKSyncAutomationRegistryLogicB2_3} from "../../v2_3_zksync/ZKSyncAutomationRegistryLogicB2_3.sol"; +import {ZKSyncAutomationRegistryLogicC2_3} from "../../v2_3_zksync/ZKSyncAutomationRegistryLogicC2_3.sol"; +import {ZKSyncAutomationRegistryBase2_3 as ZKSyncAutoBase} from "../../v2_3_zksync/ZKSyncAutomationRegistryBase2_3.sol"; +import {IAutomationRegistryMaster2_3 as Registry, AutomationRegistryBase2_3} from "../../interfaces/v2_3/IAutomationRegistryMaster2_3.sol"; +import {AutomationRegistrar2_3} from "../../v2_3/AutomationRegistrar2_3.sol"; +import {ChainModuleBase} from "../../chains/ChainModuleBase.sol"; +import {IERC20Metadata as IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import {MockUpkeep} from "../../mocks/MockUpkeep.sol"; +import {IWrappedNative} from "../../interfaces/v2_3/IWrappedNative.sol"; +import {WETH9} from "../WETH9.sol"; +import {MockGasBoundCaller} from "../../../tests/MockGasBoundCaller.sol"; +import {MockZKSyncSystemContext} from "../../../tests/MockZKSyncSystemContext.sol"; + +/** + * @title BaseTest provides basic test setup procedures and dependencies for use by other + * unit tests + */ +contract BaseTest is Test { + // test state (not exposed to derived tests) + uint256 private nonce; + + // constants + address internal constant ZERO_ADDRESS = address(0); + uint32 internal constant DEFAULT_GAS_FEE_PPB = 10_000_000; + uint24 internal constant DEFAULT_FLAT_FEE_MILLI_CENTS = 2_000; + + // config + uint8 internal constant F = 1; // number of faulty nodes + uint64 internal constant OFFCHAIN_CONFIG_VERSION = 30; // 2 for OCR2 + + // contracts + LinkToken internal linkToken; + ERC20Mock6Decimals internal usdToken6; + ERC20Mock internal usdToken18; + ERC20Mock internal usdToken18_2; + WETH9 internal weth; + MockV3Aggregator internal LINK_USD_FEED; + MockV3Aggregator internal NATIVE_USD_FEED; + MockV3Aggregator internal USDTOKEN_USD_FEED; + MockV3Aggregator internal FAST_GAS_FEED; + MockUpkeep internal TARGET1; + MockUpkeep internal TARGET2; + Transcoder internal TRANSCODER; + MockGasBoundCaller internal GAS_BOUND_CALLER; + MockZKSyncSystemContext internal SYSTEM_CONTEXT; + + // roles + address internal constant OWNER = address(uint160(uint256(keccak256("OWNER")))); + address internal constant UPKEEP_ADMIN = address(uint160(uint256(keccak256("UPKEEP_ADMIN")))); + address internal constant FINANCE_ADMIN = address(uint160(uint256(keccak256("FINANCE_ADMIN")))); + address internal constant STRANGER = address(uint160(uint256(keccak256("STRANGER")))); + address internal constant BROKE_USER = address(uint160(uint256(keccak256("BROKE_USER")))); // do not mint to this address + address internal constant PRIVILEGE_MANAGER = address(uint160(uint256(keccak256("PRIVILEGE_MANAGER")))); + + // nodes + uint256 internal constant SIGNING_KEY0 = 0x7b2e97fe057e6de99d6872a2ef2abf52c9b4469bc848c2465ac3fcd8d336e81d; + uint256 internal constant SIGNING_KEY1 = 0xab56160806b05ef1796789248e1d7f34a6465c5280899159d645218cd216cee6; + uint256 internal constant SIGNING_KEY2 = 0x6ec7caa8406a49b76736602810e0a2871959fbbb675e23a8590839e4717f1f7f; + uint256 internal constant SIGNING_KEY3 = 0x80f14b11da94ae7f29d9a7713ea13dc838e31960a5c0f2baf45ed458947b730a; + address[] internal SIGNERS = new address[](4); + address[] internal TRANSMITTERS = new address[](4); + address[] internal NEW_TRANSMITTERS = new address[](4); + address[] internal PAYEES = new address[](4); + address[] internal NEW_PAYEES = new address[](4); + + function setUp() public virtual { + vm.startPrank(OWNER); + linkToken = new LinkToken(); + linkToken.grantMintRole(OWNER); + usdToken18 = new ERC20Mock("MOCK_ERC20_18Decimals", "MOCK_ERC20_18Decimals", OWNER, 0); + usdToken18_2 = new ERC20Mock("Second_MOCK_ERC20_18Decimals", "Second_MOCK_ERC20_18Decimals", OWNER, 0); + usdToken6 = new ERC20Mock6Decimals("MOCK_ERC20_6Decimals", "MOCK_ERC20_6Decimals", OWNER, 0); + weth = new WETH9(); + + LINK_USD_FEED = new MockV3Aggregator(8, 2_000_000_000); // $20 + NATIVE_USD_FEED = new MockV3Aggregator(8, 400_000_000_000); // $4,000 + USDTOKEN_USD_FEED = new MockV3Aggregator(8, 100_000_000); // $1 + FAST_GAS_FEED = new MockV3Aggregator(0, 1_000_000_000); // 1 gwei + + TARGET1 = new MockUpkeep(); + TARGET2 = new MockUpkeep(); + + TRANSCODER = new Transcoder(); + GAS_BOUND_CALLER = new MockGasBoundCaller(); + SYSTEM_CONTEXT = new MockZKSyncSystemContext(); + + bytes memory callerCode = address(GAS_BOUND_CALLER).code; + vm.etch(0xc706EC7dfA5D4Dc87f29f859094165E8290530f5, callerCode); + + bytes memory contextCode = address(SYSTEM_CONTEXT).code; + vm.etch(0x000000000000000000000000000000000000800B, contextCode); + + SIGNERS[0] = vm.addr(SIGNING_KEY0); //0xc110458BE52CaA6bB68E66969C3218A4D9Db0211 + SIGNERS[1] = vm.addr(SIGNING_KEY1); //0xc110a19c08f1da7F5FfB281dc93630923F8E3719 + SIGNERS[2] = vm.addr(SIGNING_KEY2); //0xc110fdF6e8fD679C7Cc11602d1cd829211A18e9b + SIGNERS[3] = vm.addr(SIGNING_KEY3); //0xc11028017c9b445B6bF8aE7da951B5cC28B326C0 + + TRANSMITTERS[0] = address(uint160(uint256(keccak256("TRANSMITTER1")))); + TRANSMITTERS[1] = address(uint160(uint256(keccak256("TRANSMITTER2")))); + TRANSMITTERS[2] = address(uint160(uint256(keccak256("TRANSMITTER3")))); + TRANSMITTERS[3] = address(uint160(uint256(keccak256("TRANSMITTER4")))); + NEW_TRANSMITTERS[0] = address(uint160(uint256(keccak256("TRANSMITTER1")))); + NEW_TRANSMITTERS[1] = address(uint160(uint256(keccak256("TRANSMITTER2")))); + NEW_TRANSMITTERS[2] = address(uint160(uint256(keccak256("TRANSMITTER5")))); + NEW_TRANSMITTERS[3] = address(uint160(uint256(keccak256("TRANSMITTER6")))); + + PAYEES[0] = address(100); + PAYEES[1] = address(101); + PAYEES[2] = address(102); + PAYEES[3] = address(103); + NEW_PAYEES[0] = address(100); + NEW_PAYEES[1] = address(101); + NEW_PAYEES[2] = address(106); + NEW_PAYEES[3] = address(107); + + // mint funds + vm.deal(OWNER, 100 ether); + vm.deal(UPKEEP_ADMIN, 100 ether); + vm.deal(FINANCE_ADMIN, 100 ether); + vm.deal(STRANGER, 100 ether); + + linkToken.mint(OWNER, 1000e18); + linkToken.mint(UPKEEP_ADMIN, 1000e18); + linkToken.mint(FINANCE_ADMIN, 1000e18); + linkToken.mint(STRANGER, 1000e18); + + usdToken18.mint(OWNER, 1000e18); + usdToken18.mint(UPKEEP_ADMIN, 1000e18); + usdToken18.mint(FINANCE_ADMIN, 1000e18); + usdToken18.mint(STRANGER, 1000e18); + + usdToken18_2.mint(UPKEEP_ADMIN, 1000e18); + + usdToken6.mint(OWNER, 1000e6); + usdToken6.mint(UPKEEP_ADMIN, 1000e6); + usdToken6.mint(FINANCE_ADMIN, 1000e6); + usdToken6.mint(STRANGER, 1000e6); + + weth.mint(OWNER, 1000e18); + weth.mint(UPKEEP_ADMIN, 1000e18); + weth.mint(FINANCE_ADMIN, 1000e18); + weth.mint(STRANGER, 1000e18); + + vm.stopPrank(); + } + + /// @notice deploys the component parts of a registry, but nothing more + function deployZKSyncRegistry(ZKSyncAutoBase.PayoutMode payoutMode) internal returns (Registry) { + AutomationForwarderLogic forwarderLogic = new AutomationForwarderLogic(); + ZKSyncAutomationRegistryLogicC2_3 logicC2_3 = new ZKSyncAutomationRegistryLogicC2_3( + address(linkToken), + address(LINK_USD_FEED), + address(NATIVE_USD_FEED), + address(FAST_GAS_FEED), + address(forwarderLogic), + ZERO_ADDRESS, + payoutMode, + address(weth) + ); + ZKSyncAutomationRegistryLogicB2_3 logicB2_3 = new ZKSyncAutomationRegistryLogicB2_3(logicC2_3); + ZKSyncAutomationRegistryLogicA2_3 logicA2_3 = new ZKSyncAutomationRegistryLogicA2_3(logicB2_3); + return Registry(payable(address(new ZKSyncAutomationRegistry2_3(logicA2_3)))); + } + + /// @notice deploys and configures a registry, registrar, and everything needed for most tests + function deployAndConfigureZKSyncRegistryAndRegistrar( + ZKSyncAutoBase.PayoutMode payoutMode + ) internal returns (Registry, AutomationRegistrar2_3) { + Registry registry = deployZKSyncRegistry(payoutMode); + + IERC20[] memory billingTokens = new IERC20[](4); + billingTokens[0] = IERC20(address(usdToken18)); + billingTokens[1] = IERC20(address(weth)); + billingTokens[2] = IERC20(address(linkToken)); + billingTokens[3] = IERC20(address(usdToken6)); + uint256[] memory minRegistrationFees = new uint256[](billingTokens.length); + minRegistrationFees[0] = 100e18; // 100 USD + minRegistrationFees[1] = 5e18; // 5 Native + minRegistrationFees[2] = 5e18; // 5 LINK + minRegistrationFees[3] = 100e6; // 100 USD + address[] memory billingTokenAddresses = new address[](billingTokens.length); + for (uint256 i = 0; i < billingTokens.length; i++) { + billingTokenAddresses[i] = address(billingTokens[i]); + } + AutomationRegistryBase2_3.BillingConfig[] + memory billingTokenConfigs = new AutomationRegistryBase2_3.BillingConfig[](billingTokens.length); + billingTokenConfigs[0] = AutomationRegistryBase2_3.BillingConfig({ + gasFeePPB: DEFAULT_GAS_FEE_PPB, // 15% + flatFeeMilliCents: DEFAULT_FLAT_FEE_MILLI_CENTS, // 2 cents + priceFeed: address(USDTOKEN_USD_FEED), + fallbackPrice: 100_000_000, // $1 + minSpend: 1e18, // 1 USD + decimals: 18 + }); + billingTokenConfigs[1] = AutomationRegistryBase2_3.BillingConfig({ + gasFeePPB: DEFAULT_GAS_FEE_PPB, // 15% + flatFeeMilliCents: DEFAULT_FLAT_FEE_MILLI_CENTS, // 2 cents + priceFeed: address(NATIVE_USD_FEED), + fallbackPrice: 100_000_000, // $1 + minSpend: 5e18, // 5 Native + decimals: 18 + }); + billingTokenConfigs[2] = AutomationRegistryBase2_3.BillingConfig({ + gasFeePPB: DEFAULT_GAS_FEE_PPB, // 10% + flatFeeMilliCents: DEFAULT_FLAT_FEE_MILLI_CENTS, // 2 cents + priceFeed: address(LINK_USD_FEED), + fallbackPrice: 1_000_000_000, // $10 + minSpend: 1e18, // 1 LINK + decimals: 18 + }); + billingTokenConfigs[3] = AutomationRegistryBase2_3.BillingConfig({ + gasFeePPB: DEFAULT_GAS_FEE_PPB, // 15% + flatFeeMilliCents: DEFAULT_FLAT_FEE_MILLI_CENTS, // 2 cents + priceFeed: address(USDTOKEN_USD_FEED), + fallbackPrice: 1e8, // $1 + minSpend: 1e6, // 1 USD + decimals: 6 + }); + + if (payoutMode == ZKSyncAutoBase.PayoutMode.OFF_CHAIN) { + // remove LINK as a payment method if we are settling offchain + assembly { + mstore(billingTokens, 2) + mstore(minRegistrationFees, 2) + mstore(billingTokenAddresses, 2) + mstore(billingTokenConfigs, 2) + } + } + + // deploy registrar + AutomationRegistrar2_3.InitialTriggerConfig[] + memory triggerConfigs = new AutomationRegistrar2_3.InitialTriggerConfig[](2); + triggerConfigs[0] = AutomationRegistrar2_3.InitialTriggerConfig({ + triggerType: 0, // condition + autoApproveType: AutomationRegistrar2_3.AutoApproveType.DISABLED, + autoApproveMaxAllowed: 0 + }); + triggerConfigs[1] = AutomationRegistrar2_3.InitialTriggerConfig({ + triggerType: 1, // log + autoApproveType: AutomationRegistrar2_3.AutoApproveType.DISABLED, + autoApproveMaxAllowed: 0 + }); + AutomationRegistrar2_3 registrar = new AutomationRegistrar2_3( + address(linkToken), + registry, + triggerConfigs, + billingTokens, + minRegistrationFees, + IWrappedNative(address(weth)) + ); + + address[] memory registrars; + registrars = new address[](1); + registrars[0] = address(registrar); + + AutomationRegistryBase2_3.OnchainConfig memory cfg = AutomationRegistryBase2_3.OnchainConfig({ + checkGasLimit: 5_000_000, + stalenessSeconds: 90_000, + gasCeilingMultiplier: 2, + maxPerformGas: 10_000_000, + maxCheckDataSize: 5_000, + maxPerformDataSize: 5_000, + maxRevertDataSize: 5_000, + fallbackGasPrice: 20_000_000_000, + fallbackLinkPrice: 2_000_000_000, // $20 + fallbackNativePrice: 400_000_000_000, // $4,000 + transcoder: address(TRANSCODER), + registrars: registrars, + upkeepPrivilegeManager: PRIVILEGE_MANAGER, + chainModule: address(new ChainModuleBase()), + reorgProtectionEnabled: true, + financeAdmin: FINANCE_ADMIN + }); + + registry.setConfigTypeSafe( + SIGNERS, + TRANSMITTERS, + F, + cfg, + OFFCHAIN_CONFIG_VERSION, + "", + billingTokenAddresses, + billingTokenConfigs + ); + return (registry, registrar); + } + + /// @notice this function updates the billing config for the provided token on the provided registry, + /// and throws an error if the token is not found + function _updateBillingTokenConfig( + Registry registry, + address billingToken, + AutomationRegistryBase2_3.BillingConfig memory newConfig + ) internal { + (, , address[] memory signers, address[] memory transmitters, uint8 f) = registry.getState(); + AutomationRegistryBase2_3.OnchainConfig memory config = registry.getConfig(); + address[] memory billingTokens = registry.getBillingTokens(); + + AutomationRegistryBase2_3.BillingConfig[] + memory billingTokenConfigs = new AutomationRegistryBase2_3.BillingConfig[](billingTokens.length); + + bool found = false; + for (uint256 i = 0; i < billingTokens.length; i++) { + if (billingTokens[i] == billingToken) { + found = true; + billingTokenConfigs[i] = newConfig; + } else { + billingTokenConfigs[i] = registry.getBillingTokenConfig(billingTokens[i]); + } + } + require(found, "could not find billing token provided on registry"); + + registry.setConfigTypeSafe( + signers, + transmitters, + f, + config, + OFFCHAIN_CONFIG_VERSION, + "", + billingTokens, + billingTokenConfigs + ); + } + + /// @notice this function removes a billing token from the registry + function _removeBillingTokenConfig(Registry registry, address billingToken) internal { + (, , address[] memory signers, address[] memory transmitters, uint8 f) = registry.getState(); + AutomationRegistryBase2_3.OnchainConfig memory config = registry.getConfig(); + address[] memory billingTokens = registry.getBillingTokens(); + + address[] memory newBillingTokens = new address[](billingTokens.length - 1); + AutomationRegistryBase2_3.BillingConfig[] + memory billingTokenConfigs = new AutomationRegistryBase2_3.BillingConfig[](billingTokens.length - 1); + + uint256 j = 0; + for (uint256 i = 0; i < billingTokens.length; i++) { + if (billingTokens[i] != billingToken) { + if (j == newBillingTokens.length) revert("could not find billing token provided on registry"); + newBillingTokens[j] = billingTokens[i]; + billingTokenConfigs[j] = registry.getBillingTokenConfig(billingTokens[i]); + j++; + } + } + + registry.setConfigTypeSafe( + signers, + transmitters, + f, + config, + OFFCHAIN_CONFIG_VERSION, + "", + newBillingTokens, + billingTokenConfigs + ); + } + + function _transmit(uint256 id, Registry registry, bytes4 selector) internal { + uint256[] memory ids = new uint256[](1); + ids[0] = id; + _transmit(ids, registry, selector); + } + + function _transmit(uint256[] memory ids, Registry registry, bytes4 selector) internal { + bytes memory reportBytes; + { + uint256[] memory upkeepIds = new uint256[](ids.length); + uint256[] memory gasLimits = new uint256[](ids.length); + bytes[] memory performDatas = new bytes[](ids.length); + bytes[] memory triggers = new bytes[](ids.length); + for (uint256 i = 0; i < ids.length; i++) { + upkeepIds[i] = ids[i]; + gasLimits[i] = registry.getUpkeep(ids[i]).performGas; + performDatas[i] = new bytes(0); + uint8 triggerType = registry.getTriggerType(ids[i]); + if (triggerType == 0) { + triggers[i] = _encodeConditionalTrigger( + ZKSyncAutoBase.ConditionalTrigger(uint32(block.number - 1), blockhash(block.number - 1)) + ); + } else { + revert("not implemented"); + } + } + ZKSyncAutoBase.Report memory report = ZKSyncAutoBase.Report( + uint256(1000000000), + uint256(2000000000), + upkeepIds, + gasLimits, + triggers, + performDatas + ); + + reportBytes = _encodeReport(report); + } + (, , bytes32 configDigest) = registry.latestConfigDetails(); + bytes32[3] memory reportContext = [configDigest, configDigest, configDigest]; + uint256[] memory signerPKs = new uint256[](2); + signerPKs[0] = SIGNING_KEY0; + signerPKs[1] = SIGNING_KEY1; + (bytes32[] memory rs, bytes32[] memory ss, bytes32 vs) = _signReport(reportBytes, reportContext, signerPKs); + + vm.startPrank(TRANSMITTERS[0]); + if (selector != bytes4(0)) { + vm.expectRevert(selector); + } + registry.transmit(reportContext, reportBytes, rs, ss, vs); + vm.stopPrank(); + } + + /// @notice Gather signatures on report data + /// @param report - Report bytes generated from `_buildReport` + /// @param reportContext - Report context bytes32 generated from `_buildReport` + /// @param signerPrivateKeys - One or more addresses that will sign the report data + /// @return rawRs - Signature rs + /// @return rawSs - Signature ss + /// @return rawVs - Signature vs + function _signReport( + bytes memory report, + bytes32[3] memory reportContext, + uint256[] memory signerPrivateKeys + ) internal pure returns (bytes32[] memory, bytes32[] memory, bytes32) { + bytes32[] memory rs = new bytes32[](signerPrivateKeys.length); + bytes32[] memory ss = new bytes32[](signerPrivateKeys.length); + bytes memory vs = new bytes(signerPrivateKeys.length); + + bytes32 reportDigest = keccak256(abi.encodePacked(keccak256(report), reportContext)); + + for (uint256 i = 0; i < signerPrivateKeys.length; i++) { + (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPrivateKeys[i], reportDigest); + rs[i] = r; + ss[i] = s; + vs[i] = bytes1(v - 27); + } + + return (rs, ss, bytes32(vs)); + } + + function _encodeReport(ZKSyncAutoBase.Report memory report) internal pure returns (bytes memory reportBytes) { + return abi.encode(report); + } + + function _encodeConditionalTrigger( + ZKSyncAutoBase.ConditionalTrigger memory trigger + ) internal pure returns (bytes memory triggerBytes) { + return abi.encode(trigger.blockNum, trigger.blockHash); + } + + /// @dev mints LINK to the recipient + function _mintLink(address recipient, uint256 amount) internal { + vm.prank(OWNER); + linkToken.mint(recipient, amount); + } + + /// @dev mints USDToken with 18 decimals to the recipient + function _mintERC20_18Decimals(address recipient, uint256 amount) internal { + vm.prank(OWNER); + usdToken18.mint(recipient, amount); + } + + /// @dev returns a pseudo-random 32 bytes + function _random() private returns (bytes32) { + nonce++; + return keccak256(abi.encode(block.timestamp, nonce)); + } + + /// @dev returns a pseudo-random number + function randomNumber() internal returns (uint256) { + return uint256(_random()); + } + + /// @dev returns a pseudo-random address + function randomAddress() internal returns (address) { + return address(uint160(randomNumber())); + } + + /// @dev returns a pseudo-random byte array + function randomBytes(uint256 length) internal returns (bytes memory) { + bytes memory result = new bytes(length); + bytes32 entropy; + for (uint256 i = 0; i < length; i++) { + if (i % 32 == 0) { + entropy = _random(); + } + result[i] = entropy[i % 32]; + } + return result; + } +} diff --git a/contracts/src/v0.8/automation/test/v2_3_zksync/ZKSyncAutomationRegistry2_3.t.sol b/contracts/src/v0.8/automation/test/v2_3_zksync/ZKSyncAutomationRegistry2_3.t.sol new file mode 100644 index 0000000000..7098d9f38f --- /dev/null +++ b/contracts/src/v0.8/automation/test/v2_3_zksync/ZKSyncAutomationRegistry2_3.t.sol @@ -0,0 +1,2772 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.19; + +import {Vm} from "forge-std/Test.sol"; +import {BaseTest} from "./BaseTest.t.sol"; +import {ZKSyncAutomationRegistryBase2_3 as AutoBase} from "../../v2_3_zksync/ZKSyncAutomationRegistryBase2_3.sol"; +import {AutomationRegistrar2_3 as Registrar} from "../../v2_3/AutomationRegistrar2_3.sol"; +import {IAutomationRegistryMaster2_3 as Registry, AutomationRegistryBase2_3, IAutomationV21PlusCommon} from "../../interfaces/v2_3/IAutomationRegistryMaster2_3.sol"; +import {ChainModuleBase} from "../../chains/ChainModuleBase.sol"; +import {IERC20Metadata as IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import {IWrappedNative} from "../../interfaces/v2_3/IWrappedNative.sol"; + +// forge test --match-path src/v0.8/automation/test/v2_3_zksync/ZKSyncAutomationRegistry2_3.t.sol --match-test + +enum Trigger { + CONDITION, + LOG +} + +contract SetUp is BaseTest { + Registry internal registry; + AutomationRegistryBase2_3.OnchainConfig internal config; + bytes internal constant offchainConfigBytes = abi.encode(1234, ZERO_ADDRESS); + + uint256 linkUpkeepID; + uint256 linkUpkeepID2; // 2 upkeeps use the same billing token (LINK) to test migration scenario + uint256 usdUpkeepID18; // 1 upkeep uses ERC20 token with 18 decimals + uint256 usdUpkeepID6; // 1 upkeep uses ERC20 token with 6 decimals + uint256 nativeUpkeepID; + + function setUp() public virtual override { + super.setUp(); + (registry, ) = deployAndConfigureZKSyncRegistryAndRegistrar(AutoBase.PayoutMode.ON_CHAIN); + config = registry.getConfig(); + + vm.startPrank(OWNER); + linkToken.approve(address(registry), type(uint256).max); + usdToken6.approve(address(registry), type(uint256).max); + usdToken18.approve(address(registry), type(uint256).max); + weth.approve(address(registry), type(uint256).max); + vm.startPrank(UPKEEP_ADMIN); + linkToken.approve(address(registry), type(uint256).max); + usdToken6.approve(address(registry), type(uint256).max); + usdToken18.approve(address(registry), type(uint256).max); + weth.approve(address(registry), type(uint256).max); + vm.startPrank(STRANGER); + linkToken.approve(address(registry), type(uint256).max); + usdToken6.approve(address(registry), type(uint256).max); + usdToken18.approve(address(registry), type(uint256).max); + weth.approve(address(registry), type(uint256).max); + vm.stopPrank(); + + linkUpkeepID = registry.registerUpkeep( + address(TARGET1), + config.maxPerformGas, + UPKEEP_ADMIN, + uint8(Trigger.CONDITION), + address(linkToken), + "", + "", + "" + ); + + linkUpkeepID2 = registry.registerUpkeep( + address(TARGET1), + config.maxPerformGas, + UPKEEP_ADMIN, + uint8(Trigger.CONDITION), + address(linkToken), + "", + "", + "" + ); + + usdUpkeepID18 = registry.registerUpkeep( + address(TARGET1), + config.maxPerformGas, + UPKEEP_ADMIN, + uint8(Trigger.CONDITION), + address(usdToken18), + "", + "", + "" + ); + + usdUpkeepID6 = registry.registerUpkeep( + address(TARGET1), + config.maxPerformGas, + UPKEEP_ADMIN, + uint8(Trigger.CONDITION), + address(usdToken6), + "", + "", + "" + ); + + nativeUpkeepID = registry.registerUpkeep( + address(TARGET1), + config.maxPerformGas, + UPKEEP_ADMIN, + uint8(Trigger.CONDITION), + address(weth), + "", + "", + "" + ); + + vm.startPrank(OWNER); + registry.addFunds(linkUpkeepID, registry.getMinBalanceForUpkeep(linkUpkeepID)); + registry.addFunds(linkUpkeepID2, registry.getMinBalanceForUpkeep(linkUpkeepID2)); + registry.addFunds(usdUpkeepID18, registry.getMinBalanceForUpkeep(usdUpkeepID18)); + registry.addFunds(usdUpkeepID6, registry.getMinBalanceForUpkeep(usdUpkeepID6)); + registry.addFunds(nativeUpkeepID, registry.getMinBalanceForUpkeep(nativeUpkeepID)); + vm.stopPrank(); + } +} + +contract LatestConfigDetails is SetUp { + function testGet() public { + (uint32 configCount, uint32 blockNumber, bytes32 configDigest) = registry.latestConfigDetails(); + assertEq(configCount, 1); + assertTrue(blockNumber > 0); + assertNotEq(configDigest, ""); + } +} + +contract CheckUpkeep is SetUp { + function testPreventExecutionOnCheckUpkeep() public { + uint256 id = 1; + bytes memory triggerData = abi.encodePacked("trigger_data"); + + // The tx.origin is the DEFAULT_SENDER (0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38) of foundry + // Expecting a revert since the tx.origin is not address(0) + vm.expectRevert(abi.encodeWithSelector(Registry.OnlySimulatedBackend.selector)); + registry.checkUpkeep(id, triggerData); + } +} + +contract WithdrawFunds is SetUp { + event FundsWithdrawn(uint256 indexed id, uint256 amount, address to); + + function test_RevertsWhen_CalledByNonAdmin() external { + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + vm.prank(STRANGER); + registry.withdrawFunds(linkUpkeepID, STRANGER); + } + + function test_RevertsWhen_InvalidRecipient() external { + vm.expectRevert(Registry.InvalidRecipient.selector); + vm.prank(UPKEEP_ADMIN); + registry.withdrawFunds(linkUpkeepID, ZERO_ADDRESS); + } + + function test_RevertsWhen_UpkeepNotCanceled() external { + vm.expectRevert(Registry.UpkeepNotCanceled.selector); + vm.prank(UPKEEP_ADMIN); + registry.withdrawFunds(linkUpkeepID, UPKEEP_ADMIN); + } + + function test_Happy_Link() external { + vm.startPrank(UPKEEP_ADMIN); + registry.cancelUpkeep(linkUpkeepID); + vm.roll(100 + block.number); + + uint256 startUpkeepAdminBalance = linkToken.balanceOf(UPKEEP_ADMIN); + uint256 startLinkReserveAmountBalance = registry.getReserveAmount(address(linkToken)); + + uint256 upkeepBalance = registry.getBalance(linkUpkeepID); + vm.expectEmit(); + emit FundsWithdrawn(linkUpkeepID, upkeepBalance, address(UPKEEP_ADMIN)); + registry.withdrawFunds(linkUpkeepID, UPKEEP_ADMIN); + + assertEq(registry.getBalance(linkUpkeepID), 0); + assertEq(linkToken.balanceOf(UPKEEP_ADMIN), startUpkeepAdminBalance + upkeepBalance); + assertEq(registry.getReserveAmount(address(linkToken)), startLinkReserveAmountBalance - upkeepBalance); + } + + function test_Happy_USDToken() external { + vm.startPrank(UPKEEP_ADMIN); + registry.cancelUpkeep(usdUpkeepID6); + vm.roll(100 + block.number); + + uint256 startUpkeepAdminBalance = usdToken6.balanceOf(UPKEEP_ADMIN); + uint256 startUSDToken6ReserveAmountBalance = registry.getReserveAmount(address(usdToken6)); + + uint256 upkeepBalance = registry.getBalance(usdUpkeepID6); + vm.expectEmit(); + emit FundsWithdrawn(usdUpkeepID6, upkeepBalance, address(UPKEEP_ADMIN)); + registry.withdrawFunds(usdUpkeepID6, UPKEEP_ADMIN); + + assertEq(registry.getBalance(usdUpkeepID6), 0); + assertEq(usdToken6.balanceOf(UPKEEP_ADMIN), startUpkeepAdminBalance + upkeepBalance); + assertEq(registry.getReserveAmount(address(usdToken6)), startUSDToken6ReserveAmountBalance - upkeepBalance); + } +} + +contract AddFunds is SetUp { + event FundsAdded(uint256 indexed id, address indexed from, uint96 amount); + + // when msg.value is 0, it uses the ERC20 payment path + function test_HappyWhen_NativeUpkeep_WithMsgValue0() external { + vm.startPrank(OWNER); + uint256 startRegistryBalance = registry.getBalance(nativeUpkeepID); + uint256 startTokenBalance = registry.getBalance(nativeUpkeepID); + registry.addFunds(nativeUpkeepID, 1); + assertEq(registry.getBalance(nativeUpkeepID), startRegistryBalance + 1); + assertEq(weth.balanceOf(address(registry)), startTokenBalance + 1); + assertEq(registry.getAvailableERC20ForPayment(address(weth)), 0); + } + + // when msg.value is not 0, it uses the native payment path + function test_HappyWhen_NativeUpkeep_WithMsgValueNot0() external { + uint256 startRegistryBalance = registry.getBalance(nativeUpkeepID); + uint256 startTokenBalance = registry.getBalance(nativeUpkeepID); + registry.addFunds{value: 1}(nativeUpkeepID, 1000); // parameter amount should be ignored + assertEq(registry.getBalance(nativeUpkeepID), startRegistryBalance + 1); + assertEq(weth.balanceOf(address(registry)), startTokenBalance + 1); + assertEq(registry.getAvailableERC20ForPayment(address(weth)), 0); + } + + // it fails when the billing token is not native, but trying to pay with native + function test_RevertsWhen_NativePaymentDoesntMatchBillingToken() external { + vm.expectRevert(abi.encodeWithSelector(Registry.InvalidToken.selector)); + registry.addFunds{value: 1}(linkUpkeepID, 0); + } + + function test_RevertsWhen_UpkeepDoesNotExist() public { + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.addFunds(randomNumber(), 1); + } + + function test_RevertsWhen_UpkeepIsCanceled() public { + registry.cancelUpkeep(linkUpkeepID); + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.addFunds(linkUpkeepID, 1); + } + + function test_anyoneCanAddFunds() public { + uint256 startAmount = registry.getBalance(linkUpkeepID); + vm.prank(UPKEEP_ADMIN); + registry.addFunds(linkUpkeepID, 1); + assertEq(registry.getBalance(linkUpkeepID), startAmount + 1); + vm.prank(STRANGER); + registry.addFunds(linkUpkeepID, 1); + assertEq(registry.getBalance(linkUpkeepID), startAmount + 2); + } + + function test_movesFundFromCorrectToken() public { + vm.startPrank(UPKEEP_ADMIN); + + uint256 startLINKRegistryBalance = linkToken.balanceOf(address(registry)); + uint256 startUSDRegistryBalance = usdToken18.balanceOf(address(registry)); + uint256 startLinkUpkeepBalance = registry.getBalance(linkUpkeepID); + uint256 startUSDUpkeepBalance = registry.getBalance(usdUpkeepID18); + + registry.addFunds(linkUpkeepID, 1); + assertEq(registry.getBalance(linkUpkeepID), startLinkUpkeepBalance + 1); + assertEq(registry.getBalance(usdUpkeepID18), startUSDRegistryBalance); + assertEq(linkToken.balanceOf(address(registry)), startLINKRegistryBalance + 1); + assertEq(usdToken18.balanceOf(address(registry)), startUSDUpkeepBalance); + + registry.addFunds(usdUpkeepID18, 2); + assertEq(registry.getBalance(linkUpkeepID), startLinkUpkeepBalance + 1); + assertEq(registry.getBalance(usdUpkeepID18), startUSDRegistryBalance + 2); + assertEq(linkToken.balanceOf(address(registry)), startLINKRegistryBalance + 1); + assertEq(usdToken18.balanceOf(address(registry)), startUSDUpkeepBalance + 2); + } + + function test_emitsAnEvent() public { + vm.startPrank(UPKEEP_ADMIN); + vm.expectEmit(); + emit FundsAdded(linkUpkeepID, address(UPKEEP_ADMIN), 100); + registry.addFunds(linkUpkeepID, 100); + } +} + +contract Withdraw is SetUp { + address internal aMockAddress = randomAddress(); + + function testLinkAvailableForPaymentReturnsLinkBalance() public { + uint256 startBalance = linkToken.balanceOf(address(registry)); + int256 startLinkAvailable = registry.linkAvailableForPayment(); + + //simulate a deposit of link to the liquidity pool + _mintLink(address(registry), 1e10); + + //check there's a balance + assertEq(linkToken.balanceOf(address(registry)), startBalance + 1e10); + + //check the link available has increased by the same amount + assertEq(uint256(registry.linkAvailableForPayment()), uint256(startLinkAvailable) + 1e10); + } + + function testWithdrawLinkRevertsBecauseOnlyFinanceAdminAllowed() public { + vm.expectRevert(abi.encodeWithSelector(Registry.OnlyFinanceAdmin.selector)); + registry.withdrawLink(aMockAddress, 1); + } + + function testWithdrawLinkRevertsBecauseOfInsufficientBalance() public { + vm.startPrank(FINANCE_ADMIN); + + // try to withdraw 1 link while there is 0 balance + vm.expectRevert(abi.encodeWithSelector(Registry.InsufficientBalance.selector, 0, 1)); + registry.withdrawLink(aMockAddress, 1); + + vm.stopPrank(); + } + + function testWithdrawLinkRevertsBecauseOfInvalidRecipient() public { + vm.startPrank(FINANCE_ADMIN); + + // try to withdraw 1 link while there is 0 balance + vm.expectRevert(abi.encodeWithSelector(Registry.InvalidRecipient.selector)); + registry.withdrawLink(ZERO_ADDRESS, 1); + + vm.stopPrank(); + } + + function testWithdrawLinkSuccess() public { + //simulate a deposit of link to the liquidity pool + _mintLink(address(registry), 1e10); + uint256 startBalance = linkToken.balanceOf(address(registry)); + + vm.startPrank(FINANCE_ADMIN); + + // try to withdraw 1 link while there is a ton of link available + registry.withdrawLink(aMockAddress, 1); + + vm.stopPrank(); + + assertEq(linkToken.balanceOf(address(aMockAddress)), 1); + assertEq(linkToken.balanceOf(address(registry)), startBalance - 1); + } + + function test_WithdrawERC20Fees_RespectsReserveAmount() public { + assertEq(registry.getBalance(usdUpkeepID18), registry.getReserveAmount(address(usdToken18))); + vm.startPrank(FINANCE_ADMIN); + vm.expectRevert(abi.encodeWithSelector(Registry.InsufficientBalance.selector, 0, 1)); + registry.withdrawERC20Fees(address(usdToken18), FINANCE_ADMIN, 1); + } + + function test_WithdrawERC20Fees_RevertsWhen_AttemptingToWithdrawLINK() public { + _mintLink(address(registry), 1e10); + vm.startPrank(FINANCE_ADMIN); + vm.expectRevert(Registry.InvalidToken.selector); + registry.withdrawERC20Fees(address(linkToken), FINANCE_ADMIN, 1); // should revert + registry.withdrawLink(FINANCE_ADMIN, 1); // but using link withdraw functions succeeds + } + + // default is ON_CHAIN mode + function test_WithdrawERC20Fees_RevertsWhen_LinkAvailableForPaymentIsNegative() public { + _transmit(usdUpkeepID18, registry, bytes4(0)); // adds USD token to finance withdrawable, and gives NOPs a LINK balance + require(registry.linkAvailableForPayment() < 0, "linkAvailableForPayment should be negative"); + require( + registry.getAvailableERC20ForPayment(address(usdToken18)) > 0, + "ERC20AvailableForPayment should be positive" + ); + vm.expectRevert(Registry.InsufficientLinkLiquidity.selector); + vm.prank(FINANCE_ADMIN); + registry.withdrawERC20Fees(address(usdToken18), FINANCE_ADMIN, 1); // should revert + _mintLink(address(registry), uint256(registry.linkAvailableForPayment() * -10)); // top up LINK liquidity pool + vm.prank(FINANCE_ADMIN); + registry.withdrawERC20Fees(address(usdToken18), FINANCE_ADMIN, 1); // now finance can withdraw + } + + function test_WithdrawERC20Fees_InOffChainMode_Happy() public { + // deploy and configure a registry with OFF_CHAIN payout + (Registry registry, ) = deployAndConfigureZKSyncRegistryAndRegistrar(AutoBase.PayoutMode.OFF_CHAIN); + + // register an upkeep and add funds + uint256 id = registry.registerUpkeep(address(TARGET1), 1000000, UPKEEP_ADMIN, 0, address(usdToken18), "", "", ""); + _mintERC20_18Decimals(UPKEEP_ADMIN, 1e20); + vm.startPrank(UPKEEP_ADMIN); + usdToken18.approve(address(registry), 1e20); + registry.addFunds(id, 1e20); + + // manually create a transmit so transmitters earn some rewards + _transmit(id, registry, bytes4(0)); + require(registry.linkAvailableForPayment() < 0, "linkAvailableForPayment should be negative"); + vm.prank(FINANCE_ADMIN); + registry.withdrawERC20Fees(address(usdToken18), aMockAddress, 1); // finance can withdraw + + // recipient should get the funds + assertEq(usdToken18.balanceOf(address(aMockAddress)), 1); + } + + function testWithdrawERC20FeeSuccess() public { + // deposit excess USDToken to the registry (this goes to the "finance withdrawable" pool be default) + uint256 startReserveAmount = registry.getReserveAmount(address(usdToken18)); + uint256 startAmount = usdToken18.balanceOf(address(registry)); + _mintERC20_18Decimals(address(registry), 1e10); + + // depositing shouldn't change reserve amount + assertEq(registry.getReserveAmount(address(usdToken18)), startReserveAmount); + + vm.startPrank(FINANCE_ADMIN); + + // try to withdraw 1 USDToken + registry.withdrawERC20Fees(address(usdToken18), aMockAddress, 1); + + vm.stopPrank(); + + assertEq(usdToken18.balanceOf(address(aMockAddress)), 1); + assertEq(usdToken18.balanceOf(address(registry)), startAmount + 1e10 - 1); + assertEq(registry.getReserveAmount(address(usdToken18)), startReserveAmount); + } +} + +contract SetConfig is SetUp { + event ConfigSet( + uint32 previousConfigBlockNumber, + bytes32 configDigest, + uint64 configCount, + address[] signers, + address[] transmitters, + uint8 f, + bytes onchainConfig, + uint64 offchainConfigVersion, + bytes offchainConfig + ); + + address module = address(new ChainModuleBase()); + + AutomationRegistryBase2_3.OnchainConfig cfg = + AutomationRegistryBase2_3.OnchainConfig({ + checkGasLimit: 5_000_000, + stalenessSeconds: 90_000, + gasCeilingMultiplier: 0, + maxPerformGas: 10_000_000, + maxCheckDataSize: 5_000, + maxPerformDataSize: 5_000, + maxRevertDataSize: 5_000, + fallbackGasPrice: 20_000_000_000, + fallbackLinkPrice: 2_000_000_000, // $20 + fallbackNativePrice: 400_000_000_000, // $4,000 + transcoder: 0xB1e66855FD67f6e85F0f0fA38cd6fBABdf00923c, + registrars: _getRegistrars(), + upkeepPrivilegeManager: PRIVILEGE_MANAGER, + chainModule: module, + reorgProtectionEnabled: true, + financeAdmin: FINANCE_ADMIN + }); + + function testSetConfigSuccess() public { + (uint32 configCount, uint32 blockNumber, ) = registry.latestConfigDetails(); + assertEq(configCount, 1); + + address billingTokenAddress = address(usdToken18); + address[] memory billingTokens = new address[](1); + billingTokens[0] = billingTokenAddress; + + AutomationRegistryBase2_3.BillingConfig[] memory billingConfigs = new AutomationRegistryBase2_3.BillingConfig[](1); + billingConfigs[0] = AutomationRegistryBase2_3.BillingConfig({ + gasFeePPB: 5_000, + flatFeeMilliCents: 20_000, + priceFeed: address(USDTOKEN_USD_FEED), + fallbackPrice: 2_000_000_000, // $20 + minSpend: 100_000, + decimals: 18 + }); + + bytes memory onchainConfigBytes = abi.encode(cfg); + bytes memory onchainConfigBytesWithBilling = abi.encode(cfg, billingTokens, billingConfigs); + + bytes32 configDigest = _configDigestFromConfigData( + block.chainid, + address(registry), + ++configCount, + SIGNERS, + TRANSMITTERS, + F, + onchainConfigBytes, + OFFCHAIN_CONFIG_VERSION, + offchainConfigBytes + ); + + vm.expectEmit(); + emit ConfigSet( + blockNumber, + configDigest, + configCount, + SIGNERS, + TRANSMITTERS, + F, + onchainConfigBytes, + OFFCHAIN_CONFIG_VERSION, + offchainConfigBytes + ); + + registry.setConfig( + SIGNERS, + TRANSMITTERS, + F, + onchainConfigBytesWithBilling, + OFFCHAIN_CONFIG_VERSION, + offchainConfigBytes + ); + + (, , address[] memory signers, address[] memory transmitters, uint8 f) = registry.getState(); + + assertEq(signers, SIGNERS); + assertEq(transmitters, TRANSMITTERS); + assertEq(f, F); + + AutomationRegistryBase2_3.BillingConfig memory config = registry.getBillingTokenConfig(billingTokenAddress); + assertEq(config.gasFeePPB, 5_000); + assertEq(config.flatFeeMilliCents, 20_000); + assertEq(config.priceFeed, address(USDTOKEN_USD_FEED)); + assertEq(config.minSpend, 100_000); + + address[] memory tokens = registry.getBillingTokens(); + assertEq(tokens.length, 1); + } + + function testSetConfigMultipleBillingConfigsSuccess() public { + (uint32 configCount, , ) = registry.latestConfigDetails(); + assertEq(configCount, 1); + + address billingTokenAddress1 = address(linkToken); + address billingTokenAddress2 = address(usdToken18); + address[] memory billingTokens = new address[](2); + billingTokens[0] = billingTokenAddress1; + billingTokens[1] = billingTokenAddress2; + + AutomationRegistryBase2_3.BillingConfig[] memory billingConfigs = new AutomationRegistryBase2_3.BillingConfig[](2); + billingConfigs[0] = AutomationRegistryBase2_3.BillingConfig({ + gasFeePPB: 5_001, + flatFeeMilliCents: 20_001, + priceFeed: address(USDTOKEN_USD_FEED), + fallbackPrice: 100, + minSpend: 100, + decimals: 18 + }); + billingConfigs[1] = AutomationRegistryBase2_3.BillingConfig({ + gasFeePPB: 5_002, + flatFeeMilliCents: 20_002, + priceFeed: address(USDTOKEN_USD_FEED), + fallbackPrice: 200, + minSpend: 200, + decimals: 18 + }); + + bytes memory onchainConfigBytesWithBilling = abi.encode(cfg, billingTokens, billingConfigs); + + registry.setConfig( + SIGNERS, + TRANSMITTERS, + F, + onchainConfigBytesWithBilling, + OFFCHAIN_CONFIG_VERSION, + offchainConfigBytes + ); + + (, , address[] memory signers, address[] memory transmitters, uint8 f) = registry.getState(); + + assertEq(signers, SIGNERS); + assertEq(transmitters, TRANSMITTERS); + assertEq(f, F); + + AutomationRegistryBase2_3.BillingConfig memory config1 = registry.getBillingTokenConfig(billingTokenAddress1); + assertEq(config1.gasFeePPB, 5_001); + assertEq(config1.flatFeeMilliCents, 20_001); + assertEq(config1.priceFeed, address(USDTOKEN_USD_FEED)); + assertEq(config1.fallbackPrice, 100); + assertEq(config1.minSpend, 100); + + AutomationRegistryBase2_3.BillingConfig memory config2 = registry.getBillingTokenConfig(billingTokenAddress2); + assertEq(config2.gasFeePPB, 5_002); + assertEq(config2.flatFeeMilliCents, 20_002); + assertEq(config2.priceFeed, address(USDTOKEN_USD_FEED)); + assertEq(config2.fallbackPrice, 200); + assertEq(config2.minSpend, 200); + + address[] memory tokens = registry.getBillingTokens(); + assertEq(tokens.length, 2); + } + + function testSetConfigTwiceAndLastSetOverwrites() public { + (uint32 configCount, , ) = registry.latestConfigDetails(); + assertEq(configCount, 1); + + // BillingConfig1 + address billingTokenAddress1 = address(usdToken18); + address[] memory billingTokens1 = new address[](1); + billingTokens1[0] = billingTokenAddress1; + + AutomationRegistryBase2_3.BillingConfig[] memory billingConfigs1 = new AutomationRegistryBase2_3.BillingConfig[](1); + billingConfigs1[0] = AutomationRegistryBase2_3.BillingConfig({ + gasFeePPB: 5_001, + flatFeeMilliCents: 20_001, + priceFeed: address(USDTOKEN_USD_FEED), + fallbackPrice: 100, + minSpend: 100, + decimals: 18 + }); + + // the first time uses the default onchain config with 2 registrars + bytes memory onchainConfigBytesWithBilling1 = abi.encode(cfg, billingTokens1, billingConfigs1); + + // set config once + registry.setConfig( + SIGNERS, + TRANSMITTERS, + F, + onchainConfigBytesWithBilling1, + OFFCHAIN_CONFIG_VERSION, + offchainConfigBytes + ); + + (, IAutomationV21PlusCommon.OnchainConfigLegacy memory onchainConfig1, , , ) = registry.getState(); + assertEq(onchainConfig1.registrars.length, 2); + + // BillingConfig2 + address billingTokenAddress2 = address(usdToken18); + address[] memory billingTokens2 = new address[](1); + billingTokens2[0] = billingTokenAddress2; + + AutomationRegistryBase2_3.BillingConfig[] memory billingConfigs2 = new AutomationRegistryBase2_3.BillingConfig[](1); + billingConfigs2[0] = AutomationRegistryBase2_3.BillingConfig({ + gasFeePPB: 5_002, + flatFeeMilliCents: 20_002, + priceFeed: address(USDTOKEN_USD_FEED), + fallbackPrice: 200, + minSpend: 200, + decimals: 18 + }); + + address[] memory newRegistrars = new address[](3); + newRegistrars[0] = address(uint160(uint256(keccak256("newRegistrar1")))); + newRegistrars[1] = address(uint160(uint256(keccak256("newRegistrar2")))); + newRegistrars[2] = address(uint160(uint256(keccak256("newRegistrar3")))); + + // new onchain config with 3 new registrars, all other fields stay the same as the default + AutomationRegistryBase2_3.OnchainConfig memory cfg2 = AutomationRegistryBase2_3.OnchainConfig({ + checkGasLimit: 5_000_000, + stalenessSeconds: 90_000, + gasCeilingMultiplier: 0, + maxPerformGas: 10_000_000, + maxCheckDataSize: 5_000, + maxPerformDataSize: 5_000, + maxRevertDataSize: 5_000, + fallbackGasPrice: 20_000_000_000, + fallbackLinkPrice: 2_000_000_000, // $20 + fallbackNativePrice: 400_000_000_000, // $4,000 + transcoder: 0xB1e66855FD67f6e85F0f0fA38cd6fBABdf00923c, + registrars: newRegistrars, + upkeepPrivilegeManager: PRIVILEGE_MANAGER, + chainModule: module, + reorgProtectionEnabled: true, + financeAdmin: FINANCE_ADMIN + }); + + // the second time uses the new onchain config with 3 new registrars and also new billing tokens/configs + bytes memory onchainConfigBytesWithBilling2 = abi.encode(cfg2, billingTokens2, billingConfigs2); + + // set config twice + registry.setConfig( + SIGNERS, + TRANSMITTERS, + F, + onchainConfigBytesWithBilling2, + OFFCHAIN_CONFIG_VERSION, + offchainConfigBytes + ); + + ( + , + IAutomationV21PlusCommon.OnchainConfigLegacy memory onchainConfig2, + address[] memory signers, + address[] memory transmitters, + uint8 f + ) = registry.getState(); + + assertEq(onchainConfig2.registrars.length, 3); + for (uint256 i = 0; i < newRegistrars.length; i++) { + assertEq(newRegistrars[i], onchainConfig2.registrars[i]); + } + assertEq(signers, SIGNERS); + assertEq(transmitters, TRANSMITTERS); + assertEq(f, F); + + AutomationRegistryBase2_3.BillingConfig memory config2 = registry.getBillingTokenConfig(billingTokenAddress2); + assertEq(config2.gasFeePPB, 5_002); + assertEq(config2.flatFeeMilliCents, 20_002); + assertEq(config2.priceFeed, address(USDTOKEN_USD_FEED)); + assertEq(config2.fallbackPrice, 200); + assertEq(config2.minSpend, 200); + + address[] memory tokens = registry.getBillingTokens(); + assertEq(tokens.length, 1); + } + + function testSetConfigDuplicateBillingConfigFailure() public { + (uint32 configCount, , ) = registry.latestConfigDetails(); + assertEq(configCount, 1); + + address billingTokenAddress1 = address(linkToken); + address billingTokenAddress2 = address(linkToken); + address[] memory billingTokens = new address[](2); + billingTokens[0] = billingTokenAddress1; + billingTokens[1] = billingTokenAddress2; + + AutomationRegistryBase2_3.BillingConfig[] memory billingConfigs = new AutomationRegistryBase2_3.BillingConfig[](2); + billingConfigs[0] = AutomationRegistryBase2_3.BillingConfig({ + gasFeePPB: 5_001, + flatFeeMilliCents: 20_001, + priceFeed: address(USDTOKEN_USD_FEED), + fallbackPrice: 100, + minSpend: 100, + decimals: 18 + }); + billingConfigs[1] = AutomationRegistryBase2_3.BillingConfig({ + gasFeePPB: 5_002, + flatFeeMilliCents: 20_002, + priceFeed: address(USDTOKEN_USD_FEED), + fallbackPrice: 200, + minSpend: 200, + decimals: 18 + }); + + bytes memory onchainConfigBytesWithBilling = abi.encode(cfg, billingTokens, billingConfigs); + + // expect revert because of duplicate tokens + vm.expectRevert(abi.encodeWithSelector(Registry.DuplicateEntry.selector)); + registry.setConfig( + SIGNERS, + TRANSMITTERS, + F, + onchainConfigBytesWithBilling, + OFFCHAIN_CONFIG_VERSION, + offchainConfigBytes + ); + } + + function testSetConfigRevertDueToInvalidToken() public { + address[] memory billingTokens = new address[](1); + billingTokens[0] = address(linkToken); + + AutomationRegistryBase2_3.BillingConfig[] memory billingConfigs = new AutomationRegistryBase2_3.BillingConfig[](1); + billingConfigs[0] = AutomationRegistryBase2_3.BillingConfig({ + gasFeePPB: 5_000, + flatFeeMilliCents: 20_000, + priceFeed: address(USDTOKEN_USD_FEED), + fallbackPrice: 2_000_000_000, // $20 + minSpend: 100_000, + decimals: 18 + }); + + // deploy registry with OFF_CHAIN payout mode + registry = deployZKSyncRegistry(AutoBase.PayoutMode.OFF_CHAIN); + + vm.expectRevert(abi.encodeWithSelector(Registry.InvalidToken.selector)); + registry.setConfigTypeSafe( + SIGNERS, + TRANSMITTERS, + F, + cfg, + OFFCHAIN_CONFIG_VERSION, + offchainConfigBytes, + billingTokens, + billingConfigs + ); + } + + function testSetConfigRevertDueToInvalidDecimals() public { + address[] memory billingTokens = new address[](1); + billingTokens[0] = address(linkToken); + + AutomationRegistryBase2_3.BillingConfig[] memory billingConfigs = new AutomationRegistryBase2_3.BillingConfig[](1); + billingConfigs[0] = AutomationRegistryBase2_3.BillingConfig({ + gasFeePPB: 5_000, + flatFeeMilliCents: 20_000, + priceFeed: address(USDTOKEN_USD_FEED), + fallbackPrice: 2_000_000_000, // $20 + minSpend: 100_000, + decimals: 6 // link token should have 18 decimals + }); + + vm.expectRevert(abi.encodeWithSelector(Registry.InvalidToken.selector)); + registry.setConfigTypeSafe( + SIGNERS, + TRANSMITTERS, + F, + cfg, + OFFCHAIN_CONFIG_VERSION, + offchainConfigBytes, + billingTokens, + billingConfigs + ); + } + + function testSetConfigOnTransmittersAndPayees() public { + registry.setPayees(PAYEES); + AutomationRegistryBase2_3.TransmitterPayeeInfo[] memory transmitterPayeeInfos = registry + .getTransmittersWithPayees(); + assertEq(transmitterPayeeInfos.length, TRANSMITTERS.length); + + for (uint256 i = 0; i < transmitterPayeeInfos.length; i++) { + address transmitterAddress = transmitterPayeeInfos[i].transmitterAddress; + address payeeAddress = transmitterPayeeInfos[i].payeeAddress; + + address expectedTransmitter = TRANSMITTERS[i]; + address expectedPayee = PAYEES[i]; + + assertEq(transmitterAddress, expectedTransmitter); + assertEq(payeeAddress, expectedPayee); + } + } + + function testSetConfigWithNewTransmittersSuccess() public { + registry = deployZKSyncRegistry(AutoBase.PayoutMode.OFF_CHAIN); + + (uint32 configCount, uint32 blockNumber, ) = registry.latestConfigDetails(); + assertEq(configCount, 0); + + address billingTokenAddress = address(usdToken18); + address[] memory billingTokens = new address[](1); + billingTokens[0] = billingTokenAddress; + + AutomationRegistryBase2_3.BillingConfig[] memory billingConfigs = new AutomationRegistryBase2_3.BillingConfig[](1); + billingConfigs[0] = AutomationRegistryBase2_3.BillingConfig({ + gasFeePPB: 5_000, + flatFeeMilliCents: 20_000, + priceFeed: address(USDTOKEN_USD_FEED), + fallbackPrice: 2_000_000_000, // $20 + minSpend: 100_000, + decimals: 18 + }); + + bytes memory onchainConfigBytes = abi.encode(cfg); + + bytes32 configDigest = _configDigestFromConfigData( + block.chainid, + address(registry), + ++configCount, + SIGNERS, + TRANSMITTERS, + F, + onchainConfigBytes, + OFFCHAIN_CONFIG_VERSION, + offchainConfigBytes + ); + + vm.expectEmit(); + emit ConfigSet( + blockNumber, + configDigest, + configCount, + SIGNERS, + TRANSMITTERS, + F, + onchainConfigBytes, + OFFCHAIN_CONFIG_VERSION, + offchainConfigBytes + ); + + registry.setConfigTypeSafe( + SIGNERS, + TRANSMITTERS, + F, + cfg, + OFFCHAIN_CONFIG_VERSION, + offchainConfigBytes, + billingTokens, + billingConfigs + ); + + (, , address[] memory signers, address[] memory transmitters, ) = registry.getState(); + assertEq(signers, SIGNERS); + assertEq(transmitters, TRANSMITTERS); + + (configCount, blockNumber, ) = registry.latestConfigDetails(); + configDigest = _configDigestFromConfigData( + block.chainid, + address(registry), + ++configCount, + SIGNERS, + NEW_TRANSMITTERS, + F, + onchainConfigBytes, + OFFCHAIN_CONFIG_VERSION, + offchainConfigBytes + ); + + vm.expectEmit(); + emit ConfigSet( + blockNumber, + configDigest, + configCount, + SIGNERS, + NEW_TRANSMITTERS, + F, + onchainConfigBytes, + OFFCHAIN_CONFIG_VERSION, + offchainConfigBytes + ); + + registry.setConfigTypeSafe( + SIGNERS, + NEW_TRANSMITTERS, + F, + cfg, + OFFCHAIN_CONFIG_VERSION, + offchainConfigBytes, + billingTokens, + billingConfigs + ); + + (, , signers, transmitters, ) = registry.getState(); + assertEq(signers, SIGNERS); + assertEq(transmitters, NEW_TRANSMITTERS); + } + + function _getRegistrars() private pure returns (address[] memory) { + address[] memory registrars = new address[](2); + registrars[0] = address(uint160(uint256(keccak256("registrar1")))); + registrars[1] = address(uint160(uint256(keccak256("registrar2")))); + return registrars; + } + + function _configDigestFromConfigData( + uint256 chainId, + address contractAddress, + uint64 configCount, + address[] memory signers, + address[] memory transmitters, + uint8 f, + bytes memory onchainConfig, + uint64 offchainConfigVersion, + bytes memory offchainConfig + ) internal pure returns (bytes32) { + uint256 h = uint256( + keccak256( + abi.encode( + chainId, + contractAddress, + configCount, + signers, + transmitters, + f, + onchainConfig, + offchainConfigVersion, + offchainConfig + ) + ) + ); + uint256 prefixMask = type(uint256).max << (256 - 16); // 0xFFFF00..00 + uint256 prefix = 0x0001 << (256 - 16); // 0x000100..00 + return bytes32((prefix & prefixMask) | (h & ~prefixMask)); + } +} + +contract NOPsSettlement is SetUp { + event NOPsSettledOffchain(address[] payees, uint256[] payments); + event FundsWithdrawn(uint256 indexed id, uint256 amount, address to); + event PaymentWithdrawn(address indexed transmitter, uint256 indexed amount, address indexed to, address payee); + + function testSettleNOPsOffchainRevertDueToUnauthorizedCaller() public { + (Registry registry, ) = deployAndConfigureZKSyncRegistryAndRegistrar(AutoBase.PayoutMode.ON_CHAIN); + + vm.expectRevert(abi.encodeWithSelector(Registry.OnlyFinanceAdmin.selector)); + registry.settleNOPsOffchain(); + } + + function testSettleNOPsOffchainRevertDueToOffchainSettlementDisabled() public { + (Registry registry, ) = deployAndConfigureZKSyncRegistryAndRegistrar(AutoBase.PayoutMode.OFF_CHAIN); + + vm.prank(registry.owner()); + registry.disableOffchainPayments(); + + vm.prank(FINANCE_ADMIN); + vm.expectRevert(abi.encodeWithSelector(Registry.MustSettleOnchain.selector)); + registry.settleNOPsOffchain(); + } + + function testSettleNOPsOffchainSuccess() public { + // deploy and configure a registry with OFF_CHAIN payout + (Registry registry, ) = deployAndConfigureZKSyncRegistryAndRegistrar(AutoBase.PayoutMode.OFF_CHAIN); + registry.setPayees(PAYEES); + + uint256[] memory payments = new uint256[](TRANSMITTERS.length); + for (uint256 i = 0; i < TRANSMITTERS.length; i++) { + payments[i] = 0; + } + + vm.startPrank(FINANCE_ADMIN); + vm.expectEmit(); + emit NOPsSettledOffchain(PAYEES, payments); + registry.settleNOPsOffchain(); + } + + // 1. transmitter balance zeroed after settlement, 2. admin can withdraw ERC20, 3. switch to onchain mode, 4. link amount owed to NOPs stays the same + function testSettleNOPsOffchainSuccessWithERC20MultiSteps() public { + // deploy and configure a registry with OFF_CHAIN payout + (Registry registry, ) = deployAndConfigureZKSyncRegistryAndRegistrar(AutoBase.PayoutMode.OFF_CHAIN); + registry.setPayees(PAYEES); + + // register an upkeep and add funds + uint256 id = registry.registerUpkeep(address(TARGET1), 1000000, UPKEEP_ADMIN, 0, address(usdToken18), "", "", ""); + _mintERC20_18Decimals(UPKEEP_ADMIN, 1e20); + vm.startPrank(UPKEEP_ADMIN); + usdToken18.approve(address(registry), 1e20); + registry.addFunds(id, 1e20); + + // manually create a transmit so transmitters earn some rewards + _transmit(id, registry, bytes4(0)); + + // verify transmitters have positive balances + uint256[] memory payments = new uint256[](TRANSMITTERS.length); + for (uint256 i = 0; i < TRANSMITTERS.length; i++) { + (bool active, uint8 index, uint96 balance, uint96 lastCollected, ) = registry.getTransmitterInfo(TRANSMITTERS[i]); + assertTrue(active); + assertEq(i, index); + assertTrue(balance > 0); + assertEq(0, lastCollected); + + payments[i] = balance; + } + + // verify offchain settlement will emit NOPs' balances + vm.startPrank(FINANCE_ADMIN); + vm.expectEmit(); + emit NOPsSettledOffchain(PAYEES, payments); + registry.settleNOPsOffchain(); + + // verify that transmitters balance has been zeroed out + for (uint256 i = 0; i < TRANSMITTERS.length; i++) { + (bool active, uint8 index, uint96 balance, , ) = registry.getTransmitterInfo(TRANSMITTERS[i]); + assertTrue(active); + assertEq(i, index); + assertEq(0, balance); + } + + // after the offchain settlement, the total reserve amount of LINK should be 0 + assertEq(registry.getReserveAmount(address(linkToken)), 0); + // should have some ERC20s in registry after transmit + uint256 erc20ForPayment1 = registry.getAvailableERC20ForPayment(address(usdToken18)); + require(erc20ForPayment1 > 0, "ERC20AvailableForPayment should be positive"); + + vm.startPrank(UPKEEP_ADMIN); + vm.roll(100 + block.number); + // manually create a transmit so transmitters earn some rewards + _transmit(id, registry, bytes4(0)); + + uint256 erc20ForPayment2 = registry.getAvailableERC20ForPayment(address(usdToken18)); + require(erc20ForPayment2 > erc20ForPayment1, "ERC20AvailableForPayment should be greater after another transmit"); + + // finance admin comes to withdraw all available ERC20s + vm.startPrank(FINANCE_ADMIN); + registry.withdrawERC20Fees(address(usdToken18), FINANCE_ADMIN, erc20ForPayment2); + + uint256 erc20ForPayment3 = registry.getAvailableERC20ForPayment(address(usdToken18)); + require(erc20ForPayment3 == 0, "ERC20AvailableForPayment should be 0 now after withdrawal"); + + uint256 reservedLink = registry.getReserveAmount(address(linkToken)); + require(reservedLink > 0, "Reserve amount of LINK should be positive since there was another transmit"); + + // owner comes to disable offchain mode + vm.startPrank(registry.owner()); + registry.disableOffchainPayments(); + + // finance admin comes to withdraw all available ERC20s, should revert bc of insufficient link liquidity + vm.startPrank(FINANCE_ADMIN); + uint256 erc20ForPayment4 = registry.getAvailableERC20ForPayment(address(usdToken18)); + vm.expectRevert(abi.encodeWithSelector(Registry.InsufficientLinkLiquidity.selector)); + registry.withdrawERC20Fees(address(usdToken18), FINANCE_ADMIN, erc20ForPayment4); + + // reserved link amount to NOPs should stay the same after switching to onchain mode + assertEq(registry.getReserveAmount(address(linkToken)), reservedLink); + // available ERC20 for payment should be 0 since finance admin withdrew all already + assertEq(erc20ForPayment4, 0); + } + + function testSettleNOPsOffchainForDeactivatedTransmittersSuccess() public { + // deploy and configure a registry with OFF_CHAIN payout + (Registry registry, Registrar registrar) = deployAndConfigureZKSyncRegistryAndRegistrar( + AutoBase.PayoutMode.OFF_CHAIN + ); + + // register an upkeep and add funds + uint256 id = registry.registerUpkeep(address(TARGET1), 1000000, UPKEEP_ADMIN, 0, address(usdToken18), "", "", ""); + _mintERC20_18Decimals(UPKEEP_ADMIN, 1e20); + vm.startPrank(UPKEEP_ADMIN); + usdToken18.approve(address(registry), 1e20); + registry.addFunds(id, 1e20); + + // manually create a transmit so TRANSMITTERS earn some rewards + _transmit(id, registry, bytes4(0)); + + // TRANSMITTERS have positive balance now + // configure the registry to use NEW_TRANSMITTERS + _configureWithNewTransmitters(registry, registrar); + + _transmit(id, registry, bytes4(0)); + + // verify all transmitters have positive balances + address[] memory expectedPayees = new address[](6); + uint256[] memory expectedPayments = new uint256[](6); + for (uint256 i = 0; i < NEW_TRANSMITTERS.length; i++) { + (bool active, uint8 index, uint96 balance, uint96 lastCollected, address payee) = registry.getTransmitterInfo( + NEW_TRANSMITTERS[i] + ); + assertTrue(active); + assertEq(i, index); + assertTrue(lastCollected > 0); + expectedPayments[i] = balance; + expectedPayees[i] = payee; + } + for (uint256 i = 2; i < TRANSMITTERS.length; i++) { + (bool active, uint8 index, uint96 balance, uint96 lastCollected, address payee) = registry.getTransmitterInfo( + TRANSMITTERS[i] + ); + assertFalse(active); + assertEq(i, index); + assertTrue(balance > 0); + assertTrue(lastCollected > 0); + expectedPayments[2 + i] = balance; + expectedPayees[2 + i] = payee; + } + + // verify offchain settlement will emit NOPs' balances + vm.startPrank(FINANCE_ADMIN); + + // simply expectEmit won't work here because s_deactivatedTransmitters is an enumerable set so the order of these + // deactivated transmitters is not guaranteed. To handle this, we record logs and decode data field manually. + vm.recordLogs(); + registry.settleNOPsOffchain(); + Vm.Log[] memory entries = vm.getRecordedLogs(); + + assertEq(entries.length, 1); + Vm.Log memory l = entries[0]; + assertEq(l.topics[0], keccak256("NOPsSettledOffchain(address[],uint256[])")); + (address[] memory actualPayees, uint256[] memory actualPayments) = abi.decode(l.data, (address[], uint256[])); + assertEq(actualPayees.length, 6); + assertEq(actualPayments.length, 6); + + // first 4 payees and payments are for NEW_TRANSMITTERS and they are ordered. + for (uint256 i = 0; i < NEW_TRANSMITTERS.length; i++) { + assertEq(actualPayees[i], expectedPayees[i]); + assertEq(actualPayments[i], expectedPayments[i]); + } + + // the last 2 payees and payments for TRANSMITTERS[2] and TRANSMITTERS[3] and they are not ordered + assertTrue( + (actualPayments[5] == expectedPayments[5] && + actualPayees[5] == expectedPayees[5] && + actualPayments[4] == expectedPayments[4] && + actualPayees[4] == expectedPayees[4]) || + (actualPayments[5] == expectedPayments[4] && + actualPayees[5] == expectedPayees[4] && + actualPayments[4] == expectedPayments[5] && + actualPayees[4] == expectedPayees[5]) + ); + + // verify that new transmitters balance has been zeroed out + for (uint256 i = 0; i < NEW_TRANSMITTERS.length; i++) { + (bool active, uint8 index, uint96 balance, , ) = registry.getTransmitterInfo(NEW_TRANSMITTERS[i]); + assertTrue(active); + assertEq(i, index); + assertEq(0, balance); + } + // verify that deactivated transmitters (TRANSMITTERS[2] and TRANSMITTERS[3]) balance has been zeroed out + for (uint256 i = 2; i < TRANSMITTERS.length; i++) { + (bool active, uint8 index, uint96 balance, , ) = registry.getTransmitterInfo(TRANSMITTERS[i]); + assertFalse(active); + assertEq(i, index); + assertEq(0, balance); + } + + // after the offchain settlement, the total reserve amount of LINK should be 0 + assertEq(registry.getReserveAmount(address(linkToken)), 0); + } + + function testDisableOffchainPaymentsRevertDueToUnauthorizedCaller() public { + (Registry registry, ) = deployAndConfigureZKSyncRegistryAndRegistrar(AutoBase.PayoutMode.OFF_CHAIN); + + vm.startPrank(FINANCE_ADMIN); + vm.expectRevert(bytes("Only callable by owner")); + registry.disableOffchainPayments(); + } + + function testDisableOffchainPaymentsSuccess() public { + (Registry registry, ) = deployAndConfigureZKSyncRegistryAndRegistrar(AutoBase.PayoutMode.OFF_CHAIN); + + vm.startPrank(registry.owner()); + registry.disableOffchainPayments(); + + assertEq(uint8(AutoBase.PayoutMode.ON_CHAIN), registry.getPayoutMode()); + } + + function testSinglePerformAndNodesCanWithdrawOnchain() public { + // deploy and configure a registry with OFF_CHAIN payout + (Registry registry, ) = deployAndConfigureZKSyncRegistryAndRegistrar(AutoBase.PayoutMode.OFF_CHAIN); + registry.setPayees(PAYEES); + + // register an upkeep and add funds + uint256 id = registry.registerUpkeep(address(TARGET1), 1000000, UPKEEP_ADMIN, 0, address(usdToken18), "", "", ""); + _mintERC20_18Decimals(UPKEEP_ADMIN, 1e20); + vm.startPrank(UPKEEP_ADMIN); + usdToken18.approve(address(registry), 1e20); + registry.addFunds(id, 1e20); + + // manually create a transmit so transmitters earn some rewards + _transmit(id, registry, bytes4(0)); + + // disable offchain payments + _mintLink(address(registry), 1e19); + vm.prank(registry.owner()); + registry.disableOffchainPayments(); + + // payees should be able to withdraw onchain + for (uint256 i = 0; i < TRANSMITTERS.length; i++) { + (, , uint96 balance, , address payee) = registry.getTransmitterInfo(TRANSMITTERS[i]); + vm.prank(payee); + vm.expectEmit(); + emit PaymentWithdrawn(TRANSMITTERS[i], balance, payee, payee); + registry.withdrawPayment(TRANSMITTERS[i], payee); + } + + // allow upkeep admin to withdraw funds + vm.startPrank(UPKEEP_ADMIN); + registry.cancelUpkeep(id); + vm.roll(100 + block.number); + vm.expectEmit(); + // the upkeep spent less than minimum spending limit so upkeep admin can only withdraw upkeep balance - min spend value + emit FundsWithdrawn(id, 9.9e19, UPKEEP_ADMIN); + registry.withdrawFunds(id, UPKEEP_ADMIN); + } + + function testMultiplePerformsAndNodesCanWithdrawOnchain() public { + // deploy and configure a registry with OFF_CHAIN payout + (Registry registry, ) = deployAndConfigureZKSyncRegistryAndRegistrar(AutoBase.PayoutMode.OFF_CHAIN); + registry.setPayees(PAYEES); + + // register an upkeep and add funds + uint256 id = registry.registerUpkeep(address(TARGET1), 1000000, UPKEEP_ADMIN, 0, address(usdToken18), "", "", ""); + _mintERC20_18Decimals(UPKEEP_ADMIN, 1e20); + vm.startPrank(UPKEEP_ADMIN); + usdToken18.approve(address(registry), 1e20); + registry.addFunds(id, 1e20); + + // manually call transmit so transmitters earn some rewards + for (uint256 i = 0; i < 50; i++) { + vm.roll(100 + block.number); + _transmit(id, registry, bytes4(0)); + } + + // disable offchain payments + _mintLink(address(registry), 1e19); + vm.prank(registry.owner()); + registry.disableOffchainPayments(); + + // manually call transmit after offchain payment is disabled + for (uint256 i = 0; i < 50; i++) { + vm.roll(100 + block.number); + _transmit(id, registry, bytes4(0)); + } + + // payees should be able to withdraw onchain + for (uint256 i = 0; i < TRANSMITTERS.length; i++) { + (, , uint96 balance, , address payee) = registry.getTransmitterInfo(TRANSMITTERS[i]); + vm.prank(payee); + vm.expectEmit(); + emit PaymentWithdrawn(TRANSMITTERS[i], balance, payee, payee); + registry.withdrawPayment(TRANSMITTERS[i], payee); + } + + // allow upkeep admin to withdraw funds + vm.startPrank(UPKEEP_ADMIN); + registry.cancelUpkeep(id); + vm.roll(100 + block.number); + uint256 balance = registry.getBalance(id); + vm.expectEmit(); + emit FundsWithdrawn(id, balance, UPKEEP_ADMIN); + registry.withdrawFunds(id, UPKEEP_ADMIN); + } + + function _configureWithNewTransmitters(Registry registry, Registrar registrar) internal { + IERC20[] memory billingTokens = new IERC20[](1); + billingTokens[0] = IERC20(address(usdToken18)); + + uint256[] memory minRegistrationFees = new uint256[](billingTokens.length); + minRegistrationFees[0] = 100e18; // 100 USD + + address[] memory billingTokenAddresses = new address[](billingTokens.length); + for (uint256 i = 0; i < billingTokens.length; i++) { + billingTokenAddresses[i] = address(billingTokens[i]); + } + + AutomationRegistryBase2_3.BillingConfig[] + memory billingTokenConfigs = new AutomationRegistryBase2_3.BillingConfig[](billingTokens.length); + billingTokenConfigs[0] = AutomationRegistryBase2_3.BillingConfig({ + gasFeePPB: 10_000_000, // 15% + flatFeeMilliCents: 2_000, // 2 cents + priceFeed: address(USDTOKEN_USD_FEED), + fallbackPrice: 1e8, // $1 + minSpend: 1e18, // 1 USD + decimals: 18 + }); + + address[] memory registrars = new address[](1); + registrars[0] = address(registrar); + + AutomationRegistryBase2_3.OnchainConfig memory cfg = AutomationRegistryBase2_3.OnchainConfig({ + checkGasLimit: 5_000_000, + stalenessSeconds: 90_000, + gasCeilingMultiplier: 2, + maxPerformGas: 10_000_000, + maxCheckDataSize: 5_000, + maxPerformDataSize: 5_000, + maxRevertDataSize: 5_000, + fallbackGasPrice: 20_000_000_000, + fallbackLinkPrice: 2_000_000_000, // $20 + fallbackNativePrice: 400_000_000_000, // $4,000 + transcoder: 0xB1e66855FD67f6e85F0f0fA38cd6fBABdf00923c, + registrars: registrars, + upkeepPrivilegeManager: PRIVILEGE_MANAGER, + chainModule: address(new ChainModuleBase()), + reorgProtectionEnabled: true, + financeAdmin: FINANCE_ADMIN + }); + + registry.setConfigTypeSafe( + SIGNERS, + NEW_TRANSMITTERS, + F, + cfg, + OFFCHAIN_CONFIG_VERSION, + "", + billingTokenAddresses, + billingTokenConfigs + ); + + registry.setPayees(NEW_PAYEES); + } +} + +contract WithdrawPayment is SetUp { + function testWithdrawPaymentRevertDueToOffchainPayoutMode() public { + registry = deployZKSyncRegistry(AutoBase.PayoutMode.OFF_CHAIN); + vm.expectRevert(abi.encodeWithSelector(Registry.MustSettleOffchain.selector)); + vm.prank(TRANSMITTERS[0]); + registry.withdrawPayment(TRANSMITTERS[0], TRANSMITTERS[0]); + } +} + +contract RegisterUpkeep is SetUp { + function test_RevertsWhen_Paused() public { + registry.pause(); + vm.expectRevert(Registry.RegistryPaused.selector); + registry.registerUpkeep( + address(TARGET1), + config.maxPerformGas, + UPKEEP_ADMIN, + uint8(Trigger.CONDITION), + address(linkToken), + "", + "", + "" + ); + } + + function test_RevertsWhen_TargetIsNotAContract() public { + vm.expectRevert(Registry.NotAContract.selector); + registry.registerUpkeep( + randomAddress(), + config.maxPerformGas, + UPKEEP_ADMIN, + uint8(Trigger.CONDITION), + address(linkToken), + "", + "", + "" + ); + } + + function test_RevertsWhen_CalledByNonOwner() public { + vm.prank(STRANGER); + vm.expectRevert(Registry.OnlyCallableByOwnerOrRegistrar.selector); + registry.registerUpkeep( + address(TARGET1), + config.maxPerformGas, + UPKEEP_ADMIN, + uint8(Trigger.CONDITION), + address(linkToken), + "", + "", + "" + ); + } + + function test_RevertsWhen_ExecuteGasIsTooLow() public { + vm.expectRevert(Registry.GasLimitOutsideRange.selector); + registry.registerUpkeep( + address(TARGET1), + 2299, // 1 less than min + UPKEEP_ADMIN, + uint8(Trigger.CONDITION), + address(linkToken), + "", + "", + "" + ); + } + + function test_RevertsWhen_ExecuteGasIsTooHigh() public { + vm.expectRevert(Registry.GasLimitOutsideRange.selector); + registry.registerUpkeep( + address(TARGET1), + config.maxPerformGas + 1, + UPKEEP_ADMIN, + uint8(Trigger.CONDITION), + address(linkToken), + "", + "", + "" + ); + } + + function test_RevertsWhen_TheBillingTokenIsNotConfigured() public { + vm.expectRevert(Registry.InvalidToken.selector); + registry.registerUpkeep( + address(TARGET1), + config.maxPerformGas, + UPKEEP_ADMIN, + uint8(Trigger.CONDITION), + randomAddress(), + "", + "", + "" + ); + } + + function test_RevertsWhen_CheckDataIsTooLarge() public { + vm.expectRevert(Registry.CheckDataExceedsLimit.selector); + registry.registerUpkeep( + address(TARGET1), + config.maxPerformGas, + UPKEEP_ADMIN, + uint8(Trigger.CONDITION), + address(linkToken), + randomBytes(config.maxCheckDataSize + 1), + "", + "" + ); + } + + function test_Happy() public { + bytes memory checkData = randomBytes(config.maxCheckDataSize); + bytes memory trigggerConfig = randomBytes(100); + bytes memory offchainConfig = randomBytes(100); + + uint256 upkeepCount = registry.getNumUpkeeps(); + + uint256 upkeepID = registry.registerUpkeep( + address(TARGET1), + config.maxPerformGas, + UPKEEP_ADMIN, + uint8(Trigger.LOG), + address(linkToken), + checkData, + trigggerConfig, + offchainConfig + ); + + assertEq(registry.getNumUpkeeps(), upkeepCount + 1); + assertEq(registry.getUpkeep(upkeepID).target, address(TARGET1)); + assertEq(registry.getUpkeep(upkeepID).performGas, config.maxPerformGas); + assertEq(registry.getUpkeep(upkeepID).checkData, checkData); + assertEq(registry.getUpkeep(upkeepID).balance, 0); + assertEq(registry.getUpkeep(upkeepID).admin, UPKEEP_ADMIN); + assertEq(registry.getUpkeep(upkeepID).offchainConfig, offchainConfig); + assertEq(registry.getUpkeepTriggerConfig(upkeepID), trigggerConfig); + assertEq(uint8(registry.getTriggerType(upkeepID)), uint8(Trigger.LOG)); + } +} + +contract OnTokenTransfer is SetUp { + function test_RevertsWhen_NotCalledByTheLinkToken() public { + vm.expectRevert(Registry.OnlyCallableByLINKToken.selector); + registry.onTokenTransfer(UPKEEP_ADMIN, 100, abi.encode(linkUpkeepID)); + } + + function test_RevertsWhen_NotCalledWithExactly32Bytes() public { + vm.startPrank(address(linkToken)); + vm.expectRevert(Registry.InvalidDataLength.selector); + registry.onTokenTransfer(UPKEEP_ADMIN, 100, randomBytes(31)); + vm.expectRevert(Registry.InvalidDataLength.selector); + registry.onTokenTransfer(UPKEEP_ADMIN, 100, randomBytes(33)); + } + + function test_RevertsWhen_TheUpkeepIsCancelledOrDNE() public { + vm.startPrank(address(linkToken)); + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.onTokenTransfer(UPKEEP_ADMIN, 100, abi.encode(randomNumber())); + } + + function test_RevertsWhen_TheUpkeepDoesNotUseLINKAsItsBillingToken() public { + vm.startPrank(address(linkToken)); + vm.expectRevert(Registry.InvalidToken.selector); + registry.onTokenTransfer(UPKEEP_ADMIN, 100, abi.encode(usdUpkeepID18)); + } + + function test_Happy() public { + vm.startPrank(address(linkToken)); + uint256 beforeBalance = registry.getBalance(linkUpkeepID); + registry.onTokenTransfer(UPKEEP_ADMIN, 100, abi.encode(linkUpkeepID)); + assertEq(registry.getBalance(linkUpkeepID), beforeBalance + 100); + } +} + +contract GetMinBalanceForUpkeep is SetUp { + function test_accountsForFlatFee_with18Decimals() public { + // set fee to 0 + AutomationRegistryBase2_3.BillingConfig memory usdTokenConfig = registry.getBillingTokenConfig(address(usdToken18)); + usdTokenConfig.flatFeeMilliCents = 0; + _updateBillingTokenConfig(registry, address(usdToken18), usdTokenConfig); + + uint256 minBalanceBefore = registry.getMinBalanceForUpkeep(usdUpkeepID18); + + // set fee to non-zero + usdTokenConfig.flatFeeMilliCents = 100; + _updateBillingTokenConfig(registry, address(usdToken18), usdTokenConfig); + + uint256 minBalanceAfter = registry.getMinBalanceForUpkeep(usdUpkeepID18); + assertEq( + minBalanceAfter, + minBalanceBefore + ((uint256(usdTokenConfig.flatFeeMilliCents) * 1e13) / 10 ** (18 - usdTokenConfig.decimals)) + ); + } + + function test_accountsForFlatFee_with6Decimals() public { + // set fee to 0 + AutomationRegistryBase2_3.BillingConfig memory usdTokenConfig = registry.getBillingTokenConfig(address(usdToken6)); + usdTokenConfig.flatFeeMilliCents = 0; + _updateBillingTokenConfig(registry, address(usdToken6), usdTokenConfig); + + uint256 minBalanceBefore = registry.getMinBalanceForUpkeep(usdUpkeepID6); + + // set fee to non-zero + usdTokenConfig.flatFeeMilliCents = 100; + _updateBillingTokenConfig(registry, address(usdToken6), usdTokenConfig); + + uint256 minBalanceAfter = registry.getMinBalanceForUpkeep(usdUpkeepID6); + assertEq( + minBalanceAfter, + minBalanceBefore + ((uint256(usdTokenConfig.flatFeeMilliCents) * 1e13) / 10 ** (18 - usdTokenConfig.decimals)) + ); + } +} + +contract BillingOverrides is SetUp { + event BillingConfigOverridden(uint256 indexed id, AutomationRegistryBase2_3.BillingOverrides overrides); + event BillingConfigOverrideRemoved(uint256 indexed id); + + function test_RevertsWhen_NotPrivilegeManager() public { + AutomationRegistryBase2_3.BillingOverrides memory billingOverrides = AutomationRegistryBase2_3.BillingOverrides({ + gasFeePPB: 5_000, + flatFeeMilliCents: 20_000 + }); + + vm.expectRevert(Registry.OnlyCallableByUpkeepPrivilegeManager.selector); + registry.setBillingOverrides(linkUpkeepID, billingOverrides); + } + + function test_RevertsWhen_UpkeepCancelled() public { + AutomationRegistryBase2_3.BillingOverrides memory billingOverrides = AutomationRegistryBase2_3.BillingOverrides({ + gasFeePPB: 5_000, + flatFeeMilliCents: 20_000 + }); + + registry.cancelUpkeep(linkUpkeepID); + + vm.startPrank(PRIVILEGE_MANAGER); + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.setBillingOverrides(linkUpkeepID, billingOverrides); + } + + function test_Happy_SetBillingOverrides() public { + AutomationRegistryBase2_3.BillingOverrides memory billingOverrides = AutomationRegistryBase2_3.BillingOverrides({ + gasFeePPB: 5_000, + flatFeeMilliCents: 20_000 + }); + + vm.startPrank(PRIVILEGE_MANAGER); + + vm.expectEmit(); + emit BillingConfigOverridden(linkUpkeepID, billingOverrides); + registry.setBillingOverrides(linkUpkeepID, billingOverrides); + } + + function test_Happy_RemoveBillingOverrides() public { + vm.startPrank(PRIVILEGE_MANAGER); + + vm.expectEmit(); + emit BillingConfigOverrideRemoved(linkUpkeepID); + registry.removeBillingOverrides(linkUpkeepID); + } + + function test_Happy_MaxGasPayment_WithBillingOverrides() public { + uint96 maxPayment1 = registry.getMaxPaymentForGas(linkUpkeepID, 0, 5_000_000, address(linkToken)); + + // Double the two billing values + AutomationRegistryBase2_3.BillingOverrides memory billingOverrides = AutomationRegistryBase2_3.BillingOverrides({ + gasFeePPB: DEFAULT_GAS_FEE_PPB * 2, + flatFeeMilliCents: DEFAULT_FLAT_FEE_MILLI_CENTS * 2 + }); + + vm.startPrank(PRIVILEGE_MANAGER); + registry.setBillingOverrides(linkUpkeepID, billingOverrides); + + // maxPayment2 should be greater than maxPayment1 after the overrides + // The 2 numbers should follow this: maxPayment2 - maxPayment1 == 2 * recepit.premium + // We do not apply the exact equation since we couldn't get the receipt.premium value + uint96 maxPayment2 = registry.getMaxPaymentForGas(linkUpkeepID, 0, 5_000_000, address(linkToken)); + assertGt(maxPayment2, maxPayment1); + } +} + +contract Transmit is SetUp { + function test_transmitRevertWithExtraBytes() external { + bytes32[3] memory exampleReportContext = [ + bytes32(0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef), + bytes32(0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890), + bytes32(0x7890abcdef1234567890abcdef1234567890abcdef1234567890abcdef123456) + ]; + + bytes memory exampleRawReport = hex"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; + + bytes32[] memory exampleRs = new bytes32[](3); + exampleRs[0] = bytes32(0x1234561234561234561234561234561234561234561234561234561234561234); + exampleRs[1] = bytes32(0x1234561234561234561234561234561234561234561234561234561234561234); + exampleRs[2] = bytes32(0x7890789078907890789078907890789078907890789078907890789078907890); + + bytes32[] memory exampleSs = new bytes32[](3); + exampleSs[0] = bytes32(0x1234561234561234561234561234561234561234561234561234561234561234); + exampleSs[1] = bytes32(0x1234561234561234561234561234561234561234561234561234561234561234); + exampleSs[2] = bytes32(0x1234561234561234561234561234561234561234561234561234561234561234); + + bytes32 exampleRawVs = bytes32(0x1234561234561234561234561234561234561234561234561234561234561234); + + bytes memory transmitData = abi.encodeWithSelector( + registry.transmit.selector, + exampleReportContext, + exampleRawReport, + exampleRs, + exampleSs, + exampleRawVs + ); + bytes memory badTransmitData = bytes.concat(transmitData, bytes1(0x00)); // add extra data + vm.startPrank(TRANSMITTERS[0]); + (bool success, bytes memory returnData) = address(registry).call(badTransmitData); // send the bogus transmit + assertFalse(success, "Call did not revert as expected"); + assertEq(returnData, abi.encodePacked(Registry.InvalidDataLength.selector)); + vm.stopPrank(); + } + + function test_handlesMixedBatchOfBillingTokens() external { + uint256[] memory prevUpkeepBalances = new uint256[](3); + prevUpkeepBalances[0] = registry.getBalance(linkUpkeepID); + prevUpkeepBalances[1] = registry.getBalance(usdUpkeepID18); + prevUpkeepBalances[2] = registry.getBalance(nativeUpkeepID); + uint256[] memory prevTokenBalances = new uint256[](3); + prevTokenBalances[0] = linkToken.balanceOf(address(registry)); + prevTokenBalances[1] = usdToken18.balanceOf(address(registry)); + prevTokenBalances[2] = weth.balanceOf(address(registry)); + uint256[] memory prevReserveBalances = new uint256[](3); + prevReserveBalances[0] = registry.getReserveAmount(address(linkToken)); + prevReserveBalances[1] = registry.getReserveAmount(address(usdToken18)); + prevReserveBalances[2] = registry.getReserveAmount(address(weth)); + uint256[] memory upkeepIDs = new uint256[](3); + upkeepIDs[0] = linkUpkeepID; + upkeepIDs[1] = usdUpkeepID18; + upkeepIDs[2] = nativeUpkeepID; + + // withdraw-able by finance team should be 0 + require(registry.getAvailableERC20ForPayment(address(usdToken18)) == 0, "ERC20AvailableForPayment should be 0"); + require(registry.getAvailableERC20ForPayment(address(weth)) == 0, "ERC20AvailableForPayment should be 0"); + + // do the thing + _transmit(upkeepIDs, registry, bytes4(0)); + + // withdraw-able by the finance team should be positive + require( + registry.getAvailableERC20ForPayment(address(usdToken18)) > 0, + "ERC20AvailableForPayment should be positive" + ); + require(registry.getAvailableERC20ForPayment(address(weth)) > 0, "ERC20AvailableForPayment should be positive"); + + // assert upkeep balances have decreased + require(prevUpkeepBalances[0] > registry.getBalance(linkUpkeepID), "link upkeep balance should have decreased"); + require(prevUpkeepBalances[1] > registry.getBalance(usdUpkeepID18), "usd upkeep balance should have decreased"); + require(prevUpkeepBalances[2] > registry.getBalance(nativeUpkeepID), "native upkeep balance should have decreased"); + // assert token balances have not changed + assertEq(prevTokenBalances[0], linkToken.balanceOf(address(registry))); + assertEq(prevTokenBalances[1], usdToken18.balanceOf(address(registry))); + assertEq(prevTokenBalances[2], weth.balanceOf(address(registry))); + // assert reserve amounts have adjusted accordingly + require( + prevReserveBalances[0] < registry.getReserveAmount(address(linkToken)), + "usd reserve amount should have increased" + ); // link reserve amount increases in value equal to the decrease of the other reserve amounts + require( + prevReserveBalances[1] > registry.getReserveAmount(address(usdToken18)), + "usd reserve amount should have decreased" + ); + require( + prevReserveBalances[2] > registry.getReserveAmount(address(weth)), + "native reserve amount should have decreased" + ); + } + + function test_handlesInsufficientBalanceWithUSDToken18() external { + // deploy and configure a registry with ON_CHAIN payout + (Registry registry, ) = deployAndConfigureZKSyncRegistryAndRegistrar(AutoBase.PayoutMode.ON_CHAIN); + + // register an upkeep and add funds + uint256 upkeepID = registry.registerUpkeep( + address(TARGET1), + 1000000, + UPKEEP_ADMIN, + 0, + address(usdToken18), + "", + "", + "" + ); + _mintERC20_18Decimals(UPKEEP_ADMIN, 1e20); + vm.startPrank(UPKEEP_ADMIN); + usdToken18.approve(address(registry), 1e20); + registry.addFunds(upkeepID, 1); // smaller than gasCharge + uint256 balance = registry.getBalance(upkeepID); + + // manually create a transmit + vm.recordLogs(); + _transmit(upkeepID, registry, bytes4(0)); + Vm.Log[] memory entries = vm.getRecordedLogs(); + + assertEq(entries.length, 3); + Vm.Log memory l1 = entries[1]; + assertEq( + l1.topics[0], + keccak256("UpkeepCharged(uint256,(uint96,uint96,uint96,uint96,address,uint96,uint96,uint96))") + ); + ( + uint96 gasChargeInBillingToken, + uint96 premiumInBillingToken, + uint96 gasReimbursementInJuels, + uint96 premiumInJuels, + address billingToken, + uint96 linkUSD, + uint96 nativeUSD, + uint96 billingUSD + ) = abi.decode(l1.data, (uint96, uint96, uint96, uint96, address, uint96, uint96, uint96)); + + assertEq(gasChargeInBillingToken, balance); + assertEq(gasReimbursementInJuels, (balance * billingUSD) / linkUSD); + assertEq(premiumInJuels, 0); + assertEq(premiumInBillingToken, 0); + } + + function test_handlesInsufficientBalanceWithUSDToken6() external { + // deploy and configure a registry with ON_CHAIN payout + (Registry registry, ) = deployAndConfigureZKSyncRegistryAndRegistrar(AutoBase.PayoutMode.ON_CHAIN); + + // register an upkeep and add funds + uint256 upkeepID = registry.registerUpkeep( + address(TARGET1), + 1000000, + UPKEEP_ADMIN, + 0, + address(usdToken6), + "", + "", + "" + ); + vm.prank(OWNER); + usdToken6.mint(UPKEEP_ADMIN, 1e20); + + vm.startPrank(UPKEEP_ADMIN); + usdToken6.approve(address(registry), 1e20); + registry.addFunds(upkeepID, 100); // this is greater than gasCharge but less than (gasCharge + premium) + uint256 balance = registry.getBalance(upkeepID); + + // manually create a transmit + vm.recordLogs(); + _transmit(upkeepID, registry, bytes4(0)); + Vm.Log[] memory entries = vm.getRecordedLogs(); + + assertEq(entries.length, 3); + Vm.Log memory l1 = entries[1]; + assertEq( + l1.topics[0], + keccak256("UpkeepCharged(uint256,(uint96,uint96,uint96,uint96,address,uint96,uint96,uint96))") + ); + ( + uint96 gasChargeInBillingToken, + uint96 premiumInBillingToken, + uint96 gasReimbursementInJuels, + uint96 premiumInJuels, + address billingToken, + uint96 linkUSD, + uint96 nativeUSD, + uint96 billingUSD + ) = abi.decode(l1.data, (uint96, uint96, uint96, uint96, address, uint96, uint96, uint96)); + + assertEq(premiumInJuels, (balance * billingUSD * 1e12) / linkUSD - gasReimbursementInJuels); // scale to 18 decimals + assertEq(premiumInBillingToken, (premiumInJuels * linkUSD + (billingUSD * 1e12 - 1)) / (billingUSD * 1e12)); + } +} + +contract MigrateReceive is SetUp { + event UpkeepMigrated(uint256 indexed id, uint256 remainingBalance, address destination); + event UpkeepReceived(uint256 indexed id, uint256 startingBalance, address importedFrom); + + Registry newRegistry; + uint256[] idsToMigrate; + + function setUp() public override { + super.setUp(); + (newRegistry, ) = deployAndConfigureZKSyncRegistryAndRegistrar(AutoBase.PayoutMode.ON_CHAIN); + idsToMigrate.push(linkUpkeepID); + idsToMigrate.push(linkUpkeepID2); + idsToMigrate.push(usdUpkeepID18); + idsToMigrate.push(nativeUpkeepID); + registry.setPeerRegistryMigrationPermission(address(newRegistry), 1); + newRegistry.setPeerRegistryMigrationPermission(address(registry), 2); + } + + function test_RevertsWhen_PermissionsNotSet() external { + // no permissions + registry.setPeerRegistryMigrationPermission(address(newRegistry), 0); + newRegistry.setPeerRegistryMigrationPermission(address(registry), 0); + vm.expectRevert(Registry.MigrationNotPermitted.selector); + vm.prank(UPKEEP_ADMIN); + registry.migrateUpkeeps(idsToMigrate, address(newRegistry)); + + // only outgoing permissions + registry.setPeerRegistryMigrationPermission(address(newRegistry), 1); + newRegistry.setPeerRegistryMigrationPermission(address(registry), 0); + vm.expectRevert(Registry.MigrationNotPermitted.selector); + vm.prank(UPKEEP_ADMIN); + registry.migrateUpkeeps(idsToMigrate, address(newRegistry)); + + // only incoming permissions + registry.setPeerRegistryMigrationPermission(address(newRegistry), 0); + newRegistry.setPeerRegistryMigrationPermission(address(registry), 2); + vm.expectRevert(Registry.MigrationNotPermitted.selector); + vm.prank(UPKEEP_ADMIN); + registry.migrateUpkeeps(idsToMigrate, address(newRegistry)); + + // permissions opposite direction + registry.setPeerRegistryMigrationPermission(address(newRegistry), 2); + newRegistry.setPeerRegistryMigrationPermission(address(registry), 1); + vm.expectRevert(Registry.MigrationNotPermitted.selector); + vm.prank(UPKEEP_ADMIN); + registry.migrateUpkeeps(idsToMigrate, address(newRegistry)); + } + + function test_RevertsWhen_ReceivingRegistryDoesNotSupportToken() external { + _removeBillingTokenConfig(newRegistry, address(weth)); + vm.expectRevert(Registry.InvalidToken.selector); + vm.prank(UPKEEP_ADMIN); + registry.migrateUpkeeps(idsToMigrate, address(newRegistry)); + idsToMigrate.pop(); // remove native upkeep id + vm.prank(UPKEEP_ADMIN); + registry.migrateUpkeeps(idsToMigrate, address(newRegistry)); // should succeed now + } + + function test_RevertsWhen_CalledByNonAdmin() external { + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + vm.prank(STRANGER); + registry.migrateUpkeeps(idsToMigrate, address(newRegistry)); + } + + function test_Success() external { + vm.startPrank(UPKEEP_ADMIN); + + // add some changes in upkeep data to the mix + registry.pauseUpkeep(usdUpkeepID18); + registry.setUpkeepTriggerConfig(linkUpkeepID, randomBytes(100)); + registry.setUpkeepCheckData(nativeUpkeepID, randomBytes(25)); + + // record previous state + uint256[] memory prevUpkeepBalances = new uint256[](4); + prevUpkeepBalances[0] = registry.getBalance(linkUpkeepID); + prevUpkeepBalances[1] = registry.getBalance(linkUpkeepID2); + prevUpkeepBalances[2] = registry.getBalance(usdUpkeepID18); + prevUpkeepBalances[3] = registry.getBalance(nativeUpkeepID); + uint256[] memory prevReserveBalances = new uint256[](3); + prevReserveBalances[0] = registry.getReserveAmount(address(linkToken)); + prevReserveBalances[1] = registry.getReserveAmount(address(usdToken18)); + prevReserveBalances[2] = registry.getReserveAmount(address(weth)); + uint256[] memory prevTokenBalances = new uint256[](3); + prevTokenBalances[0] = linkToken.balanceOf(address(registry)); + prevTokenBalances[1] = usdToken18.balanceOf(address(registry)); + prevTokenBalances[2] = weth.balanceOf(address(registry)); + bytes[] memory prevUpkeepData = new bytes[](4); + prevUpkeepData[0] = abi.encode(registry.getUpkeep(linkUpkeepID)); + prevUpkeepData[1] = abi.encode(registry.getUpkeep(linkUpkeepID2)); + prevUpkeepData[2] = abi.encode(registry.getUpkeep(usdUpkeepID18)); + prevUpkeepData[3] = abi.encode(registry.getUpkeep(nativeUpkeepID)); + bytes[] memory prevUpkeepTriggerData = new bytes[](4); + prevUpkeepTriggerData[0] = registry.getUpkeepTriggerConfig(linkUpkeepID); + prevUpkeepTriggerData[1] = registry.getUpkeepTriggerConfig(linkUpkeepID2); + prevUpkeepTriggerData[2] = registry.getUpkeepTriggerConfig(usdUpkeepID18); + prevUpkeepTriggerData[3] = registry.getUpkeepTriggerConfig(nativeUpkeepID); + + // event expectations + vm.expectEmit(address(registry)); + emit UpkeepMigrated(linkUpkeepID, prevUpkeepBalances[0], address(newRegistry)); + vm.expectEmit(address(registry)); + emit UpkeepMigrated(linkUpkeepID2, prevUpkeepBalances[1], address(newRegistry)); + vm.expectEmit(address(registry)); + emit UpkeepMigrated(usdUpkeepID18, prevUpkeepBalances[2], address(newRegistry)); + vm.expectEmit(address(registry)); + emit UpkeepMigrated(nativeUpkeepID, prevUpkeepBalances[3], address(newRegistry)); + vm.expectEmit(address(newRegistry)); + emit UpkeepReceived(linkUpkeepID, prevUpkeepBalances[0], address(registry)); + vm.expectEmit(address(newRegistry)); + emit UpkeepReceived(linkUpkeepID2, prevUpkeepBalances[1], address(registry)); + vm.expectEmit(address(newRegistry)); + emit UpkeepReceived(usdUpkeepID18, prevUpkeepBalances[2], address(registry)); + vm.expectEmit(address(newRegistry)); + emit UpkeepReceived(nativeUpkeepID, prevUpkeepBalances[3], address(registry)); + + // do the thing + registry.migrateUpkeeps(idsToMigrate, address(newRegistry)); + + // assert upkeep balances have been migrated + assertEq(registry.getBalance(linkUpkeepID), 0); + assertEq(registry.getBalance(linkUpkeepID2), 0); + assertEq(registry.getBalance(usdUpkeepID18), 0); + assertEq(registry.getBalance(nativeUpkeepID), 0); + assertEq(newRegistry.getBalance(linkUpkeepID), prevUpkeepBalances[0]); + assertEq(newRegistry.getBalance(linkUpkeepID2), prevUpkeepBalances[1]); + assertEq(newRegistry.getBalance(usdUpkeepID18), prevUpkeepBalances[2]); + assertEq(newRegistry.getBalance(nativeUpkeepID), prevUpkeepBalances[3]); + + // assert reserve balances have been adjusted + assertEq( + newRegistry.getReserveAmount(address(linkToken)), + newRegistry.getBalance(linkUpkeepID) + newRegistry.getBalance(linkUpkeepID2) + ); + assertEq(newRegistry.getReserveAmount(address(usdToken18)), newRegistry.getBalance(usdUpkeepID18)); + assertEq(newRegistry.getReserveAmount(address(weth)), newRegistry.getBalance(nativeUpkeepID)); + assertEq( + newRegistry.getReserveAmount(address(linkToken)), + prevReserveBalances[0] - registry.getReserveAmount(address(linkToken)) + ); + assertEq( + newRegistry.getReserveAmount(address(usdToken18)), + prevReserveBalances[1] - registry.getReserveAmount(address(usdToken18)) + ); + assertEq( + newRegistry.getReserveAmount(address(weth)), + prevReserveBalances[2] - registry.getReserveAmount(address(weth)) + ); + + // assert token have been transferred + assertEq( + linkToken.balanceOf(address(newRegistry)), + newRegistry.getBalance(linkUpkeepID) + newRegistry.getBalance(linkUpkeepID2) + ); + assertEq(usdToken18.balanceOf(address(newRegistry)), newRegistry.getBalance(usdUpkeepID18)); + assertEq(weth.balanceOf(address(newRegistry)), newRegistry.getBalance(nativeUpkeepID)); + assertEq(linkToken.balanceOf(address(registry)), prevTokenBalances[0] - linkToken.balanceOf(address(newRegistry))); + assertEq( + usdToken18.balanceOf(address(registry)), + prevTokenBalances[1] - usdToken18.balanceOf(address(newRegistry)) + ); + assertEq(weth.balanceOf(address(registry)), prevTokenBalances[2] - weth.balanceOf(address(newRegistry))); + + // assert upkeep data matches + assertEq(prevUpkeepData[0], abi.encode(newRegistry.getUpkeep(linkUpkeepID))); + assertEq(prevUpkeepData[1], abi.encode(newRegistry.getUpkeep(linkUpkeepID2))); + assertEq(prevUpkeepData[2], abi.encode(newRegistry.getUpkeep(usdUpkeepID18))); + assertEq(prevUpkeepData[3], abi.encode(newRegistry.getUpkeep(nativeUpkeepID))); + assertEq(prevUpkeepTriggerData[0], newRegistry.getUpkeepTriggerConfig(linkUpkeepID)); + assertEq(prevUpkeepTriggerData[1], newRegistry.getUpkeepTriggerConfig(linkUpkeepID2)); + assertEq(prevUpkeepTriggerData[2], newRegistry.getUpkeepTriggerConfig(usdUpkeepID18)); + assertEq(prevUpkeepTriggerData[3], newRegistry.getUpkeepTriggerConfig(nativeUpkeepID)); + + vm.stopPrank(); + } +} + +contract Pause is SetUp { + function test_RevertsWhen_CalledByNonOwner() external { + vm.expectRevert(bytes("Only callable by owner")); + vm.prank(STRANGER); + registry.pause(); + } + + function test_CalledByOwner_success() external { + vm.startPrank(registry.owner()); + registry.pause(); + + (IAutomationV21PlusCommon.StateLegacy memory state, , , , ) = registry.getState(); + assertTrue(state.paused); + } + + function test_revertsWhen_registerUpkeepInPausedRegistry() external { + vm.startPrank(registry.owner()); + registry.pause(); + + vm.expectRevert(Registry.RegistryPaused.selector); + uint256 id = registry.registerUpkeep( + address(TARGET1), + config.maxPerformGas, + UPKEEP_ADMIN, + uint8(Trigger.CONDITION), + address(linkToken), + "", + "", + "" + ); + } + + function test_revertsWhen_transmitInPausedRegistry() external { + vm.startPrank(registry.owner()); + registry.pause(); + + _transmit(usdUpkeepID18, registry, Registry.RegistryPaused.selector); + } +} + +contract Unpause is SetUp { + function test_RevertsWhen_CalledByNonOwner() external { + vm.startPrank(registry.owner()); + registry.pause(); + + vm.expectRevert(bytes("Only callable by owner")); + vm.startPrank(STRANGER); + registry.unpause(); + } + + function test_CalledByOwner_success() external { + vm.startPrank(registry.owner()); + registry.pause(); + (IAutomationV21PlusCommon.StateLegacy memory state1, , , , ) = registry.getState(); + assertTrue(state1.paused); + + registry.unpause(); + (IAutomationV21PlusCommon.StateLegacy memory state2, , , , ) = registry.getState(); + assertFalse(state2.paused); + } +} + +contract CancelUpkeep is SetUp { + event UpkeepCanceled(uint256 indexed id, uint64 indexed atBlockHeight); + + function test_RevertsWhen_IdIsInvalid_CalledByAdmin() external { + vm.startPrank(UPKEEP_ADMIN); + vm.expectRevert(Registry.CannotCancel.selector); + registry.cancelUpkeep(1111111); + } + + function test_RevertsWhen_IdIsInvalid_CalledByOwner() external { + vm.startPrank(registry.owner()); + vm.expectRevert(Registry.CannotCancel.selector); + registry.cancelUpkeep(1111111); + } + + function test_RevertsWhen_NotCalledByOwnerOrAdmin() external { + vm.startPrank(STRANGER); + vm.expectRevert(Registry.OnlyCallableByOwnerOrAdmin.selector); + registry.cancelUpkeep(linkUpkeepID); + } + + function test_RevertsWhen_UpkeepAlreadyCanceledByAdmin_CalledByOwner() external { + uint256 bn = block.number; + vm.startPrank(UPKEEP_ADMIN); + registry.cancelUpkeep(linkUpkeepID); + + vm.startPrank(registry.owner()); + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.cancelUpkeep(linkUpkeepID); + } + + function test_RevertsWhen_UpkeepAlreadyCanceledByOwner_CalledByAdmin() external { + uint256 bn = block.number; + vm.startPrank(registry.owner()); + registry.cancelUpkeep(linkUpkeepID); + + vm.startPrank(UPKEEP_ADMIN); + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.cancelUpkeep(linkUpkeepID); + } + + function test_RevertsWhen_UpkeepAlreadyCanceledByAdmin_CalledByAdmin() external { + uint256 bn = block.number; + vm.startPrank(UPKEEP_ADMIN); + registry.cancelUpkeep(linkUpkeepID); + + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.cancelUpkeep(linkUpkeepID); + } + + function test_RevertsWhen_UpkeepAlreadyCanceledByOwner_CalledByOwner() external { + uint256 bn = block.number; + vm.startPrank(registry.owner()); + registry.cancelUpkeep(linkUpkeepID); + + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.cancelUpkeep(linkUpkeepID); + } + + function test_CancelUpkeep_SetMaxValidBlockNumber_CalledByAdmin() external { + uint256 bn = block.number; + vm.startPrank(UPKEEP_ADMIN); + registry.cancelUpkeep(linkUpkeepID); + + uint256 maxValidBlocknumber = uint256(registry.getUpkeep(linkUpkeepID).maxValidBlocknumber); + + // 50 is the cancellation delay + assertEq(bn + 50, maxValidBlocknumber); + } + + function test_CancelUpkeep_SetMaxValidBlockNumber_CalledByOwner() external { + uint256 bn = block.number; + vm.startPrank(registry.owner()); + registry.cancelUpkeep(linkUpkeepID); + + uint256 maxValidBlocknumber = uint256(registry.getUpkeep(linkUpkeepID).maxValidBlocknumber); + + // cancellation by registry owner is immediate and no cancellation delay is applied + assertEq(bn, maxValidBlocknumber); + } + + function test_CancelUpkeep_EmitEvent_CalledByAdmin() external { + uint256 bn = block.number; + vm.startPrank(UPKEEP_ADMIN); + + vm.expectEmit(); + emit UpkeepCanceled(linkUpkeepID, uint64(bn + 50)); + registry.cancelUpkeep(linkUpkeepID); + } + + function test_CancelUpkeep_EmitEvent_CalledByOwner() external { + uint256 bn = block.number; + vm.startPrank(registry.owner()); + + vm.expectEmit(); + emit UpkeepCanceled(linkUpkeepID, uint64(bn)); + registry.cancelUpkeep(linkUpkeepID); + } +} + +contract SetPeerRegistryMigrationPermission is SetUp { + function test_SetPeerRegistryMigrationPermission_CalledByOwner() external { + address peer = randomAddress(); + vm.startPrank(registry.owner()); + + uint8 permission = registry.getPeerRegistryMigrationPermission(peer); + assertEq(0, permission); + + registry.setPeerRegistryMigrationPermission(peer, 1); + permission = registry.getPeerRegistryMigrationPermission(peer); + assertEq(1, permission); + + registry.setPeerRegistryMigrationPermission(peer, 2); + permission = registry.getPeerRegistryMigrationPermission(peer); + assertEq(2, permission); + + registry.setPeerRegistryMigrationPermission(peer, 0); + permission = registry.getPeerRegistryMigrationPermission(peer); + assertEq(0, permission); + } + + function test_RevertsWhen_InvalidPermission_CalledByOwner() external { + address peer = randomAddress(); + vm.startPrank(registry.owner()); + + vm.expectRevert(); + registry.setPeerRegistryMigrationPermission(peer, 100); + } + + function test_RevertsWhen_CalledByNonOwner() external { + address peer = randomAddress(); + vm.startPrank(STRANGER); + + vm.expectRevert(bytes("Only callable by owner")); + registry.setPeerRegistryMigrationPermission(peer, 1); + } +} + +contract SetUpkeepPrivilegeConfig is SetUp { + function test_RevertsWhen_CalledByNonManager() external { + vm.startPrank(STRANGER); + + vm.expectRevert(Registry.OnlyCallableByUpkeepPrivilegeManager.selector); + registry.setUpkeepPrivilegeConfig(linkUpkeepID, hex"1233"); + } + + function test_UpkeepHasEmptyConfig() external { + bytes memory cfg = registry.getUpkeepPrivilegeConfig(linkUpkeepID); + assertEq(cfg, bytes("")); + } + + function test_SetUpkeepPrivilegeConfig_CalledByManager() external { + vm.startPrank(PRIVILEGE_MANAGER); + + registry.setUpkeepPrivilegeConfig(linkUpkeepID, hex"1233"); + + bytes memory cfg = registry.getUpkeepPrivilegeConfig(linkUpkeepID); + assertEq(cfg, hex"1233"); + } +} + +contract SetAdminPrivilegeConfig is SetUp { + function test_RevertsWhen_CalledByNonManager() external { + vm.startPrank(STRANGER); + + vm.expectRevert(Registry.OnlyCallableByUpkeepPrivilegeManager.selector); + registry.setAdminPrivilegeConfig(randomAddress(), hex"1233"); + } + + function test_UpkeepHasEmptyConfig() external { + bytes memory cfg = registry.getAdminPrivilegeConfig(randomAddress()); + assertEq(cfg, bytes("")); + } + + function test_SetAdminPrivilegeConfig_CalledByManager() external { + vm.startPrank(PRIVILEGE_MANAGER); + address admin = randomAddress(); + + registry.setAdminPrivilegeConfig(admin, hex"1233"); + + bytes memory cfg = registry.getAdminPrivilegeConfig(admin); + assertEq(cfg, hex"1233"); + } +} + +contract TransferUpkeepAdmin is SetUp { + event UpkeepAdminTransferRequested(uint256 indexed id, address indexed from, address indexed to); + + function test_RevertsWhen_NotCalledByAdmin() external { + vm.startPrank(STRANGER); + + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + registry.transferUpkeepAdmin(linkUpkeepID, randomAddress()); + } + + function test_RevertsWhen_TransferToSelf() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectRevert(Registry.ValueNotChanged.selector); + registry.transferUpkeepAdmin(linkUpkeepID, UPKEEP_ADMIN); + } + + function test_RevertsWhen_UpkeepCanceled() external { + vm.startPrank(UPKEEP_ADMIN); + + registry.cancelUpkeep(linkUpkeepID); + + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.transferUpkeepAdmin(linkUpkeepID, randomAddress()); + } + + function test_DoesNotChangeUpkeepAdmin() external { + vm.startPrank(UPKEEP_ADMIN); + registry.transferUpkeepAdmin(linkUpkeepID, randomAddress()); + + assertEq(registry.getUpkeep(linkUpkeepID).admin, UPKEEP_ADMIN); + } + + function test_EmitEvent_CalledByAdmin() external { + vm.startPrank(UPKEEP_ADMIN); + address newAdmin = randomAddress(); + + vm.expectEmit(); + emit UpkeepAdminTransferRequested(linkUpkeepID, UPKEEP_ADMIN, newAdmin); + registry.transferUpkeepAdmin(linkUpkeepID, newAdmin); + + // transferring to the same propose admin won't yield another event + vm.recordLogs(); + registry.transferUpkeepAdmin(linkUpkeepID, newAdmin); + Vm.Log[] memory entries = vm.getRecordedLogs(); + assertEq(0, entries.length); + } + + function test_CancelTransfer_ByTransferToEmptyAddress() external { + vm.startPrank(UPKEEP_ADMIN); + address newAdmin = randomAddress(); + + vm.expectEmit(); + emit UpkeepAdminTransferRequested(linkUpkeepID, UPKEEP_ADMIN, newAdmin); + registry.transferUpkeepAdmin(linkUpkeepID, newAdmin); + + vm.expectEmit(); + emit UpkeepAdminTransferRequested(linkUpkeepID, UPKEEP_ADMIN, address(0)); + registry.transferUpkeepAdmin(linkUpkeepID, address(0)); + } +} + +contract AcceptUpkeepAdmin is SetUp { + event UpkeepAdminTransferred(uint256 indexed id, address indexed from, address indexed to); + + function test_RevertsWhen_NotCalledByProposedAdmin() external { + vm.startPrank(UPKEEP_ADMIN); + address newAdmin = randomAddress(); + registry.transferUpkeepAdmin(linkUpkeepID, newAdmin); + + vm.startPrank(STRANGER); + vm.expectRevert(Registry.OnlyCallableByProposedAdmin.selector); + registry.acceptUpkeepAdmin(linkUpkeepID); + } + + function test_RevertsWhen_UpkeepCanceled() external { + vm.startPrank(UPKEEP_ADMIN); + address newAdmin = randomAddress(); + registry.transferUpkeepAdmin(linkUpkeepID, newAdmin); + + registry.cancelUpkeep(linkUpkeepID); + + vm.startPrank(newAdmin); + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.acceptUpkeepAdmin(linkUpkeepID); + } + + function test_UpkeepAdminChanged() external { + vm.startPrank(UPKEEP_ADMIN); + address newAdmin = randomAddress(); + registry.transferUpkeepAdmin(linkUpkeepID, newAdmin); + + vm.startPrank(newAdmin); + vm.expectEmit(); + emit UpkeepAdminTransferred(linkUpkeepID, UPKEEP_ADMIN, newAdmin); + registry.acceptUpkeepAdmin(linkUpkeepID); + + assertEq(newAdmin, registry.getUpkeep(linkUpkeepID).admin); + } +} + +contract PauseUpkeep is SetUp { + event UpkeepPaused(uint256 indexed id); + + function test_RevertsWhen_NotCalledByUpkeepAdmin() external { + vm.startPrank(STRANGER); + + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + registry.pauseUpkeep(linkUpkeepID); + } + + function test_RevertsWhen_InvalidUpkeepId() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + registry.pauseUpkeep(linkUpkeepID + 1); + } + + function test_RevertsWhen_UpkeepAlreadyCanceled() external { + vm.startPrank(UPKEEP_ADMIN); + registry.cancelUpkeep(linkUpkeepID); + + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.pauseUpkeep(linkUpkeepID); + } + + function test_RevertsWhen_UpkeepAlreadyPaused() external { + vm.startPrank(UPKEEP_ADMIN); + registry.pauseUpkeep(linkUpkeepID); + + vm.expectRevert(Registry.OnlyUnpausedUpkeep.selector); + registry.pauseUpkeep(linkUpkeepID); + } + + function test_EmitEvent_CalledByAdmin() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectEmit(); + emit UpkeepPaused(linkUpkeepID); + registry.pauseUpkeep(linkUpkeepID); + } +} + +contract UnpauseUpkeep is SetUp { + event UpkeepUnpaused(uint256 indexed id); + + function test_RevertsWhen_InvalidUpkeepId() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + registry.unpauseUpkeep(linkUpkeepID + 1); + } + + function test_RevertsWhen_UpkeepIsNotPaused() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectRevert(Registry.OnlyPausedUpkeep.selector); + registry.unpauseUpkeep(linkUpkeepID); + } + + function test_RevertsWhen_UpkeepAlreadyCanceled() external { + vm.startPrank(UPKEEP_ADMIN); + registry.pauseUpkeep(linkUpkeepID); + + registry.cancelUpkeep(linkUpkeepID); + + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.unpauseUpkeep(linkUpkeepID); + } + + function test_RevertsWhen_NotCalledByUpkeepAdmin() external { + vm.startPrank(UPKEEP_ADMIN); + registry.pauseUpkeep(linkUpkeepID); + + vm.startPrank(STRANGER); + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + registry.unpauseUpkeep(linkUpkeepID); + } + + function test_UnpauseUpkeep_CalledByUpkeepAdmin() external { + vm.startPrank(UPKEEP_ADMIN); + registry.pauseUpkeep(linkUpkeepID); + + uint256[] memory ids1 = registry.getActiveUpkeepIDs(0, 0); + + vm.expectEmit(); + emit UpkeepUnpaused(linkUpkeepID); + registry.unpauseUpkeep(linkUpkeepID); + + uint256[] memory ids2 = registry.getActiveUpkeepIDs(0, 0); + assertEq(ids1.length + 1, ids2.length); + } +} + +contract SetUpkeepCheckData is SetUp { + event UpkeepCheckDataSet(uint256 indexed id, bytes newCheckData); + + function test_RevertsWhen_InvalidUpkeepId() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + registry.setUpkeepCheckData(linkUpkeepID + 1, hex"1234"); + } + + function test_RevertsWhen_UpkeepAlreadyCanceled() external { + vm.startPrank(UPKEEP_ADMIN); + registry.cancelUpkeep(linkUpkeepID); + + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.setUpkeepCheckData(linkUpkeepID, hex"1234"); + } + + function test_RevertsWhen_NewCheckDataTooLarge() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectRevert(Registry.CheckDataExceedsLimit.selector); + registry.setUpkeepCheckData(linkUpkeepID, new bytes(10_000)); + } + + function test_RevertsWhen_NotCalledByAdmin() external { + vm.startPrank(STRANGER); + + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + registry.setUpkeepCheckData(linkUpkeepID, hex"1234"); + } + + function test_UpdateOffchainConfig_CalledByAdmin() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectEmit(); + emit UpkeepCheckDataSet(linkUpkeepID, hex"1234"); + registry.setUpkeepCheckData(linkUpkeepID, hex"1234"); + + assertEq(registry.getUpkeep(linkUpkeepID).checkData, hex"1234"); + } + + function test_UpdateOffchainConfigOnPausedUpkeep_CalledByAdmin() external { + vm.startPrank(UPKEEP_ADMIN); + + registry.pauseUpkeep(linkUpkeepID); + + vm.expectEmit(); + emit UpkeepCheckDataSet(linkUpkeepID, hex"1234"); + registry.setUpkeepCheckData(linkUpkeepID, hex"1234"); + + assertTrue(registry.getUpkeep(linkUpkeepID).paused); + assertEq(registry.getUpkeep(linkUpkeepID).checkData, hex"1234"); + } +} + +contract SetUpkeepGasLimit is SetUp { + event UpkeepGasLimitSet(uint256 indexed id, uint96 gasLimit); + + function test_RevertsWhen_InvalidUpkeepId() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + registry.setUpkeepGasLimit(linkUpkeepID + 1, 1230000); + } + + function test_RevertsWhen_UpkeepAlreadyCanceled() external { + vm.startPrank(UPKEEP_ADMIN); + registry.cancelUpkeep(linkUpkeepID); + + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.setUpkeepGasLimit(linkUpkeepID, 1230000); + } + + function test_RevertsWhen_NewGasLimitOutOfRange() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectRevert(Registry.GasLimitOutsideRange.selector); + registry.setUpkeepGasLimit(linkUpkeepID, 300); + + vm.expectRevert(Registry.GasLimitOutsideRange.selector); + registry.setUpkeepGasLimit(linkUpkeepID, 3000000000); + } + + function test_RevertsWhen_NotCalledByAdmin() external { + vm.startPrank(STRANGER); + + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + registry.setUpkeepGasLimit(linkUpkeepID, 1230000); + } + + function test_UpdateGasLimit_CalledByAdmin() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectEmit(); + emit UpkeepGasLimitSet(linkUpkeepID, 1230000); + registry.setUpkeepGasLimit(linkUpkeepID, 1230000); + + assertEq(registry.getUpkeep(linkUpkeepID).performGas, 1230000); + } +} + +contract SetUpkeepOffchainConfig is SetUp { + event UpkeepOffchainConfigSet(uint256 indexed id, bytes offchainConfig); + + function test_RevertsWhen_InvalidUpkeepId() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + registry.setUpkeepOffchainConfig(linkUpkeepID + 1, hex"1233"); + } + + function test_RevertsWhen_UpkeepAlreadyCanceled() external { + vm.startPrank(UPKEEP_ADMIN); + registry.cancelUpkeep(linkUpkeepID); + + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.setUpkeepOffchainConfig(linkUpkeepID, hex"1234"); + } + + function test_RevertsWhen_NotCalledByAdmin() external { + vm.startPrank(STRANGER); + + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + registry.setUpkeepOffchainConfig(linkUpkeepID, hex"1234"); + } + + function test_UpdateOffchainConfig_CalledByAdmin() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectEmit(); + emit UpkeepOffchainConfigSet(linkUpkeepID, hex"1234"); + registry.setUpkeepOffchainConfig(linkUpkeepID, hex"1234"); + + assertEq(registry.getUpkeep(linkUpkeepID).offchainConfig, hex"1234"); + } +} + +contract SetUpkeepTriggerConfig is SetUp { + event UpkeepTriggerConfigSet(uint256 indexed id, bytes triggerConfig); + + function test_RevertsWhen_InvalidUpkeepId() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + registry.setUpkeepTriggerConfig(linkUpkeepID + 1, hex"1233"); + } + + function test_RevertsWhen_UpkeepAlreadyCanceled() external { + vm.startPrank(UPKEEP_ADMIN); + registry.cancelUpkeep(linkUpkeepID); + + vm.expectRevert(Registry.UpkeepCancelled.selector); + registry.setUpkeepTriggerConfig(linkUpkeepID, hex"1234"); + } + + function test_RevertsWhen_NotCalledByAdmin() external { + vm.startPrank(STRANGER); + + vm.expectRevert(Registry.OnlyCallableByAdmin.selector); + registry.setUpkeepTriggerConfig(linkUpkeepID, hex"1234"); + } + + function test_UpdateTriggerConfig_CalledByAdmin() external { + vm.startPrank(UPKEEP_ADMIN); + + vm.expectEmit(); + emit UpkeepTriggerConfigSet(linkUpkeepID, hex"1234"); + registry.setUpkeepTriggerConfig(linkUpkeepID, hex"1234"); + + assertEq(registry.getUpkeepTriggerConfig(linkUpkeepID), hex"1234"); + } +} + +contract TransferPayeeship is SetUp { + event PayeeshipTransferRequested(address indexed transmitter, address indexed from, address indexed to); + + function test_RevertsWhen_NotCalledByPayee() external { + vm.startPrank(STRANGER); + + vm.expectRevert(Registry.OnlyCallableByPayee.selector); + registry.transferPayeeship(TRANSMITTERS[0], randomAddress()); + } + + function test_RevertsWhen_TransferToSelf() external { + registry.setPayees(PAYEES); + vm.startPrank(PAYEES[0]); + + vm.expectRevert(Registry.ValueNotChanged.selector); + registry.transferPayeeship(TRANSMITTERS[0], PAYEES[0]); + } + + function test_Transfer_DoesNotChangePayee() external { + registry.setPayees(PAYEES); + + vm.startPrank(PAYEES[0]); + + registry.transferPayeeship(TRANSMITTERS[0], randomAddress()); + + (, , , , address payee) = registry.getTransmitterInfo(TRANSMITTERS[0]); + assertEq(PAYEES[0], payee); + } + + function test_EmitEvent_CalledByPayee() external { + registry.setPayees(PAYEES); + + vm.startPrank(PAYEES[0]); + address newPayee = randomAddress(); + + vm.expectEmit(); + emit PayeeshipTransferRequested(TRANSMITTERS[0], PAYEES[0], newPayee); + registry.transferPayeeship(TRANSMITTERS[0], newPayee); + + // transferring to the same propose payee won't yield another event + vm.recordLogs(); + registry.transferPayeeship(TRANSMITTERS[0], newPayee); + Vm.Log[] memory entries = vm.getRecordedLogs(); + assertEq(0, entries.length); + } +} + +contract AcceptPayeeship is SetUp { + event PayeeshipTransferred(address indexed transmitter, address indexed from, address indexed to); + + function test_RevertsWhen_NotCalledByProposedPayee() external { + registry.setPayees(PAYEES); + + vm.startPrank(PAYEES[0]); + address newPayee = randomAddress(); + registry.transferPayeeship(TRANSMITTERS[0], newPayee); + + vm.startPrank(STRANGER); + vm.expectRevert(Registry.OnlyCallableByProposedPayee.selector); + registry.acceptPayeeship(TRANSMITTERS[0]); + } + + function test_PayeeChanged() external { + registry.setPayees(PAYEES); + + vm.startPrank(PAYEES[0]); + address newPayee = randomAddress(); + registry.transferPayeeship(TRANSMITTERS[0], newPayee); + + vm.startPrank(newPayee); + vm.expectEmit(); + emit PayeeshipTransferred(TRANSMITTERS[0], PAYEES[0], newPayee); + registry.acceptPayeeship(TRANSMITTERS[0]); + + (, , , , address payee) = registry.getTransmitterInfo(TRANSMITTERS[0]); + assertEq(newPayee, payee); + } +} + +contract SetPayees is SetUp { + event PayeesUpdated(address[] transmitters, address[] payees); + + function test_RevertsWhen_NotCalledByOwner() external { + vm.startPrank(STRANGER); + + vm.expectRevert(bytes("Only callable by owner")); + registry.setPayees(NEW_PAYEES); + } + + function test_RevertsWhen_PayeesLengthError() external { + vm.startPrank(registry.owner()); + + address[] memory payees = new address[](5); + vm.expectRevert(Registry.ParameterLengthError.selector); + registry.setPayees(payees); + } + + function test_RevertsWhen_InvalidPayee() external { + vm.startPrank(registry.owner()); + + NEW_PAYEES[0] = address(0); + vm.expectRevert(Registry.InvalidPayee.selector); + registry.setPayees(NEW_PAYEES); + } + + function test_SetPayees_WhenExistingPayeesAreEmpty() external { + (registry, ) = deployAndConfigureZKSyncRegistryAndRegistrar(AutoBase.PayoutMode.ON_CHAIN); + + for (uint256 i = 0; i < TRANSMITTERS.length; i++) { + (, , , , address payee) = registry.getTransmitterInfo(TRANSMITTERS[i]); + assertEq(address(0), payee); + } + + vm.startPrank(registry.owner()); + + vm.expectEmit(); + emit PayeesUpdated(TRANSMITTERS, PAYEES); + registry.setPayees(PAYEES); + for (uint256 i = 0; i < TRANSMITTERS.length; i++) { + (bool active, , , , address payee) = registry.getTransmitterInfo(TRANSMITTERS[i]); + assertTrue(active); + assertEq(PAYEES[i], payee); + } + } + + function test_DotNotSetPayeesToIgnoredAddress() external { + address IGNORE_ADDRESS = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF; + (registry, ) = deployAndConfigureZKSyncRegistryAndRegistrar(AutoBase.PayoutMode.ON_CHAIN); + PAYEES[0] = IGNORE_ADDRESS; + + registry.setPayees(PAYEES); + (bool active, , , , address payee) = registry.getTransmitterInfo(TRANSMITTERS[0]); + assertTrue(active); + assertEq(address(0), payee); + + (active, , , , payee) = registry.getTransmitterInfo(TRANSMITTERS[1]); + assertTrue(active); + assertEq(PAYEES[1], payee); + } +} + +contract GetActiveUpkeepIDs is SetUp { + function test_RevertsWhen_IndexOutOfRange() external { + vm.expectRevert(Registry.IndexOutOfRange.selector); + registry.getActiveUpkeepIDs(5, 0); + + vm.expectRevert(Registry.IndexOutOfRange.selector); + registry.getActiveUpkeepIDs(6, 0); + } + + function test_ReturnsAllUpkeeps_WhenMaxCountIsZero() external { + uint256[] memory uids = registry.getActiveUpkeepIDs(0, 0); + assertEq(5, uids.length); + + uids = registry.getActiveUpkeepIDs(2, 0); + assertEq(3, uids.length); + } + + function test_ReturnsAllRemainingUpkeeps_WhenMaxCountIsTooLarge() external { + uint256[] memory uids = registry.getActiveUpkeepIDs(2, 20); + assertEq(3, uids.length); + } + + function test_ReturnsUpkeeps_BoundByMaxCount() external { + uint256[] memory uids = registry.getActiveUpkeepIDs(1, 2); + assertEq(2, uids.length); + assertEq(uids[0], linkUpkeepID2); + assertEq(uids[1], usdUpkeepID18); + } +} diff --git a/contracts/src/v0.8/automation/testhelpers/UpkeepCounterNew.sol b/contracts/src/v0.8/automation/testhelpers/UpkeepCounterNew.sol new file mode 100644 index 0000000000..76b3877689 --- /dev/null +++ b/contracts/src/v0.8/automation/testhelpers/UpkeepCounterNew.sol @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.16; + +contract UpkeepCounterNew { + event PerformingUpkeep( + address indexed from, + uint256 initialTimestamp, + uint256 lastTimestamp, + uint256 previousBlock, + uint256 counter + ); + error InvalidCaller(address caller, address forwarder); + + uint256 public testRange; + uint256 public interval; + uint256 public lastTimestamp; + uint256 public previousPerformBlock; + uint256 public initialTimestamp; + uint256 public counter; + bool public useMoreCheckGas; + bool public useMorePerformGas; + bool public useMorePerformData; + uint256 public checkGasToBurn; + uint256 public performGasToBurn; + bytes public data; + bytes public dataCopy; + bool public trickSimulation = false; + address public forwarder; + + constructor() { + testRange = 1000000; + interval = 40; + previousPerformBlock = 0; + lastTimestamp = block.timestamp; + initialTimestamp = 0; + counter = 0; + useMoreCheckGas = false; + useMorePerformData = false; + useMorePerformGas = false; + checkGasToBurn = 9700000; + performGasToBurn = 7700000; + } + + function setPerformGasToBurn(uint256 _performGasToBurn) external { + performGasToBurn = _performGasToBurn; + } + + function setCheckGasToBurn(uint256 _checkGasToBurn) external { + checkGasToBurn = _checkGasToBurn; + } + + function setUseMoreCheckGas(bool _useMoreCheckGas) external { + useMoreCheckGas = _useMoreCheckGas; + } + + function setUseMorePerformGas(bool _useMorePerformGas) external { + useMorePerformGas = _useMorePerformGas; + } + + function setUseMorePerformData(bool _useMorePerformData) external { + useMorePerformData = _useMorePerformData; + } + + function setData(bytes calldata _data) external { + data = _data; + } + + function checkUpkeep(bytes calldata) external view returns (bool, bytes memory) { + if (useMoreCheckGas) { + uint256 startGas = gasleft(); + while (startGas - gasleft() < checkGasToBurn) {} // burn gas + } + + if (useMorePerformData) { + return (eligible(), data); + } + return (eligible(), ""); + } + + function setTrickSimulation(bool _trickSimulation) external { + trickSimulation = _trickSimulation; + } + + function performUpkeep(bytes calldata performData) external { + if (trickSimulation && tx.origin == address(0)) { + return; + } + + if (msg.sender != forwarder) { + revert InvalidCaller(msg.sender, forwarder); + } + + if (useMorePerformGas) { + uint256 startGas = gasleft(); + while (startGas - gasleft() < performGasToBurn) {} // burn gas + } + + if (initialTimestamp == 0) { + initialTimestamp = block.timestamp; + } + lastTimestamp = block.timestamp; + counter = counter + 1; + dataCopy = performData; + emit PerformingUpkeep(tx.origin, initialTimestamp, lastTimestamp, previousPerformBlock, counter); + previousPerformBlock = lastTimestamp; + } + + function eligible() public view returns (bool) { + if (initialTimestamp == 0) { + return true; + } + + return (block.timestamp - initialTimestamp) < testRange && (block.timestamp - lastTimestamp) >= interval; + } + + function setSpread(uint256 _testRange, uint256 _interval) external { + testRange = _testRange; + interval = _interval; + initialTimestamp = 0; + counter = 0; + } + + function setForwarder(address _forwarder) external { + forwarder = _forwarder; + } +} diff --git a/contracts/src/v0.8/automation/upkeeps/CronUpkeep.sol b/contracts/src/v0.8/automation/upkeeps/CronUpkeep.sol index 614b84635a..b9eda1f400 100644 --- a/contracts/src/v0.8/automation/upkeeps/CronUpkeep.sol +++ b/contracts/src/v0.8/automation/upkeeps/CronUpkeep.sol @@ -18,12 +18,12 @@ pragma solidity 0.8.6; -import "@openzeppelin/contracts/security/Pausable.sol"; -import "@openzeppelin/contracts/proxy/Proxy.sol"; -import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; -import "../../shared/access/ConfirmedOwner.sol"; -import "../KeeperBase.sol"; -import "../interfaces/KeeperCompatibleInterface.sol"; +import {Pausable} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/security/Pausable.sol"; +import {Proxy} from "../../vendor/openzeppelin-solidity/v4.7.3/contracts/proxy/Proxy.sol"; +import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/structs/EnumerableSet.sol"; +import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; +import {KeeperBase as KeeperBase} from "../KeeperBase.sol"; +import {KeeperCompatibleInterface as KeeperCompatibleInterface} from "../interfaces/KeeperCompatibleInterface.sol"; import {Cron as CronInternal, Spec} from "../libraries/internal/Cron.sol"; import {Cron as CronExternal} from "../libraries/external/Cron.sol"; diff --git a/contracts/src/v0.8/automation/upkeeps/CronUpkeepDelegate.sol b/contracts/src/v0.8/automation/upkeeps/CronUpkeepDelegate.sol index ec2c2a0fd9..ed8d031c86 100644 --- a/contracts/src/v0.8/automation/upkeeps/CronUpkeepDelegate.sol +++ b/contracts/src/v0.8/automation/upkeeps/CronUpkeepDelegate.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.6; -import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/structs/EnumerableSet.sol"; import {Cron, Spec} from "../libraries/internal/Cron.sol"; /** diff --git a/contracts/src/v0.8/automation/upkeeps/CronUpkeepFactory.sol b/contracts/src/v0.8/automation/upkeeps/CronUpkeepFactory.sol index cd9ae5d7a9..2b6e97e4d0 100644 --- a/contracts/src/v0.8/automation/upkeeps/CronUpkeepFactory.sol +++ b/contracts/src/v0.8/automation/upkeeps/CronUpkeepFactory.sol @@ -2,9 +2,9 @@ pragma solidity 0.8.6; -import "./CronUpkeep.sol"; -import "./CronUpkeepDelegate.sol"; -import "../../shared/access/ConfirmedOwner.sol"; +import {CronUpkeep} from "./CronUpkeep.sol"; +import {CronUpkeepDelegate} from "./CronUpkeepDelegate.sol"; +import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; import {Spec, Cron as CronExternal} from "../libraries/external/Cron.sol"; /** diff --git a/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol b/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol index 5d8a8d58c8..824bce711b 100644 --- a/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol +++ b/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol @@ -61,6 +61,7 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter error InvalidWatchList(); error InvalidChainSelector(); error DuplicateAddress(address duplicate); + error ReentrantCall(); struct MonitoredAddress { uint96 minBalance; @@ -94,6 +95,8 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter /// whenever a new one is deployed with the same dstChainSelector. EnumerableMap.UintToAddressMap private s_onRampAddresses; + bool private reentrancyGuard; + /// @param admin is the administrator address of this contract /// @param linkToken the LINK token address /// @param minWaitPeriodSeconds represents the amount of time that has to wait a contract to be funded @@ -116,6 +119,7 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter setMaxPerform(maxPerform); setMaxCheck(maxCheck); setUpkeepInterval(upkeepInterval); + reentrancyGuard = false; } /// @notice Sets the list of subscriptions to watch and their funding parameters @@ -259,7 +263,7 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter /// @notice tries to fund an array of target addresses, checking if they're underfunded in the process /// @param targetAddresses is an array of contract addresses to be funded in case they're underfunded - function topUp(address[] memory targetAddresses) public whenNotPaused { + function topUp(address[] memory targetAddresses) public whenNotPaused nonReentrant { MonitoredAddress memory contractToFund; uint256 minWaitPeriod = s_minWaitPeriodSeconds; uint256 localBalance = i_linkToken.balanceOf(address(this)); @@ -457,6 +461,13 @@ contract LinkAvailableBalanceMonitor is AccessControl, AutomationCompatibleInter _; } + modifier nonReentrant() { + if (reentrancyGuard) revert ReentrantCall(); + reentrancyGuard = true; + _; + reentrancyGuard = false; + } + /// @notice Pause the contract, which prevents executing performUpkeep function pause() external onlyRole(ADMIN_ROLE) { _pause(); diff --git a/contracts/src/v0.8/automation/v2_2/AutomationRegistry2_2.sol b/contracts/src/v0.8/automation/v2_2/AutomationRegistry2_2.sol index f0c703679c..464e874639 100644 --- a/contracts/src/v0.8/automation/v2_2/AutomationRegistry2_2.sol +++ b/contracts/src/v0.8/automation/v2_2/AutomationRegistry2_2.sol @@ -115,7 +115,7 @@ contract AutomationRegistry2_2 is AutomationRegistryBase2_2, OCR2Abstract, Chain }); uint256 blocknumber = hotVars.chainModule.blockNumber(); - uint256 l1Fee = hotVars.chainModule.getCurrentL1Fee(); + uint256 l1Fee = hotVars.chainModule.getCurrentL1Fee(msg.data.length); for (uint256 i = 0; i < report.upkeepIds.length; i++) { upkeepTransmitInfo[i].upkeep = s_upkeep[report.upkeepIds[i]]; diff --git a/contracts/src/v0.8/automation/v2_3/AutomationRegistry2_3.sol b/contracts/src/v0.8/automation/v2_3/AutomationRegistry2_3.sol index 6113cbf9fd..031d7b5dfb 100644 --- a/contracts/src/v0.8/automation/v2_3/AutomationRegistry2_3.sol +++ b/contracts/src/v0.8/automation/v2_3/AutomationRegistry2_3.sol @@ -136,7 +136,7 @@ contract AutomationRegistry2_3 is AutomationRegistryBase2_3, OCR2Abstract, Chain }); uint256 blocknumber = hotVars.chainModule.blockNumber(); - uint256 l1Fee = hotVars.chainModule.getCurrentL1Fee(); + uint256 l1Fee = hotVars.chainModule.getCurrentL1Fee(msg.data.length); for (uint256 i = 0; i < report.upkeepIds.length; i++) { upkeepTransmitInfo[i].upkeep = s_upkeep[report.upkeepIds[i]]; diff --git a/contracts/src/v0.8/automation/v2_3/AutomationRegistryBase2_3.sol b/contracts/src/v0.8/automation/v2_3/AutomationRegistryBase2_3.sol index fa8f06ffc0..354a6a9b47 100644 --- a/contracts/src/v0.8/automation/v2_3/AutomationRegistryBase2_3.sol +++ b/contracts/src/v0.8/automation/v2_3/AutomationRegistryBase2_3.sol @@ -45,7 +45,7 @@ abstract contract AutomationRegistryBase2_3 is ConfirmedOwner { // These values are calibrated using hardhat tests which simulate various cases and verify that // the variables result in accurate estimation uint256 internal constant REGISTRY_CONDITIONAL_OVERHEAD = 98_200; // Fixed gas overhead for conditional upkeeps - uint256 internal constant REGISTRY_LOG_OVERHEAD = 122_500; // Fixed gas overhead for log upkeeps + uint256 internal constant REGISTRY_LOG_OVERHEAD = 123_500; // Fixed gas overhead for log upkeeps uint256 internal constant REGISTRY_PER_SIGNER_GAS_OVERHEAD = 5_600; // Value scales with f uint256 internal constant REGISTRY_PER_PERFORM_BYTE_GAS_OVERHEAD = 24; // Per perform data byte overhead diff --git a/contracts/src/v0.8/automation/v2_3_zksync/ZKSyncAutomationRegistry2_3.sol b/contracts/src/v0.8/automation/v2_3_zksync/ZKSyncAutomationRegistry2_3.sol new file mode 100644 index 0000000000..67a9a56b3d --- /dev/null +++ b/contracts/src/v0.8/automation/v2_3_zksync/ZKSyncAutomationRegistry2_3.sol @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.19; + +import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/structs/EnumerableSet.sol"; +import {Address} from "../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/Address.sol"; +import {ZKSyncAutomationRegistryBase2_3} from "./ZKSyncAutomationRegistryBase2_3.sol"; +import {ZKSyncAutomationRegistryLogicA2_3} from "./ZKSyncAutomationRegistryLogicA2_3.sol"; +import {ZKSyncAutomationRegistryLogicC2_3} from "./ZKSyncAutomationRegistryLogicC2_3.sol"; +import {Chainable} from "../Chainable.sol"; +import {OCR2Abstract} from "../../shared/ocr2/OCR2Abstract.sol"; +import {IERC20Metadata as IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/extensions/IERC20Metadata.sol"; + +/** + * @notice Registry for adding work for Chainlink nodes to perform on client + * contracts. Clients must support the AutomationCompatibleInterface interface. + */ +contract ZKSyncAutomationRegistry2_3 is ZKSyncAutomationRegistryBase2_3, OCR2Abstract, Chainable { + using Address for address; + using EnumerableSet for EnumerableSet.UintSet; + using EnumerableSet for EnumerableSet.AddressSet; + + /** + * @notice versions: + * AutomationRegistry 2.3.0: supports native and ERC20 billing + * changes flat fee to USD-denominated + * adds support for custom billing overrides + * AutomationRegistry 2.2.0: moves chain-specific integration code into a separate module + * KeeperRegistry 2.1.0: introduces support for log triggers + * removes the need for "wrapped perform data" + * KeeperRegistry 2.0.2: pass revert bytes as performData when target contract reverts + * fixes issue with arbitrum block number + * does an early return in case of stale report instead of revert + * KeeperRegistry 2.0.1: implements workaround for buggy migrate function in 1.X + * KeeperRegistry 2.0.0: implement OCR interface + * KeeperRegistry 1.3.0: split contract into Proxy and Logic + * account for Arbitrum and Optimism L1 gas fee + * allow users to configure upkeeps + * KeeperRegistry 1.2.0: allow funding within performUpkeep + * allow configurable registry maxPerformGas + * add function to let admin change upkeep gas limit + * add minUpkeepSpend requirement + * upgrade to solidity v0.8 + * KeeperRegistry 1.1.0: added flatFeeMicroLink + * KeeperRegistry 1.0.0: initial release + */ + string public constant override typeAndVersion = "AutomationRegistry 2.3.0"; + + /** + * @param logicA the address of the first logic contract + * @dev we cast the contract to logicC in order to call logicC functions (via fallback) + */ + constructor( + ZKSyncAutomationRegistryLogicA2_3 logicA + ) + ZKSyncAutomationRegistryBase2_3( + ZKSyncAutomationRegistryLogicC2_3(address(logicA)).getLinkAddress(), + ZKSyncAutomationRegistryLogicC2_3(address(logicA)).getLinkUSDFeedAddress(), + ZKSyncAutomationRegistryLogicC2_3(address(logicA)).getNativeUSDFeedAddress(), + ZKSyncAutomationRegistryLogicC2_3(address(logicA)).getFastGasFeedAddress(), + ZKSyncAutomationRegistryLogicC2_3(address(logicA)).getAutomationForwarderLogic(), + ZKSyncAutomationRegistryLogicC2_3(address(logicA)).getAllowedReadOnlyAddress(), + ZKSyncAutomationRegistryLogicC2_3(address(logicA)).getPayoutMode(), + ZKSyncAutomationRegistryLogicC2_3(address(logicA)).getWrappedNativeTokenAddress() + ) + Chainable(address(logicA)) + {} + + /** + * @notice holds the variables used in the transmit function, necessary to avoid stack too deep errors + */ + struct TransmitVars { + uint16 numUpkeepsPassedChecks; + uint96 totalReimbursement; + uint96 totalPremium; + } + + // ================================================================ + // | HOT PATH ACTIONS | + // ================================================================ + + /** + * @inheritdoc OCR2Abstract + */ + function transmit( + bytes32[3] calldata reportContext, + bytes calldata rawReport, + bytes32[] calldata rs, + bytes32[] calldata ss, + bytes32 rawVs + ) external override { + // use this msg.data length check to ensure no extra data is included in the call + // 4 is first 4 bytes of the keccak-256 hash of the function signature. ss.length == rs.length so use one of them + // 4 + (32 * 3) + (rawReport.length + 32 + 32) + (32 * rs.length + 32 + 32) + (32 * ss.length + 32 + 32) + 32 + uint256 requiredLength = 324 + rawReport.length + 64 * rs.length; + if (msg.data.length != requiredLength) revert InvalidDataLength(); + HotVars memory hotVars = s_hotVars; + + if (hotVars.paused) revert RegistryPaused(); + if (!s_transmitters[msg.sender].active) revert OnlyActiveTransmitters(); + + // Verify signatures + if (s_latestConfigDigest != reportContext[0]) revert ConfigDigestMismatch(); + if (rs.length != hotVars.f + 1 || rs.length != ss.length) revert IncorrectNumberOfSignatures(); + _verifyReportSignature(reportContext, rawReport, rs, ss, rawVs); + + Report memory report = _decodeReport(rawReport); + + uint40 epochAndRound = uint40(uint256(reportContext[1])); + uint32 epoch = uint32(epochAndRound >> 8); + + _handleReport(hotVars, report); + + if (epoch > hotVars.latestEpoch) { + s_hotVars.latestEpoch = epoch; + } + } + + /** + * @notice handles the report by performing the upkeeps and updating the state + * @param hotVars the hot variables of the registry + * @param report the report to be handled (already verified and decoded) + * @dev had to split this function from transmit() to avoid stack too deep errors + * @dev all other internal / private functions are generally defined in the Base contract + * we leave this here because it is essentially a continuation of the transmit() function, + */ + function _handleReport(HotVars memory hotVars, Report memory report) private { + UpkeepTransmitInfo[] memory upkeepTransmitInfo = new UpkeepTransmitInfo[](report.upkeepIds.length); + TransmitVars memory transmitVars = TransmitVars({ + numUpkeepsPassedChecks: 0, + totalReimbursement: 0, + totalPremium: 0 + }); + + uint256 blocknumber = hotVars.chainModule.blockNumber(); + uint256 gasOverhead; + + for (uint256 i = 0; i < report.upkeepIds.length; i++) { + upkeepTransmitInfo[i].upkeep = s_upkeep[report.upkeepIds[i]]; + upkeepTransmitInfo[i].triggerType = _getTriggerType(report.upkeepIds[i]); + + (upkeepTransmitInfo[i].earlyChecksPassed, upkeepTransmitInfo[i].dedupID) = _prePerformChecks( + report.upkeepIds[i], + blocknumber, + report.triggers[i], + upkeepTransmitInfo[i], + hotVars + ); + + if (upkeepTransmitInfo[i].earlyChecksPassed) { + transmitVars.numUpkeepsPassedChecks += 1; + } else { + continue; + } + + // Actually perform the target upkeep + (upkeepTransmitInfo[i].performSuccess, upkeepTransmitInfo[i].gasUsed) = _performUpkeep( + upkeepTransmitInfo[i].upkeep.forwarder, + report.gasLimits[i], + report.performDatas[i] + ); + + // Store last perform block number / deduping key for upkeep + _updateTriggerMarker(report.upkeepIds[i], blocknumber, upkeepTransmitInfo[i]); + + if (upkeepTransmitInfo[i].triggerType == Trigger.CONDITION) { + gasOverhead += REGISTRY_CONDITIONAL_OVERHEAD; + } else { + gasOverhead += REGISTRY_LOG_OVERHEAD; + } + } + // No upkeeps to be performed in this report + if (transmitVars.numUpkeepsPassedChecks == 0) { + return; + } + + gasOverhead += + 16 * + msg.data.length + + ACCOUNTING_FIXED_GAS_OVERHEAD + + (REGISTRY_PER_SIGNER_GAS_OVERHEAD * (hotVars.f + 1)); + gasOverhead = gasOverhead / transmitVars.numUpkeepsPassedChecks + ACCOUNTING_PER_UPKEEP_GAS_OVERHEAD; + + { + BillingTokenPaymentParams memory billingTokenParams; + uint256 nativeUSD = _getNativeUSD(hotVars); + for (uint256 i = 0; i < report.upkeepIds.length; i++) { + if (upkeepTransmitInfo[i].earlyChecksPassed) { + if (i == 0 || upkeepTransmitInfo[i].upkeep.billingToken != upkeepTransmitInfo[i - 1].upkeep.billingToken) { + billingTokenParams = _getBillingTokenPaymentParams(hotVars, upkeepTransmitInfo[i].upkeep.billingToken); + } + PaymentReceipt memory receipt = _handlePayment( + hotVars, + PaymentParams({ + gasLimit: upkeepTransmitInfo[i].gasUsed, + gasOverhead: gasOverhead, + l1CostWei: 0, + fastGasWei: report.fastGasWei, + linkUSD: report.linkUSD, + nativeUSD: nativeUSD, + billingToken: upkeepTransmitInfo[i].upkeep.billingToken, + billingTokenParams: billingTokenParams, + isTransaction: true + }), + report.upkeepIds[i], + upkeepTransmitInfo[i].upkeep + ); + transmitVars.totalPremium += receipt.premiumInJuels; + transmitVars.totalReimbursement += receipt.gasReimbursementInJuels; + + emit UpkeepPerformed( + report.upkeepIds[i], + upkeepTransmitInfo[i].performSuccess, + receipt.gasChargeInBillingToken + receipt.premiumInBillingToken, + upkeepTransmitInfo[i].gasUsed, + gasOverhead, + report.triggers[i] + ); + } + } + } + // record payments to NOPs, all in LINK + s_transmitters[msg.sender].balance += transmitVars.totalReimbursement; + s_hotVars.totalPremium += transmitVars.totalPremium; + s_reserveAmounts[IERC20(address(i_link))] += transmitVars.totalReimbursement + transmitVars.totalPremium; + } + + // ================================================================ + // | OCR2ABSTRACT | + // ================================================================ + + /** + * @inheritdoc OCR2Abstract + * @dev prefer the type-safe version of setConfig (below) whenever possible. The OnchainConfig could differ between registry versions + * @dev this function takes up precious space on the root contract, but must be implemented to conform to the OCR2Abstract interface + */ + function setConfig( + address[] memory signers, + address[] memory transmitters, + uint8 f, + bytes memory onchainConfigBytes, + uint64 offchainConfigVersion, + bytes memory offchainConfig + ) external override { + (OnchainConfig memory config, IERC20[] memory billingTokens, BillingConfig[] memory billingConfigs) = abi.decode( + onchainConfigBytes, + (OnchainConfig, IERC20[], BillingConfig[]) + ); + + setConfigTypeSafe( + signers, + transmitters, + f, + config, + offchainConfigVersion, + offchainConfig, + billingTokens, + billingConfigs + ); + } + + /** + * @notice sets the configuration for the registry + * @param signers the list of permitted signers + * @param transmitters the list of permitted transmitters + * @param f the maximum tolerance for faulty nodes + * @param onchainConfig configuration values that are used on-chain + * @param offchainConfigVersion the version of the offchainConfig + * @param offchainConfig configuration values that are used off-chain + * @param billingTokens the list of valid billing tokens + * @param billingConfigs the configurations for each billing token + */ + function setConfigTypeSafe( + address[] memory signers, + address[] memory transmitters, + uint8 f, + OnchainConfig memory onchainConfig, + uint64 offchainConfigVersion, + bytes memory offchainConfig, + IERC20[] memory billingTokens, + BillingConfig[] memory billingConfigs + ) public onlyOwner { + if (signers.length > MAX_NUM_ORACLES) revert TooManyOracles(); + if (f == 0) revert IncorrectNumberOfFaultyOracles(); + if (signers.length != transmitters.length || signers.length <= 3 * f) revert IncorrectNumberOfSigners(); + if (billingTokens.length != billingConfigs.length) revert ParameterLengthError(); + // set billing config for tokens + _setBillingConfig(billingTokens, billingConfigs); + + _updateTransmitters(signers, transmitters); + + s_hotVars = HotVars({ + f: f, + stalenessSeconds: onchainConfig.stalenessSeconds, + gasCeilingMultiplier: onchainConfig.gasCeilingMultiplier, + paused: s_hotVars.paused, + reentrancyGuard: s_hotVars.reentrancyGuard, + totalPremium: s_hotVars.totalPremium, + latestEpoch: 0, // DON restarts epoch + reorgProtectionEnabled: onchainConfig.reorgProtectionEnabled, + chainModule: onchainConfig.chainModule + }); + + uint32 previousConfigBlockNumber = s_storage.latestConfigBlockNumber; + uint32 newLatestConfigBlockNumber = uint32(onchainConfig.chainModule.blockNumber()); + uint32 newConfigCount = s_storage.configCount + 1; + + s_storage = Storage({ + checkGasLimit: onchainConfig.checkGasLimit, + maxPerformGas: onchainConfig.maxPerformGas, + transcoder: onchainConfig.transcoder, + maxCheckDataSize: onchainConfig.maxCheckDataSize, + maxPerformDataSize: onchainConfig.maxPerformDataSize, + maxRevertDataSize: onchainConfig.maxRevertDataSize, + upkeepPrivilegeManager: onchainConfig.upkeepPrivilegeManager, + financeAdmin: onchainConfig.financeAdmin, + nonce: s_storage.nonce, + configCount: newConfigCount, + latestConfigBlockNumber: newLatestConfigBlockNumber + }); + s_fallbackGasPrice = onchainConfig.fallbackGasPrice; + s_fallbackLinkPrice = onchainConfig.fallbackLinkPrice; + s_fallbackNativePrice = onchainConfig.fallbackNativePrice; + + bytes memory onchainConfigBytes = abi.encode(onchainConfig); + + s_latestConfigDigest = _configDigestFromConfigData( + block.chainid, + address(this), + s_storage.configCount, + signers, + transmitters, + f, + onchainConfigBytes, + offchainConfigVersion, + offchainConfig + ); + + for (uint256 idx = s_registrars.length(); idx > 0; idx--) { + s_registrars.remove(s_registrars.at(idx - 1)); + } + + for (uint256 idx = 0; idx < onchainConfig.registrars.length; idx++) { + s_registrars.add(onchainConfig.registrars[idx]); + } + + emit ConfigSet( + previousConfigBlockNumber, + s_latestConfigDigest, + s_storage.configCount, + signers, + transmitters, + f, + onchainConfigBytes, + offchainConfigVersion, + offchainConfig + ); + } + + /** + * @inheritdoc OCR2Abstract + * @dev this function takes up precious space on the root contract, but must be implemented to conform to the OCR2Abstract interface + */ + function latestConfigDetails() + external + view + override + returns (uint32 configCount, uint32 blockNumber, bytes32 configDigest) + { + return (s_storage.configCount, s_storage.latestConfigBlockNumber, s_latestConfigDigest); + } + + /** + * @inheritdoc OCR2Abstract + * @dev this function takes up precious space on the root contract, but must be implemented to conform to the OCR2Abstract interface + */ + function latestConfigDigestAndEpoch() + external + view + override + returns (bool scanLogs, bytes32 configDigest, uint32 epoch) + { + return (false, s_latestConfigDigest, s_hotVars.latestEpoch); + } +} diff --git a/contracts/src/v0.8/automation/v2_3_zksync/ZKSyncAutomationRegistryBase2_3.sol b/contracts/src/v0.8/automation/v2_3_zksync/ZKSyncAutomationRegistryBase2_3.sol new file mode 100644 index 0000000000..41097af7f2 --- /dev/null +++ b/contracts/src/v0.8/automation/v2_3_zksync/ZKSyncAutomationRegistryBase2_3.sol @@ -0,0 +1,1197 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.19; + +import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/structs/EnumerableSet.sol"; +import {Address} from "../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/Address.sol"; +import {StreamsLookupCompatibleInterface} from "../interfaces/StreamsLookupCompatibleInterface.sol"; +import {ILogAutomation, Log} from "../interfaces/ILogAutomation.sol"; +import {IAutomationForwarder} from "../interfaces/IAutomationForwarder.sol"; +import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; +import {AggregatorV3Interface} from "../../shared/interfaces/AggregatorV3Interface.sol"; +import {LinkTokenInterface} from "../../shared/interfaces/LinkTokenInterface.sol"; +import {KeeperCompatibleInterface} from "../interfaces/KeeperCompatibleInterface.sol"; +import {IChainModule} from "../interfaces/IChainModule.sol"; +import {IERC20Metadata as IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import {SafeCast} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/SafeCast.sol"; +import {IWrappedNative} from "../interfaces/v2_3/IWrappedNative.sol"; + +/** + * @notice Base Keeper Registry contract, contains shared logic between + * AutomationRegistry and AutomationRegistryLogic + * @dev all errors, events, and internal functions should live here + */ +// solhint-disable-next-line max-states-count +abstract contract ZKSyncAutomationRegistryBase2_3 is ConfirmedOwner { + using Address for address; + using EnumerableSet for EnumerableSet.UintSet; + using EnumerableSet for EnumerableSet.AddressSet; + + address internal constant ZERO_ADDRESS = address(0); + address internal constant IGNORE_ADDRESS = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF; + bytes4 internal constant CHECK_SELECTOR = KeeperCompatibleInterface.checkUpkeep.selector; + bytes4 internal constant PERFORM_SELECTOR = KeeperCompatibleInterface.performUpkeep.selector; + bytes4 internal constant CHECK_CALLBACK_SELECTOR = StreamsLookupCompatibleInterface.checkCallback.selector; + bytes4 internal constant CHECK_LOG_SELECTOR = ILogAutomation.checkLog.selector; + uint256 internal constant PERFORM_GAS_MIN = 2_300; + uint256 internal constant CANCELLATION_DELAY = 50; + uint32 internal constant UINT32_MAX = type(uint32).max; + // The first byte of the mask can be 0, because we only ever have 31 oracles + uint256 internal constant ORACLE_MASK = 0x0001010101010101010101010101010101010101010101010101010101010101; + uint8 internal constant UPKEEP_VERSION_BASE = 4; + + // Next block of constants are only used in maxPayment estimation during checkUpkeep simulation + // These values are calibrated using hardhat tests which simulate various cases and verify that + // the variables result in accurate estimation + uint256 internal constant REGISTRY_CONDITIONAL_OVERHEAD = 98_200; // Fixed gas overhead for conditional upkeeps + uint256 internal constant REGISTRY_LOG_OVERHEAD = 122_500; // Fixed gas overhead for log upkeeps + uint256 internal constant REGISTRY_PER_SIGNER_GAS_OVERHEAD = 5_600; // Value scales with f + + // Next block of constants are used in actual payment calculation. We calculate the exact gas used within the + // tx itself, but since payment processing itself takes gas, and it needs the overhead as input, we use fixed constants + // to account for gas used in payment processing. These values are calibrated using hardhat tests which simulates various cases and verifies that + // the variables result in accurate estimation + uint256 internal constant ACCOUNTING_FIXED_GAS_OVERHEAD = 51_000; // Fixed overhead per tx + uint256 internal constant ACCOUNTING_PER_UPKEEP_GAS_OVERHEAD = 20_000; // Overhead per upkeep performed in batch + + LinkTokenInterface internal immutable i_link; + AggregatorV3Interface internal immutable i_linkUSDFeed; + AggregatorV3Interface internal immutable i_nativeUSDFeed; + AggregatorV3Interface internal immutable i_fastGasFeed; + address internal immutable i_automationForwarderLogic; + address internal immutable i_allowedReadOnlyAddress; + IWrappedNative internal immutable i_wrappedNativeToken; + + /** + * @dev - The storage is gas optimised for one and only one function - transmit. All the storage accessed in transmit + * is stored compactly. Rest of the storage layout is not of much concern as transmit is the only hot path + */ + + // Upkeep storage + EnumerableSet.UintSet internal s_upkeepIDs; + mapping(uint256 => Upkeep) internal s_upkeep; // accessed during transmit + mapping(uint256 => address) internal s_upkeepAdmin; + mapping(uint256 => address) internal s_proposedAdmin; + mapping(uint256 => bytes) internal s_checkData; + mapping(bytes32 => bool) internal s_dedupKeys; + // Registry config and state + EnumerableSet.AddressSet internal s_registrars; + mapping(address => Transmitter) internal s_transmitters; + mapping(address => Signer) internal s_signers; + address[] internal s_signersList; // s_signersList contains the signing address of each oracle + address[] internal s_transmittersList; // s_transmittersList contains the transmission address of each oracle + EnumerableSet.AddressSet internal s_deactivatedTransmitters; + mapping(address => address) internal s_transmitterPayees; // s_payees contains the mapping from transmitter to payee. + mapping(address => address) internal s_proposedPayee; // proposed payee for a transmitter + bytes32 internal s_latestConfigDigest; // Read on transmit path in case of signature verification + HotVars internal s_hotVars; // Mixture of config and state, used in transmit + Storage internal s_storage; // Mixture of config and state, not used in transmit + uint256 internal s_fallbackGasPrice; + uint256 internal s_fallbackLinkPrice; + uint256 internal s_fallbackNativePrice; + mapping(address => MigrationPermission) internal s_peerRegistryMigrationPermission; // Permissions for migration to and fro + mapping(uint256 => bytes) internal s_upkeepTriggerConfig; // upkeep triggers + mapping(uint256 => bytes) internal s_upkeepOffchainConfig; // general config set by users for each upkeep + mapping(uint256 => bytes) internal s_upkeepPrivilegeConfig; // general config set by an administrative role for an upkeep + mapping(address => bytes) internal s_adminPrivilegeConfig; // general config set by an administrative role for an admin + // billing + mapping(IERC20 billingToken => uint256 reserveAmount) internal s_reserveAmounts; // unspent user deposits + unwithdrawn NOP payments + mapping(IERC20 billingToken => BillingConfig billingConfig) internal s_billingConfigs; // billing configurations for different tokens + mapping(uint256 upkeepID => BillingOverrides billingOverrides) internal s_billingOverrides; // billing overrides for specific upkeeps + IERC20[] internal s_billingTokens; // list of billing tokens + PayoutMode internal s_payoutMode; + + error ArrayHasNoEntries(); + error CannotCancel(); + error CheckDataExceedsLimit(); + error ConfigDigestMismatch(); + error DuplicateEntry(); + error DuplicateSigners(); + error GasLimitCanOnlyIncrease(); + error GasLimitOutsideRange(); + error IncorrectNumberOfFaultyOracles(); + error IncorrectNumberOfSignatures(); + error IncorrectNumberOfSigners(); + error IndexOutOfRange(); + error InsufficientBalance(uint256 available, uint256 requested); + error InsufficientLinkLiquidity(); + error InvalidDataLength(); + error InvalidFeed(); + error InvalidTrigger(); + error InvalidPayee(); + error InvalidRecipient(); + error InvalidReport(); + error InvalidSigner(); + error InvalidToken(); + error InvalidTransmitter(); + error InvalidTriggerType(); + error MigrationNotPermitted(); + error MustSettleOffchain(); + error MustSettleOnchain(); + error NotAContract(); + error OnlyActiveSigners(); + error OnlyActiveTransmitters(); + error OnlyCallableByAdmin(); + error OnlyCallableByLINKToken(); + error OnlyCallableByOwnerOrAdmin(); + error OnlyCallableByOwnerOrRegistrar(); + error OnlyCallableByPayee(); + error OnlyCallableByProposedAdmin(); + error OnlyCallableByProposedPayee(); + error OnlyCallableByUpkeepPrivilegeManager(); + error OnlyFinanceAdmin(); + error OnlyPausedUpkeep(); + error OnlySimulatedBackend(); + error OnlyUnpausedUpkeep(); + error ParameterLengthError(); + error ReentrantCall(); + error RegistryPaused(); + error RepeatedSigner(); + error RepeatedTransmitter(); + error TargetCheckReverted(bytes reason); + error TooManyOracles(); + error TranscoderNotSet(); + error TransferFailed(); + error UpkeepAlreadyExists(); + error UpkeepCancelled(); + error UpkeepNotCanceled(); + error UpkeepNotNeeded(); + error ValueNotChanged(); + error ZeroAddressNotAllowed(); + + enum MigrationPermission { + NONE, + OUTGOING, + INCOMING, + BIDIRECTIONAL + } + + enum Trigger { + CONDITION, + LOG + } + + enum UpkeepFailureReason { + NONE, + UPKEEP_CANCELLED, + UPKEEP_PAUSED, + TARGET_CHECK_REVERTED, + UPKEEP_NOT_NEEDED, + PERFORM_DATA_EXCEEDS_LIMIT, + INSUFFICIENT_BALANCE, + CALLBACK_REVERTED, + REVERT_DATA_EXCEEDS_LIMIT, + REGISTRY_PAUSED + } + + enum PayoutMode { + ON_CHAIN, + OFF_CHAIN + } + + /** + * @notice OnchainConfig of the registry + * @dev used only in setConfig() + * @member checkGasLimit gas limit when checking for upkeep + * @member stalenessSeconds number of seconds that is allowed for feed data to + * be stale before switching to the fallback pricing + * @member gasCeilingMultiplier multiplier to apply to the fast gas feed price + * when calculating the payment ceiling for keepers + * @member maxPerformGas max performGas allowed for an upkeep on this registry + * @member maxCheckDataSize max length of checkData bytes + * @member maxPerformDataSize max length of performData bytes + * @member maxRevertDataSize max length of revertData bytes + * @member fallbackGasPrice gas price used if the gas price feed is stale + * @member fallbackLinkPrice LINK price used if the LINK price feed is stale + * @member transcoder address of the transcoder contract + * @member registrars addresses of the registrar contracts + * @member upkeepPrivilegeManager address which can set privilege for upkeeps + * @member reorgProtectionEnabled if this registry enables re-org protection checks + * @member chainModule the chain specific module + */ + struct OnchainConfig { + uint32 checkGasLimit; + uint32 maxPerformGas; + uint32 maxCheckDataSize; + address transcoder; + // 1 word full + bool reorgProtectionEnabled; + uint24 stalenessSeconds; + uint32 maxPerformDataSize; + uint32 maxRevertDataSize; + address upkeepPrivilegeManager; + // 2 words full + uint16 gasCeilingMultiplier; + address financeAdmin; + // 3 words + uint256 fallbackGasPrice; + uint256 fallbackLinkPrice; + uint256 fallbackNativePrice; + address[] registrars; + IChainModule chainModule; + } + + /** + * @notice relevant state of an upkeep which is used in transmit function + * @member paused if this upkeep has been paused + * @member overridesEnabled if this upkeep has overrides enabled + * @member performGas the gas limit of upkeep execution + * @member maxValidBlocknumber until which block this upkeep is valid + * @member forwarder the forwarder contract to use for this upkeep + * @member amountSpent the amount this upkeep has spent, in the upkeep's billing token + * @member balance the balance of this upkeep + * @member lastPerformedBlockNumber the last block number when this upkeep was performed + */ + struct Upkeep { + bool paused; + bool overridesEnabled; + uint32 performGas; + uint32 maxValidBlocknumber; + IAutomationForwarder forwarder; + // 2 bytes left in 1st EVM word - read in transmit path + uint128 amountSpent; + uint96 balance; + uint32 lastPerformedBlockNumber; + // 0 bytes left in 2nd EVM word - written in transmit path + IERC20 billingToken; + // 12 bytes left in 3rd EVM word - read in transmit path + } + + /// @dev Config + State storage struct which is on hot transmit path + struct HotVars { + uint96 totalPremium; // ─────────╮ total historical payment to oracles for premium + uint32 latestEpoch; // │ latest epoch for which a report was transmitted + uint24 stalenessSeconds; // │ Staleness tolerance for feeds + uint16 gasCeilingMultiplier; // │ multiplier on top of fast gas feed for upper bound + uint8 f; // │ maximum number of faulty oracles + bool paused; // │ pause switch for all upkeeps in the registry + bool reentrancyGuard; // | guard against reentrancy + bool reorgProtectionEnabled; // ─╯ if this registry should enable the re-org protection mechanism + IChainModule chainModule; // the interface of chain specific module + } + + /// @dev Config + State storage struct which is not on hot transmit path + struct Storage { + address transcoder; // Address of transcoder contract used in migrations + uint32 checkGasLimit; // Gas limit allowed in checkUpkeep + uint32 maxPerformGas; // Max gas an upkeep can use on this registry + uint32 nonce; // Nonce for each upkeep created + // 1 EVM word full + address upkeepPrivilegeManager; // address which can set privilege for upkeeps + uint32 configCount; // incremented each time a new config is posted, The count is incorporated into the config digest to prevent replay attacks. + uint32 latestConfigBlockNumber; // makes it easier for offchain systems to extract config from logs + uint32 maxCheckDataSize; // max length of checkData bytes + // 2 EVM word full + address financeAdmin; // address which can withdraw funds from the contract + uint32 maxPerformDataSize; // max length of performData bytes + uint32 maxRevertDataSize; // max length of revertData bytes + // 4 bytes left in 3rd EVM word + } + + /// @dev Report transmitted by OCR to transmit function + struct Report { + uint256 fastGasWei; + uint256 linkUSD; + uint256[] upkeepIds; + uint256[] gasLimits; + bytes[] triggers; + bytes[] performDatas; + } + + /** + * @dev This struct is used to maintain run time information about an upkeep in transmit function + * @member upkeep the upkeep struct + * @member earlyChecksPassed whether the upkeep passed early checks before perform + * @member performSuccess whether the perform was successful + * @member triggerType the type of trigger + * @member gasUsed gasUsed by this upkeep in perform + * @member dedupID unique ID used to dedup an upkeep/trigger combo + */ + struct UpkeepTransmitInfo { + Upkeep upkeep; + bool earlyChecksPassed; + bool performSuccess; + Trigger triggerType; + uint256 gasUsed; + bytes32 dedupID; + } + + /** + * @notice holds information about a transmiter / node in the DON + * @member active can this transmitter submit reports + * @member index of oracle in s_signersList/s_transmittersList + * @member balance a node's balance in LINK + * @member lastCollected the total balance at which the node last withdrew + * @dev uint96 is safe for balance / last collected because transmitters are only ever paid in LINK + */ + struct Transmitter { + bool active; + uint8 index; + uint96 balance; + uint96 lastCollected; + } + + struct TransmitterPayeeInfo { + address transmitterAddress; + address payeeAddress; + } + + struct Signer { + bool active; + // Index of oracle in s_signersList/s_transmittersList + uint8 index; + } + + /** + * @notice the trigger structure conditional trigger type + */ + struct ConditionalTrigger { + uint32 blockNum; + bytes32 blockHash; + } + + /** + * @notice the trigger structure of log upkeeps + * @dev NOTE that blockNum / blockHash describe the block used for the callback, + * not necessarily the block number that the log was emitted in!!!! + */ + struct LogTrigger { + bytes32 logBlockHash; + bytes32 txHash; + uint32 logIndex; + uint32 blockNum; + bytes32 blockHash; + } + + /** + * @notice the billing config of a token + * @dev this is a storage struct + */ + // solhint-disable-next-line gas-struct-packing + struct BillingConfig { + uint32 gasFeePPB; + uint24 flatFeeMilliCents; // min fee is $0.00001, max fee is $167 + AggregatorV3Interface priceFeed; + uint8 decimals; + // 1st word, read in calculating BillingTokenPaymentParams + uint256 fallbackPrice; + // 2nd word only read if stale + uint96 minSpend; + // 3rd word only read during cancellation + } + + /** + * @notice override-able billing params of a billing token + */ + struct BillingOverrides { + uint32 gasFeePPB; + uint24 flatFeeMilliCents; + } + + /** + * @notice pricing params for a billing token + * @dev this is a memory-only struct, so struct packing is less important + */ + struct BillingTokenPaymentParams { + uint8 decimals; + uint32 gasFeePPB; + uint24 flatFeeMilliCents; + uint256 priceUSD; + } + + /** + * @notice struct containing price & payment information used in calculating payment amount + * @member gasLimit the amount of gas used + * @member gasOverhead the amount of gas overhead + * @member l1CostWei the amount to be charged for L1 fee in wei + * @member fastGasWei the fast gas price + * @member linkUSD the exchange ratio between LINK and USD + * @member nativeUSD the exchange ratio between the chain's native token and USD + * @member billingToken the billing token + * @member billingTokenParams the payment params specific to a particular payment token + * @member isTransaction is this an eth_call or a transaction + */ + struct PaymentParams { + uint256 gasLimit; + uint256 gasOverhead; + uint256 l1CostWei; + uint256 fastGasWei; + uint256 linkUSD; + uint256 nativeUSD; + IERC20 billingToken; + BillingTokenPaymentParams billingTokenParams; + bool isTransaction; + } + + /** + * @notice struct containing receipt information about a payment or cost estimation + * @member gasChargeInBillingToken the amount to charge a user for gas spent using the billing token's native decimals + * @member premiumInBillingToken the premium charged to the user, shared between all nodes, using the billing token's native decimals + * @member gasReimbursementInJuels the amount to reimburse a node for gas spent + * @member premiumInJuels the premium paid to NOPs, shared between all nodes + */ + // solhint-disable-next-line gas-struct-packing + struct PaymentReceipt { + uint96 gasChargeInBillingToken; + uint96 premiumInBillingToken; + // one word ends + uint96 gasReimbursementInJuels; + uint96 premiumInJuels; + // second word ends + IERC20 billingToken; + uint96 linkUSD; + // third word ends + uint96 nativeUSD; + uint96 billingUSD; + // fourth word ends + } + + event AdminPrivilegeConfigSet(address indexed admin, bytes privilegeConfig); + event BillingConfigOverridden(uint256 indexed id, BillingOverrides overrides); + event BillingConfigOverrideRemoved(uint256 indexed id); + event BillingConfigSet(IERC20 indexed token, BillingConfig config); + event CancelledUpkeepReport(uint256 indexed id, bytes trigger); + event ChainSpecificModuleUpdated(address newModule); + event DedupKeyAdded(bytes32 indexed dedupKey); + event FeesWithdrawn(address indexed assetAddress, address indexed recipient, uint256 amount); + event FundsAdded(uint256 indexed id, address indexed from, uint96 amount); + event FundsWithdrawn(uint256 indexed id, uint256 amount, address to); + event InsufficientFundsUpkeepReport(uint256 indexed id, bytes trigger); + event NOPsSettledOffchain(address[] payees, uint256[] payments); + event Paused(address account); + event PayeesUpdated(address[] transmitters, address[] payees); + event PayeeshipTransferRequested(address indexed transmitter, address indexed from, address indexed to); + event PayeeshipTransferred(address indexed transmitter, address indexed from, address indexed to); + event PaymentWithdrawn(address indexed transmitter, uint256 indexed amount, address indexed to, address payee); + event ReorgedUpkeepReport(uint256 indexed id, bytes trigger); + event StaleUpkeepReport(uint256 indexed id, bytes trigger); + event UpkeepAdminTransferred(uint256 indexed id, address indexed from, address indexed to); + event UpkeepAdminTransferRequested(uint256 indexed id, address indexed from, address indexed to); + event UpkeepCanceled(uint256 indexed id, uint64 indexed atBlockHeight); + event UpkeepCheckDataSet(uint256 indexed id, bytes newCheckData); + event UpkeepGasLimitSet(uint256 indexed id, uint96 gasLimit); + event UpkeepMigrated(uint256 indexed id, uint256 remainingBalance, address destination); + event UpkeepOffchainConfigSet(uint256 indexed id, bytes offchainConfig); + event UpkeepPaused(uint256 indexed id); + event UpkeepPerformed( + uint256 indexed id, + bool indexed success, + uint96 totalPayment, + uint256 gasUsed, + uint256 gasOverhead, + bytes trigger + ); + event UpkeepCharged(uint256 indexed id, PaymentReceipt receipt); + event UpkeepPrivilegeConfigSet(uint256 indexed id, bytes privilegeConfig); + event UpkeepReceived(uint256 indexed id, uint256 startingBalance, address importedFrom); + event UpkeepRegistered(uint256 indexed id, uint32 performGas, address admin); + event UpkeepTriggerConfigSet(uint256 indexed id, bytes triggerConfig); + event UpkeepUnpaused(uint256 indexed id); + event Unpaused(address account); + + /** + * @param link address of the LINK Token + * @param linkUSDFeed address of the LINK/USD price feed + * @param nativeUSDFeed address of the Native/USD price feed + * @param fastGasFeed address of the Fast Gas price feed + * @param automationForwarderLogic the address of automation forwarder logic + * @param allowedReadOnlyAddress the address of the allowed read only address + * @param payoutMode the payout mode + */ + constructor( + address link, + address linkUSDFeed, + address nativeUSDFeed, + address fastGasFeed, + address automationForwarderLogic, + address allowedReadOnlyAddress, + PayoutMode payoutMode, + address wrappedNativeTokenAddress + ) ConfirmedOwner(msg.sender) { + i_link = LinkTokenInterface(link); + i_linkUSDFeed = AggregatorV3Interface(linkUSDFeed); + i_nativeUSDFeed = AggregatorV3Interface(nativeUSDFeed); + i_fastGasFeed = AggregatorV3Interface(fastGasFeed); + i_automationForwarderLogic = automationForwarderLogic; + i_allowedReadOnlyAddress = allowedReadOnlyAddress; + s_payoutMode = payoutMode; + i_wrappedNativeToken = IWrappedNative(wrappedNativeTokenAddress); + if (i_linkUSDFeed.decimals() != i_nativeUSDFeed.decimals()) { + revert InvalidFeed(); + } + } + + // ================================================================ + // | INTERNAL FUNCTIONS ONLY | + // ================================================================ + + /** + * @dev creates a new upkeep with the given fields + * @param id the id of the upkeep + * @param upkeep the upkeep to create + * @param admin address to cancel upkeep and withdraw remaining funds + * @param checkData data which is passed to user's checkUpkeep + * @param triggerConfig the trigger config for this upkeep + * @param offchainConfig the off-chain config of this upkeep + */ + function _createUpkeep( + uint256 id, + Upkeep memory upkeep, + address admin, + bytes memory checkData, + bytes memory triggerConfig, + bytes memory offchainConfig + ) internal { + if (s_hotVars.paused) revert RegistryPaused(); + if (checkData.length > s_storage.maxCheckDataSize) revert CheckDataExceedsLimit(); + if (upkeep.performGas < PERFORM_GAS_MIN || upkeep.performGas > s_storage.maxPerformGas) + revert GasLimitOutsideRange(); + if (address(s_upkeep[id].forwarder) != address(0)) revert UpkeepAlreadyExists(); + if (address(s_billingConfigs[upkeep.billingToken].priceFeed) == address(0)) revert InvalidToken(); + s_upkeep[id] = upkeep; + s_upkeepAdmin[id] = admin; + s_checkData[id] = checkData; + s_reserveAmounts[upkeep.billingToken] = s_reserveAmounts[upkeep.billingToken] + upkeep.balance; + s_upkeepTriggerConfig[id] = triggerConfig; + s_upkeepOffchainConfig[id] = offchainConfig; + s_upkeepIDs.add(id); + } + + /** + * @dev creates an ID for the upkeep based on the upkeep's type + * @dev the format of the ID looks like this: + * ****00000000000X**************** + * 4 bytes of entropy + * 11 bytes of zeros + * 1 identifying byte for the trigger type + * 16 bytes of entropy + * @dev this maintains the same level of entropy as eth addresses, so IDs will still be unique + * @dev we add the "identifying" part in the middle so that it is mostly hidden from users who usually only + * see the first 4 and last 4 hex values ex 0x1234...ABCD + */ + function _createID(Trigger triggerType) internal view returns (uint256) { + bytes1 empty; + IChainModule chainModule = s_hotVars.chainModule; + bytes memory idBytes = abi.encodePacked( + keccak256(abi.encode(chainModule.blockHash((chainModule.blockNumber() - 1)), address(this), s_storage.nonce)) + ); + for (uint256 idx = 4; idx < 15; idx++) { + idBytes[idx] = empty; + } + idBytes[15] = bytes1(uint8(triggerType)); + return uint256(bytes32(idBytes)); + } + + /** + * @dev retrieves feed data for fast gas/native and link/native prices. if the feed + * data is stale it uses the configured fallback price. Once a price is picked + * for gas it takes the min of gas price in the transaction or the fast gas + * price in order to reduce costs for the upkeep clients. + */ + function _getFeedData( + HotVars memory hotVars + ) internal view returns (uint256 gasWei, uint256 linkUSD, uint256 nativeUSD) { + uint32 stalenessSeconds = hotVars.stalenessSeconds; + bool staleFallback = stalenessSeconds > 0; + uint256 timestamp; + int256 feedValue; + (, feedValue, , timestamp, ) = i_fastGasFeed.latestRoundData(); + if ( + feedValue <= 0 || block.timestamp < timestamp || (staleFallback && stalenessSeconds < block.timestamp - timestamp) + ) { + gasWei = s_fallbackGasPrice; + } else { + gasWei = uint256(feedValue); + } + (, feedValue, , timestamp, ) = i_linkUSDFeed.latestRoundData(); + if ( + feedValue <= 0 || block.timestamp < timestamp || (staleFallback && stalenessSeconds < block.timestamp - timestamp) + ) { + linkUSD = s_fallbackLinkPrice; + } else { + linkUSD = uint256(feedValue); + } + return (gasWei, linkUSD, _getNativeUSD(hotVars)); + } + + /** + * @dev this price has it's own getter for use in the transmit() hot path + * in the future, all price data should be included in the report instead of + * getting read during execution + */ + function _getNativeUSD(HotVars memory hotVars) internal view returns (uint256) { + (, int256 feedValue, , uint256 timestamp, ) = i_nativeUSDFeed.latestRoundData(); + if ( + feedValue <= 0 || + block.timestamp < timestamp || + (hotVars.stalenessSeconds > 0 && hotVars.stalenessSeconds < block.timestamp - timestamp) + ) { + return s_fallbackNativePrice; + } else { + return uint256(feedValue); + } + } + + /** + * @dev gets the price and billing params for a specific billing token + */ + function _getBillingTokenPaymentParams( + HotVars memory hotVars, + IERC20 billingToken + ) internal view returns (BillingTokenPaymentParams memory paymentParams) { + BillingConfig storage config = s_billingConfigs[billingToken]; + paymentParams.flatFeeMilliCents = config.flatFeeMilliCents; + paymentParams.gasFeePPB = config.gasFeePPB; + paymentParams.decimals = config.decimals; + (, int256 feedValue, , uint256 timestamp, ) = config.priceFeed.latestRoundData(); + if ( + feedValue <= 0 || + block.timestamp < timestamp || + (hotVars.stalenessSeconds > 0 && hotVars.stalenessSeconds < block.timestamp - timestamp) + ) { + paymentParams.priceUSD = config.fallbackPrice; + } else { + paymentParams.priceUSD = uint256(feedValue); + } + return paymentParams; + } + + /** + * @param hotVars the hot path variables + * @param paymentParams the pricing data and gas usage data + * @return receipt the receipt of payment with pricing breakdown + * @dev use of PaymentParams struct is necessary to avoid stack too deep errors + * @dev calculates LINK paid for gas spent plus a configure premium percentage + * @dev 1 USD = 1e18 attoUSD + * @dev 1 USD = 1e26 hexaicosaUSD (had to borrow this prefix from geometry because there is no metric prefix for 1e-26) + * @dev 1 millicent = 1e-5 USD = 1e13 attoUSD + */ + function _calculatePaymentAmount( + HotVars memory hotVars, + PaymentParams memory paymentParams + ) internal view returns (PaymentReceipt memory receipt) { + uint256 decimals = paymentParams.billingTokenParams.decimals; + uint256 gasWei = paymentParams.fastGasWei * hotVars.gasCeilingMultiplier; + // in case it's actual execution use actual gas price, capped by fastGasWei * gasCeilingMultiplier + if (paymentParams.isTransaction && tx.gasprice < gasWei) { + gasWei = tx.gasprice; + } + + // scaling factor is based on decimals of billing token, and applies to premium and gasCharge + uint256 numeratorScalingFactor = decimals > 18 ? 10 ** (decimals - 18) : 1; + uint256 denominatorScalingFactor = decimals < 18 ? 10 ** (18 - decimals) : 1; + + // gas calculation + uint256 gasPaymentHexaicosaUSD = (gasWei * + (paymentParams.gasLimit + paymentParams.gasOverhead) + + paymentParams.l1CostWei) * paymentParams.nativeUSD; // gasPaymentHexaicosaUSD has an extra 8 zeros because of decimals on nativeUSD feed + // gasChargeInBillingToken is scaled by the billing token's decimals. Round up to ensure a minimum billing token is charged for gas + receipt.gasChargeInBillingToken = SafeCast.toUint96( + ((gasPaymentHexaicosaUSD * numeratorScalingFactor) + + (paymentParams.billingTokenParams.priceUSD * denominatorScalingFactor - 1)) / + (paymentParams.billingTokenParams.priceUSD * denominatorScalingFactor) + ); + // 18 decimals: 26 decimals / 8 decimals + receipt.gasReimbursementInJuels = SafeCast.toUint96(gasPaymentHexaicosaUSD / paymentParams.linkUSD); + + // premium calculation + uint256 flatFeeHexaicosaUSD = uint256(paymentParams.billingTokenParams.flatFeeMilliCents) * 1e21; // 1e13 for milliCents to attoUSD and 1e8 for attoUSD to hexaicosaUSD + uint256 premiumHexaicosaUSD = ((((gasWei * paymentParams.gasLimit) + paymentParams.l1CostWei) * + paymentParams.billingTokenParams.gasFeePPB * + paymentParams.nativeUSD) / 1e9) + flatFeeHexaicosaUSD; + // premium is scaled by the billing token's decimals. Round up to ensure at least minimum charge + receipt.premiumInBillingToken = SafeCast.toUint96( + ((premiumHexaicosaUSD * numeratorScalingFactor) + + (paymentParams.billingTokenParams.priceUSD * denominatorScalingFactor - 1)) / + (paymentParams.billingTokenParams.priceUSD * denominatorScalingFactor) + ); + receipt.premiumInJuels = SafeCast.toUint96(premiumHexaicosaUSD / paymentParams.linkUSD); + + receipt.billingToken = paymentParams.billingToken; + receipt.linkUSD = SafeCast.toUint96(paymentParams.linkUSD); + receipt.nativeUSD = SafeCast.toUint96(paymentParams.nativeUSD); + receipt.billingUSD = SafeCast.toUint96(paymentParams.billingTokenParams.priceUSD); + + return receipt; + } + + /** + * @dev calculates the max payment for an upkeep. Called during checkUpkeep simulation and assumes + * maximum gas overhead, L1 fee + */ + function _getMaxPayment( + uint256 upkeepId, + HotVars memory hotVars, + Trigger triggerType, + uint32 performGas, + uint256 fastGasWei, + uint256 linkUSD, + uint256 nativeUSD, + IERC20 billingToken + ) internal view returns (uint96) { + uint256 maxGasOverhead; + + { + if (triggerType == Trigger.CONDITION) { + maxGasOverhead = REGISTRY_CONDITIONAL_OVERHEAD; + } else if (triggerType == Trigger.LOG) { + maxGasOverhead = REGISTRY_LOG_OVERHEAD; + } else { + revert InvalidTriggerType(); + } + (uint256 chainModuleFixedOverhead, ) = s_hotVars.chainModule.getGasOverhead(); + maxGasOverhead += (REGISTRY_PER_SIGNER_GAS_OVERHEAD * (hotVars.f + 1)) + chainModuleFixedOverhead; + } + + BillingTokenPaymentParams memory paymentParams = _getBillingTokenPaymentParams(hotVars, billingToken); + if (s_upkeep[upkeepId].overridesEnabled) { + BillingOverrides memory billingOverrides = s_billingOverrides[upkeepId]; + // use the overridden configs + paymentParams.gasFeePPB = billingOverrides.gasFeePPB; + paymentParams.flatFeeMilliCents = billingOverrides.flatFeeMilliCents; + } + + PaymentReceipt memory receipt = _calculatePaymentAmount( + hotVars, + PaymentParams({ + gasLimit: performGas, + gasOverhead: maxGasOverhead, + l1CostWei: 0, + fastGasWei: fastGasWei, + linkUSD: linkUSD, + nativeUSD: nativeUSD, + billingToken: billingToken, + billingTokenParams: paymentParams, + isTransaction: false + }) + ); + + return receipt.gasChargeInBillingToken + receipt.premiumInBillingToken; + } + + /** + * @dev move a transmitter's balance from total pool to withdrawable balance + */ + function _updateTransmitterBalanceFromPool( + address transmitterAddress, + uint96 totalPremium, + uint96 payeeCount + ) internal returns (uint96) { + Transmitter memory transmitter = s_transmitters[transmitterAddress]; + + if (transmitter.active) { + uint96 uncollected = totalPremium - transmitter.lastCollected; + uint96 due = uncollected / payeeCount; + transmitter.balance += due; + transmitter.lastCollected += due * payeeCount; + s_transmitters[transmitterAddress] = transmitter; + } + + return transmitter.balance; + } + + /** + * @dev gets the trigger type from an upkeepID (trigger type is encoded in the middle of the ID) + */ + function _getTriggerType(uint256 upkeepId) internal pure returns (Trigger) { + bytes32 rawID = bytes32(upkeepId); + bytes1 empty = bytes1(0); + for (uint256 idx = 4; idx < 15; idx++) { + if (rawID[idx] != empty) { + // old IDs that were created before this standard and migrated to this registry + return Trigger.CONDITION; + } + } + return Trigger(uint8(rawID[15])); + } + + function _checkPayload( + uint256 upkeepId, + Trigger triggerType, + bytes memory triggerData + ) internal view returns (bytes memory) { + if (triggerType == Trigger.CONDITION) { + return abi.encodeWithSelector(CHECK_SELECTOR, s_checkData[upkeepId]); + } else if (triggerType == Trigger.LOG) { + Log memory log = abi.decode(triggerData, (Log)); + return abi.encodeWithSelector(CHECK_LOG_SELECTOR, log, s_checkData[upkeepId]); + } + revert InvalidTriggerType(); + } + + /** + * @dev _decodeReport decodes a serialized report into a Report struct + */ + function _decodeReport(bytes calldata rawReport) internal pure returns (Report memory) { + Report memory report = abi.decode(rawReport, (Report)); + uint256 expectedLength = report.upkeepIds.length; + if ( + report.gasLimits.length != expectedLength || + report.triggers.length != expectedLength || + report.performDatas.length != expectedLength + ) { + revert InvalidReport(); + } + return report; + } + + /** + * @dev Does some early sanity checks before actually performing an upkeep + * @return bool whether the upkeep should be performed + * @return bytes32 dedupID for preventing duplicate performances of this trigger + */ + function _prePerformChecks( + uint256 upkeepId, + uint256 blocknumber, + bytes memory rawTrigger, + UpkeepTransmitInfo memory transmitInfo, + HotVars memory hotVars + ) internal returns (bool, bytes32) { + bytes32 dedupID; + if (transmitInfo.triggerType == Trigger.CONDITION) { + if (!_validateConditionalTrigger(upkeepId, blocknumber, rawTrigger, transmitInfo, hotVars)) + return (false, dedupID); + } else if (transmitInfo.triggerType == Trigger.LOG) { + bool valid; + (valid, dedupID) = _validateLogTrigger(upkeepId, blocknumber, rawTrigger, hotVars); + if (!valid) return (false, dedupID); + } else { + revert InvalidTriggerType(); + } + if (transmitInfo.upkeep.maxValidBlocknumber <= blocknumber) { + // Can happen when an upkeep got cancelled after report was generated. + // However we have a CANCELLATION_DELAY of 50 blocks so shouldn't happen in practice + emit CancelledUpkeepReport(upkeepId, rawTrigger); + return (false, dedupID); + } + return (true, dedupID); + } + + /** + * @dev Does some early sanity checks before actually performing an upkeep + */ + function _validateConditionalTrigger( + uint256 upkeepId, + uint256 blocknumber, + bytes memory rawTrigger, + UpkeepTransmitInfo memory transmitInfo, + HotVars memory hotVars + ) internal returns (bool) { + ConditionalTrigger memory trigger = abi.decode(rawTrigger, (ConditionalTrigger)); + if (trigger.blockNum < transmitInfo.upkeep.lastPerformedBlockNumber) { + // Can happen when another report performed this upkeep after this report was generated + emit StaleUpkeepReport(upkeepId, rawTrigger); + return false; + } + if ( + (hotVars.reorgProtectionEnabled && + (trigger.blockHash != bytes32("") && hotVars.chainModule.blockHash(trigger.blockNum) != trigger.blockHash)) || + trigger.blockNum >= blocknumber + ) { + // There are two cases of reorged report + // 1. trigger block number is in future: this is an edge case during extreme deep reorgs of chain + // which is always protected against + // 2. blockHash at trigger block number was same as trigger time. This is an optional check which is + // applied if DON sends non empty trigger.blockHash. Note: It only works for last 256 blocks on chain + // when it is sent + emit ReorgedUpkeepReport(upkeepId, rawTrigger); + return false; + } + return true; + } + + function _validateLogTrigger( + uint256 upkeepId, + uint256 blocknumber, + bytes memory rawTrigger, + HotVars memory hotVars + ) internal returns (bool, bytes32) { + LogTrigger memory trigger = abi.decode(rawTrigger, (LogTrigger)); + bytes32 dedupID = keccak256(abi.encodePacked(upkeepId, trigger.logBlockHash, trigger.txHash, trigger.logIndex)); + if ( + (hotVars.reorgProtectionEnabled && + (trigger.blockHash != bytes32("") && hotVars.chainModule.blockHash(trigger.blockNum) != trigger.blockHash)) || + trigger.blockNum >= blocknumber + ) { + // Reorg protection is same as conditional trigger upkeeps + emit ReorgedUpkeepReport(upkeepId, rawTrigger); + return (false, dedupID); + } + if (s_dedupKeys[dedupID]) { + emit StaleUpkeepReport(upkeepId, rawTrigger); + return (false, dedupID); + } + return (true, dedupID); + } + + /** + * @dev Verify signatures attached to report + */ + function _verifyReportSignature( + bytes32[3] calldata reportContext, + bytes calldata report, + bytes32[] calldata rs, + bytes32[] calldata ss, + bytes32 rawVs + ) internal view { + bytes32 h = keccak256(abi.encode(keccak256(report), reportContext)); + // i-th byte counts number of sigs made by i-th signer + uint256 signedCount = 0; + + Signer memory signer; + address signerAddress; + for (uint256 i = 0; i < rs.length; i++) { + signerAddress = ecrecover(h, uint8(rawVs[i]) + 27, rs[i], ss[i]); + signer = s_signers[signerAddress]; + if (!signer.active) revert OnlyActiveSigners(); + unchecked { + signedCount += 1 << (8 * signer.index); + } + } + + if (signedCount & ORACLE_MASK != signedCount) revert DuplicateSigners(); + } + + /** + * @dev updates a storage marker for this upkeep to prevent duplicate and out of order performances + * @dev for conditional triggers we set the latest block number, for log triggers we store a dedupID + */ + function _updateTriggerMarker( + uint256 upkeepID, + uint256 blocknumber, + UpkeepTransmitInfo memory upkeepTransmitInfo + ) internal { + if (upkeepTransmitInfo.triggerType == Trigger.CONDITION) { + s_upkeep[upkeepID].lastPerformedBlockNumber = uint32(blocknumber); + } else if (upkeepTransmitInfo.triggerType == Trigger.LOG) { + s_dedupKeys[upkeepTransmitInfo.dedupID] = true; + emit DedupKeyAdded(upkeepTransmitInfo.dedupID); + } + } + + /** + * @dev calls the Upkeep target with the performData param passed in by the + * transmitter and the exact gas required by the Upkeep + */ + function _performUpkeep( + IAutomationForwarder forwarder, + uint256 performGas, + bytes memory performData + ) internal nonReentrant returns (bool success, uint256 gasUsed) { + performData = abi.encodeWithSelector(PERFORM_SELECTOR, performData); + return forwarder.forward(performGas, performData); + } + + /** + * @dev handles the payment processing after an upkeep has been performed. + * Deducts an upkeep's balance and increases the amount spent. + */ + function _handlePayment( + HotVars memory hotVars, + PaymentParams memory paymentParams, + uint256 upkeepId, + Upkeep memory upkeep + ) internal returns (PaymentReceipt memory) { + if (upkeep.overridesEnabled) { + BillingOverrides memory billingOverrides = s_billingOverrides[upkeepId]; + // use the overridden configs + paymentParams.billingTokenParams.gasFeePPB = billingOverrides.gasFeePPB; + paymentParams.billingTokenParams.flatFeeMilliCents = billingOverrides.flatFeeMilliCents; + } + + PaymentReceipt memory receipt = _calculatePaymentAmount(hotVars, paymentParams); + + // balance is in the token's native decimals + uint96 balance = upkeep.balance; + // payment is in the token's native decimals + uint96 payment = receipt.gasChargeInBillingToken + receipt.premiumInBillingToken; + + // scaling factors to adjust decimals between billing token and LINK + uint256 decimals = paymentParams.billingTokenParams.decimals; + uint256 scalingFactor1 = decimals < 18 ? 10 ** (18 - decimals) : 1; + uint256 scalingFactor2 = decimals > 18 ? 10 ** (decimals - 18) : 1; + + // this shouldn't happen, but in rare edge cases, we charge the full balance in case the user + // can't cover the amount owed + if (balance < receipt.gasChargeInBillingToken) { + // if the user can't cover the gas fee, then direct all of the payment to the transmitter and distribute no premium to the DON + payment = balance; + receipt.gasReimbursementInJuels = SafeCast.toUint96( + (balance * paymentParams.billingTokenParams.priceUSD * scalingFactor1) / + (paymentParams.linkUSD * scalingFactor2) + ); + receipt.premiumInJuels = 0; + receipt.premiumInBillingToken = 0; + receipt.gasChargeInBillingToken = balance; + } else if (balance < payment) { + // if the user can cover the gas fee, but not the premium, then reduce the premium + payment = balance; + receipt.premiumInJuels = SafeCast.toUint96( + ((balance * paymentParams.billingTokenParams.priceUSD * scalingFactor1) / + (paymentParams.linkUSD * scalingFactor2)) - receipt.gasReimbursementInJuels + ); + // round up + receipt.premiumInBillingToken = SafeCast.toUint96( + ((receipt.premiumInJuels * paymentParams.linkUSD * scalingFactor2) + + (paymentParams.billingTokenParams.priceUSD * scalingFactor1 - 1)) / + (paymentParams.billingTokenParams.priceUSD * scalingFactor1) + ); + } + + s_upkeep[upkeepId].balance -= payment; + s_upkeep[upkeepId].amountSpent += payment; + s_reserveAmounts[paymentParams.billingToken] -= payment; + + emit UpkeepCharged(upkeepId, receipt); + return receipt; + } + + /** + * @dev ensures the upkeep is not cancelled and the caller is the upkeep admin + */ + function _requireAdminAndNotCancelled(uint256 upkeepId) internal view { + if (msg.sender != s_upkeepAdmin[upkeepId]) revert OnlyCallableByAdmin(); + if (s_upkeep[upkeepId].maxValidBlocknumber != UINT32_MAX) revert UpkeepCancelled(); + } + + /** + * @dev replicates Open Zeppelin's ReentrancyGuard but optimized to fit our storage + */ + modifier nonReentrant() { + if (s_hotVars.reentrancyGuard) revert ReentrantCall(); + s_hotVars.reentrancyGuard = true; + _; + s_hotVars.reentrancyGuard = false; + } + + /** + * @notice only allows a pre-configured address to initiate offchain read + */ + function _preventExecution() internal view { + // solhint-disable-next-line avoid-tx-origin + if (tx.origin != i_allowedReadOnlyAddress) { + revert OnlySimulatedBackend(); + } + } + + /** + * @notice only allows finance admin to call the function + */ + function _onlyFinanceAdminAllowed() internal view { + if (msg.sender != s_storage.financeAdmin) { + revert OnlyFinanceAdmin(); + } + } + + /** + * @notice only allows privilege manager to call the function + */ + function _onlyPrivilegeManagerAllowed() internal view { + if (msg.sender != s_storage.upkeepPrivilegeManager) { + revert OnlyCallableByUpkeepPrivilegeManager(); + } + } + + /** + * @notice sets billing configuration for a token + * @param billingTokens the addresses of tokens + * @param billingConfigs the configs for tokens + */ + function _setBillingConfig(IERC20[] memory billingTokens, BillingConfig[] memory billingConfigs) internal { + // Clear existing data + for (uint256 i = 0; i < s_billingTokens.length; i++) { + delete s_billingConfigs[s_billingTokens[i]]; + } + delete s_billingTokens; + + PayoutMode mode = s_payoutMode; + for (uint256 i = 0; i < billingTokens.length; i++) { + IERC20 token = billingTokens[i]; + BillingConfig memory config = billingConfigs[i]; + + // most ERC20 tokens are 18 decimals, priceFeed must be 8 decimals + if (config.decimals != token.decimals() || config.priceFeed.decimals() != 8) { + revert InvalidToken(); + } + + // if LINK is a billing option, payout mode must be ON_CHAIN + if (address(token) == address(i_link) && mode == PayoutMode.OFF_CHAIN) { + revert InvalidToken(); + } + if (address(token) == ZERO_ADDRESS || address(config.priceFeed) == ZERO_ADDRESS) { + revert ZeroAddressNotAllowed(); + } + + // if this is a new token, add it to tokens list. Otherwise revert + if (address(s_billingConfigs[token].priceFeed) != ZERO_ADDRESS) { + revert DuplicateEntry(); + } + s_billingTokens.push(token); + + // update the billing config for an existing token or add a new one + s_billingConfigs[token] = config; + + emit BillingConfigSet(token, config); + } + } + + /** + * @notice updates the signers and transmitters lists + */ + function _updateTransmitters(address[] memory signers, address[] memory transmitters) internal { + uint96 transmittersListLength = uint96(s_transmittersList.length); + uint96 totalPremium = s_hotVars.totalPremium; + + // move all pooled payments out of the pool to each transmitter's balance + for (uint256 i = 0; i < s_transmittersList.length; i++) { + _updateTransmitterBalanceFromPool(s_transmittersList[i], totalPremium, transmittersListLength); + } + + // remove any old signer/transmitter addresses + address transmitterAddress; + PayoutMode mode = s_payoutMode; + for (uint256 i = 0; i < s_transmittersList.length; i++) { + transmitterAddress = s_transmittersList[i]; + delete s_signers[s_signersList[i]]; + // Do not delete the whole transmitter struct as it has balance information stored + s_transmitters[transmitterAddress].active = false; + if (mode == PayoutMode.OFF_CHAIN && s_transmitters[transmitterAddress].balance > 0) { + s_deactivatedTransmitters.add(transmitterAddress); + } + } + delete s_signersList; + delete s_transmittersList; + + // add new signer/transmitter addresses + Transmitter memory transmitter; + for (uint256 i = 0; i < signers.length; i++) { + if (s_signers[signers[i]].active) revert RepeatedSigner(); + if (signers[i] == ZERO_ADDRESS) revert InvalidSigner(); + s_signers[signers[i]] = Signer({active: true, index: uint8(i)}); + + transmitterAddress = transmitters[i]; + if (transmitterAddress == ZERO_ADDRESS) revert InvalidTransmitter(); + transmitter = s_transmitters[transmitterAddress]; + if (transmitter.active) revert RepeatedTransmitter(); + transmitter.active = true; + transmitter.index = uint8(i); + // new transmitters start afresh from current totalPremium + // some spare change of premium from previous pool will be forfeited + transmitter.lastCollected = s_hotVars.totalPremium; + s_transmitters[transmitterAddress] = transmitter; + if (mode == PayoutMode.OFF_CHAIN) { + s_deactivatedTransmitters.remove(transmitterAddress); + } + } + + s_signersList = signers; + s_transmittersList = transmitters; + } + + /** + * @notice returns the size of the LINK liquidity pool + # @dev LINK max supply < 2^96, so casting to int256 is safe + */ + function _linkAvailableForPayment() internal view returns (int256) { + return int256(i_link.balanceOf(address(this))) - int256(s_reserveAmounts[IERC20(address(i_link))]); + } +} diff --git a/contracts/src/v0.8/automation/v2_3_zksync/ZKSyncAutomationRegistryLogicA2_3.sol b/contracts/src/v0.8/automation/v2_3_zksync/ZKSyncAutomationRegistryLogicA2_3.sol new file mode 100644 index 0000000000..64d697c70f --- /dev/null +++ b/contracts/src/v0.8/automation/v2_3_zksync/ZKSyncAutomationRegistryLogicA2_3.sol @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.19; + +import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/structs/EnumerableSet.sol"; +import {Address} from "../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/Address.sol"; +import {ZKSyncAutomationRegistryBase2_3} from "./ZKSyncAutomationRegistryBase2_3.sol"; +import {ZKSyncAutomationRegistryLogicC2_3} from "./ZKSyncAutomationRegistryLogicC2_3.sol"; +import {ZKSyncAutomationRegistryLogicB2_3} from "./ZKSyncAutomationRegistryLogicB2_3.sol"; +import {Chainable} from "../Chainable.sol"; +import {ZKSyncAutomationForwarder} from "../ZKSyncAutomationForwarder.sol"; +import {IAutomationForwarder} from "../interfaces/IAutomationForwarder.sol"; +import {UpkeepTranscoderInterfaceV2} from "../interfaces/UpkeepTranscoderInterfaceV2.sol"; +import {MigratableKeeperRegistryInterfaceV2} from "../interfaces/MigratableKeeperRegistryInterfaceV2.sol"; +import {IERC20Metadata as IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; +import {IERC677Receiver} from "../../shared/interfaces/IERC677Receiver.sol"; + +/** + * @notice Logic contract, works in tandem with AutomationRegistry as a proxy + */ +contract ZKSyncAutomationRegistryLogicA2_3 is ZKSyncAutomationRegistryBase2_3, Chainable, IERC677Receiver { + using Address for address; + using EnumerableSet for EnumerableSet.UintSet; + using EnumerableSet for EnumerableSet.AddressSet; + using SafeERC20 for IERC20; + + /** + * @param logicB the address of the second logic contract + * @dev we cast the contract to logicC in order to call logicC functions (via fallback) + */ + constructor( + ZKSyncAutomationRegistryLogicB2_3 logicB + ) + ZKSyncAutomationRegistryBase2_3( + ZKSyncAutomationRegistryLogicC2_3(address(logicB)).getLinkAddress(), + ZKSyncAutomationRegistryLogicC2_3(address(logicB)).getLinkUSDFeedAddress(), + ZKSyncAutomationRegistryLogicC2_3(address(logicB)).getNativeUSDFeedAddress(), + ZKSyncAutomationRegistryLogicC2_3(address(logicB)).getFastGasFeedAddress(), + ZKSyncAutomationRegistryLogicC2_3(address(logicB)).getAutomationForwarderLogic(), + ZKSyncAutomationRegistryLogicC2_3(address(logicB)).getAllowedReadOnlyAddress(), + ZKSyncAutomationRegistryLogicC2_3(address(logicB)).getPayoutMode(), + ZKSyncAutomationRegistryLogicC2_3(address(logicB)).getWrappedNativeTokenAddress() + ) + Chainable(address(logicB)) + {} + + /** + * @notice uses LINK's transferAndCall to LINK and add funding to an upkeep + * @dev safe to cast uint256 to uint96 as total LINK supply is under UINT96MAX + * @param sender the account which transferred the funds + * @param amount number of LINK transfer + */ + function onTokenTransfer(address sender, uint256 amount, bytes calldata data) external override { + if (msg.sender != address(i_link)) revert OnlyCallableByLINKToken(); + if (data.length != 32) revert InvalidDataLength(); + uint256 id = abi.decode(data, (uint256)); + if (s_upkeep[id].maxValidBlocknumber != UINT32_MAX) revert UpkeepCancelled(); + if (address(s_upkeep[id].billingToken) != address(i_link)) revert InvalidToken(); + s_upkeep[id].balance = s_upkeep[id].balance + uint96(amount); + s_reserveAmounts[IERC20(address(i_link))] = s_reserveAmounts[IERC20(address(i_link))] + amount; + emit FundsAdded(id, sender, uint96(amount)); + } + + // ================================================================ + // | UPKEEP MANAGEMENT | + // ================================================================ + + /** + * @notice adds a new upkeep + * @param target address to perform upkeep on + * @param gasLimit amount of gas to provide the target contract when + * performing upkeep + * @param admin address to cancel upkeep and withdraw remaining funds + * @param triggerType the trigger for the upkeep + * @param billingToken the billing token for the upkeep + * @param checkData data passed to the contract when checking for upkeep + * @param triggerConfig the config for the trigger + * @param offchainConfig arbitrary offchain config for the upkeep + */ + function registerUpkeep( + address target, + uint32 gasLimit, + address admin, + Trigger triggerType, + IERC20 billingToken, + bytes calldata checkData, + bytes memory triggerConfig, + bytes memory offchainConfig + ) public returns (uint256 id) { + if (msg.sender != owner() && !s_registrars.contains(msg.sender)) revert OnlyCallableByOwnerOrRegistrar(); + if (!target.isContract()) revert NotAContract(); + id = _createID(triggerType); + IAutomationForwarder forwarder = IAutomationForwarder( + address(new ZKSyncAutomationForwarder(target, address(this), i_automationForwarderLogic)) + ); + _createUpkeep( + id, + Upkeep({ + overridesEnabled: false, + performGas: gasLimit, + balance: 0, + maxValidBlocknumber: UINT32_MAX, + lastPerformedBlockNumber: 0, + amountSpent: 0, + paused: false, + forwarder: forwarder, + billingToken: billingToken + }), + admin, + checkData, + triggerConfig, + offchainConfig + ); + s_storage.nonce++; + emit UpkeepRegistered(id, gasLimit, admin); + emit UpkeepCheckDataSet(id, checkData); + emit UpkeepTriggerConfigSet(id, triggerConfig); + emit UpkeepOffchainConfigSet(id, offchainConfig); + return (id); + } + + /** + * @notice cancels an upkeep + * @param id the upkeepID to cancel + * @dev if a user cancels an upkeep, their funds are locked for CANCELLATION_DELAY blocks to + * allow any pending performUpkeep txs time to get confirmed + */ + function cancelUpkeep(uint256 id) external { + Upkeep memory upkeep = s_upkeep[id]; + bool isOwner = msg.sender == owner(); + uint96 minSpend = s_billingConfigs[upkeep.billingToken].minSpend; + + uint256 height = s_hotVars.chainModule.blockNumber(); + if (upkeep.maxValidBlocknumber == 0) revert CannotCancel(); + if (upkeep.maxValidBlocknumber != UINT32_MAX) revert UpkeepCancelled(); + if (!isOwner && msg.sender != s_upkeepAdmin[id]) revert OnlyCallableByOwnerOrAdmin(); + + if (!isOwner) { + height = height + CANCELLATION_DELAY; + } + s_upkeep[id].maxValidBlocknumber = uint32(height); + s_upkeepIDs.remove(id); + + // charge the cancellation fee if the minSpend is not met + uint96 cancellationFee = 0; + // cancellationFee is min(max(minSpend - amountSpent, 0), amountLeft) + if (upkeep.amountSpent < minSpend) { + cancellationFee = minSpend - uint96(upkeep.amountSpent); + if (cancellationFee > upkeep.balance) { + cancellationFee = upkeep.balance; + } + } + s_upkeep[id].balance = upkeep.balance - cancellationFee; + s_reserveAmounts[upkeep.billingToken] = s_reserveAmounts[upkeep.billingToken] - cancellationFee; + + emit UpkeepCanceled(id, uint64(height)); + } + + /** + * @notice migrates upkeeps from one registry to another. + * @param ids the upkeepIDs to migrate + * @param destination the destination registry address + * @dev a transcoder must be set in order to enable migration + * @dev migration permissions must be set on *both* sending and receiving registries + * @dev only an upkeep admin can migrate their upkeeps + * @dev this function is most gas-efficient if upkeepIDs are sorted by billing token + * @dev s_billingOverrides and s_upkeepPrivilegeConfig are not migrated in this function + */ + function migrateUpkeeps(uint256[] calldata ids, address destination) external { + if ( + s_peerRegistryMigrationPermission[destination] != MigrationPermission.OUTGOING && + s_peerRegistryMigrationPermission[destination] != MigrationPermission.BIDIRECTIONAL + ) revert MigrationNotPermitted(); + if (s_storage.transcoder == ZERO_ADDRESS) revert TranscoderNotSet(); + if (ids.length == 0) revert ArrayHasNoEntries(); + + IERC20 billingToken; + uint256 balanceToTransfer; + uint256 id; + Upkeep memory upkeep; + address[] memory admins = new address[](ids.length); + Upkeep[] memory upkeeps = new Upkeep[](ids.length); + bytes[] memory checkDatas = new bytes[](ids.length); + bytes[] memory triggerConfigs = new bytes[](ids.length); + bytes[] memory offchainConfigs = new bytes[](ids.length); + + for (uint256 idx = 0; idx < ids.length; idx++) { + id = ids[idx]; + upkeep = s_upkeep[id]; + + if (idx == 0) { + billingToken = upkeep.billingToken; + balanceToTransfer = upkeep.balance; + } + + // if we encounter a new billing token, send the sum from the last billing token to the destination registry + if (upkeep.billingToken != billingToken) { + s_reserveAmounts[billingToken] = s_reserveAmounts[billingToken] - balanceToTransfer; + billingToken.safeTransfer(destination, balanceToTransfer); + billingToken = upkeep.billingToken; + balanceToTransfer = upkeep.balance; + } else if (idx != 0) { + balanceToTransfer += upkeep.balance; + } + + _requireAdminAndNotCancelled(id); + upkeep.forwarder.updateRegistry(destination); + + upkeeps[idx] = upkeep; + admins[idx] = s_upkeepAdmin[id]; + checkDatas[idx] = s_checkData[id]; + triggerConfigs[idx] = s_upkeepTriggerConfig[id]; + offchainConfigs[idx] = s_upkeepOffchainConfig[id]; + delete s_upkeep[id]; + delete s_checkData[id]; + delete s_upkeepTriggerConfig[id]; + delete s_upkeepOffchainConfig[id]; + // nullify existing proposed admin change if an upkeep is being migrated + delete s_proposedAdmin[id]; + delete s_upkeepAdmin[id]; + s_upkeepIDs.remove(id); + emit UpkeepMigrated(id, upkeep.balance, destination); + } + // always transfer the rolling sum in the end + s_reserveAmounts[billingToken] = s_reserveAmounts[billingToken] - balanceToTransfer; + billingToken.safeTransfer(destination, balanceToTransfer); + + bytes memory encodedUpkeeps = abi.encode( + ids, + upkeeps, + new address[](ids.length), + admins, + checkDatas, + triggerConfigs, + offchainConfigs + ); + MigratableKeeperRegistryInterfaceV2(destination).receiveUpkeeps( + UpkeepTranscoderInterfaceV2(s_storage.transcoder).transcodeUpkeeps( + UPKEEP_VERSION_BASE, + MigratableKeeperRegistryInterfaceV2(destination).upkeepVersion(), + encodedUpkeeps + ) + ); + } + + /** + * @notice received upkeeps migrated from another registry + * @param encodedUpkeeps the raw upkeep data to import + * @dev this function is never called directly, it is only called by another registry's migrate function + * @dev s_billingOverrides and s_upkeepPrivilegeConfig are not handled in this function + */ + function receiveUpkeeps(bytes calldata encodedUpkeeps) external { + if ( + s_peerRegistryMigrationPermission[msg.sender] != MigrationPermission.INCOMING && + s_peerRegistryMigrationPermission[msg.sender] != MigrationPermission.BIDIRECTIONAL + ) revert MigrationNotPermitted(); + ( + uint256[] memory ids, + Upkeep[] memory upkeeps, + address[] memory targets, + address[] memory upkeepAdmins, + bytes[] memory checkDatas, + bytes[] memory triggerConfigs, + bytes[] memory offchainConfigs + ) = abi.decode(encodedUpkeeps, (uint256[], Upkeep[], address[], address[], bytes[], bytes[], bytes[])); + for (uint256 idx = 0; idx < ids.length; idx++) { + if (address(upkeeps[idx].forwarder) == ZERO_ADDRESS) { + upkeeps[idx].forwarder = IAutomationForwarder( + address(new ZKSyncAutomationForwarder(targets[idx], address(this), i_automationForwarderLogic)) + ); + } + _createUpkeep( + ids[idx], + upkeeps[idx], + upkeepAdmins[idx], + checkDatas[idx], + triggerConfigs[idx], + offchainConfigs[idx] + ); + emit UpkeepReceived(ids[idx], upkeeps[idx].balance, msg.sender); + } + } +} diff --git a/contracts/src/v0.8/automation/v2_3_zksync/ZKSyncAutomationRegistryLogicB2_3.sol b/contracts/src/v0.8/automation/v2_3_zksync/ZKSyncAutomationRegistryLogicB2_3.sol new file mode 100644 index 0000000000..55af99fde8 --- /dev/null +++ b/contracts/src/v0.8/automation/v2_3_zksync/ZKSyncAutomationRegistryLogicB2_3.sol @@ -0,0 +1,449 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.19; + +import {ZKSyncAutomationRegistryBase2_3} from "./ZKSyncAutomationRegistryBase2_3.sol"; +import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/structs/EnumerableSet.sol"; +import {Address} from "../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/Address.sol"; +import {ZKSyncAutomationRegistryLogicC2_3} from "./ZKSyncAutomationRegistryLogicC2_3.sol"; +import {Chainable} from "../Chainable.sol"; +import {IERC20Metadata as IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; +import {SafeCast} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/SafeCast.sol"; + +contract ZKSyncAutomationRegistryLogicB2_3 is ZKSyncAutomationRegistryBase2_3, Chainable { + using Address for address; + using EnumerableSet for EnumerableSet.UintSet; + using EnumerableSet for EnumerableSet.AddressSet; + using SafeERC20 for IERC20; + + /** + * @param logicC the address of the third logic contract + */ + constructor( + ZKSyncAutomationRegistryLogicC2_3 logicC + ) + ZKSyncAutomationRegistryBase2_3( + logicC.getLinkAddress(), + logicC.getLinkUSDFeedAddress(), + logicC.getNativeUSDFeedAddress(), + logicC.getFastGasFeedAddress(), + logicC.getAutomationForwarderLogic(), + logicC.getAllowedReadOnlyAddress(), + logicC.getPayoutMode(), + logicC.getWrappedNativeTokenAddress() + ) + Chainable(address(logicC)) + {} + + // ================================================================ + // | PIPELINE FUNCTIONS | + // ================================================================ + + /** + * @notice called by the automation DON to check if work is needed + * @param id the upkeep ID to check for work needed + * @param triggerData extra contextual data about the trigger (not used in all code paths) + * @dev this one of the core functions called in the hot path + * @dev there is a 2nd checkUpkeep function (below) that is being maintained for backwards compatibility + * @dev there is an incongruency on what gets returned during failure modes + * ex sometimes we include price data, sometimes we omit it depending on the failure + */ + function checkUpkeep( + uint256 id, + bytes memory triggerData + ) + public + returns ( + bool upkeepNeeded, + bytes memory performData, + UpkeepFailureReason upkeepFailureReason, + uint256 gasUsed, + uint256 gasLimit, + uint256 fastGasWei, + uint256 linkUSD + ) + { + _preventExecution(); + + Trigger triggerType = _getTriggerType(id); + HotVars memory hotVars = s_hotVars; + Upkeep memory upkeep = s_upkeep[id]; + + { + uint256 nativeUSD; + uint96 maxPayment; + if (hotVars.paused) return (false, bytes(""), UpkeepFailureReason.REGISTRY_PAUSED, 0, upkeep.performGas, 0, 0); + if (upkeep.maxValidBlocknumber != UINT32_MAX) + return (false, bytes(""), UpkeepFailureReason.UPKEEP_CANCELLED, 0, upkeep.performGas, 0, 0); + if (upkeep.paused) return (false, bytes(""), UpkeepFailureReason.UPKEEP_PAUSED, 0, upkeep.performGas, 0, 0); + (fastGasWei, linkUSD, nativeUSD) = _getFeedData(hotVars); + maxPayment = _getMaxPayment( + id, + hotVars, + triggerType, + upkeep.performGas, + fastGasWei, + linkUSD, + nativeUSD, + upkeep.billingToken + ); + if (upkeep.balance < maxPayment) { + return (false, bytes(""), UpkeepFailureReason.INSUFFICIENT_BALANCE, 0, upkeep.performGas, 0, 0); + } + } + + bytes memory callData = _checkPayload(id, triggerType, triggerData); + + gasUsed = gasleft(); + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory result) = upkeep.forwarder.getTarget().call{gas: s_storage.checkGasLimit}(callData); + gasUsed = gasUsed - gasleft(); + + if (!success) { + // User's target check reverted. We capture the revert data here and pass it within performData + if (result.length > s_storage.maxRevertDataSize) { + return ( + false, + bytes(""), + UpkeepFailureReason.REVERT_DATA_EXCEEDS_LIMIT, + gasUsed, + upkeep.performGas, + fastGasWei, + linkUSD + ); + } + return ( + upkeepNeeded, + result, + UpkeepFailureReason.TARGET_CHECK_REVERTED, + gasUsed, + upkeep.performGas, + fastGasWei, + linkUSD + ); + } + + (upkeepNeeded, performData) = abi.decode(result, (bool, bytes)); + if (!upkeepNeeded) + return (false, bytes(""), UpkeepFailureReason.UPKEEP_NOT_NEEDED, gasUsed, upkeep.performGas, fastGasWei, linkUSD); + + if (performData.length > s_storage.maxPerformDataSize) + return ( + false, + bytes(""), + UpkeepFailureReason.PERFORM_DATA_EXCEEDS_LIMIT, + gasUsed, + upkeep.performGas, + fastGasWei, + linkUSD + ); + + return (upkeepNeeded, performData, upkeepFailureReason, gasUsed, upkeep.performGas, fastGasWei, linkUSD); + } + + /** + * @notice see other checkUpkeep function for description + * @dev this function may be deprecated in a future version of chainlink automation + */ + function checkUpkeep( + uint256 id + ) + external + returns ( + bool upkeepNeeded, + bytes memory performData, + UpkeepFailureReason upkeepFailureReason, + uint256 gasUsed, + uint256 gasLimit, + uint256 fastGasWei, + uint256 linkUSD + ) + { + return checkUpkeep(id, bytes("")); + } + + /** + * @dev checkCallback is used specifically for automation data streams lookups (see StreamsLookupCompatibleInterface.sol) + * @param id the upkeepID to execute a callback for + * @param values the values returned from the data streams lookup + * @param extraData the user-provided extra context data + */ + function checkCallback( + uint256 id, + bytes[] memory values, + bytes calldata extraData + ) + external + returns (bool upkeepNeeded, bytes memory performData, UpkeepFailureReason upkeepFailureReason, uint256 gasUsed) + { + bytes memory payload = abi.encodeWithSelector(CHECK_CALLBACK_SELECTOR, values, extraData); + return executeCallback(id, payload); + } + + /** + * @notice this is a generic callback executor that forwards a call to a user's contract with the configured + * gas limit + * @param id the upkeepID to execute a callback for + * @param payload the data (including function selector) to call on the upkeep target contract + */ + function executeCallback( + uint256 id, + bytes memory payload + ) + public + returns (bool upkeepNeeded, bytes memory performData, UpkeepFailureReason upkeepFailureReason, uint256 gasUsed) + { + _preventExecution(); + + Upkeep memory upkeep = s_upkeep[id]; + gasUsed = gasleft(); + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory result) = upkeep.forwarder.getTarget().call{gas: s_storage.checkGasLimit}(payload); + gasUsed = gasUsed - gasleft(); + if (!success) { + return (false, bytes(""), UpkeepFailureReason.CALLBACK_REVERTED, gasUsed); + } + (upkeepNeeded, performData) = abi.decode(result, (bool, bytes)); + if (!upkeepNeeded) { + return (false, bytes(""), UpkeepFailureReason.UPKEEP_NOT_NEEDED, gasUsed); + } + if (performData.length > s_storage.maxPerformDataSize) { + return (false, bytes(""), UpkeepFailureReason.PERFORM_DATA_EXCEEDS_LIMIT, gasUsed); + } + return (upkeepNeeded, performData, upkeepFailureReason, gasUsed); + } + + /** + * @notice simulates the upkeep with the perform data returned from checkUpkeep + * @param id identifier of the upkeep to execute the data with. + * @param performData calldata parameter to be passed to the target upkeep. + * @return success whether the call reverted or not + * @return gasUsed the amount of gas the target contract consumed + */ + function simulatePerformUpkeep( + uint256 id, + bytes calldata performData + ) external returns (bool success, uint256 gasUsed) { + _preventExecution(); + + if (s_hotVars.paused) revert RegistryPaused(); + Upkeep memory upkeep = s_upkeep[id]; + (success, gasUsed) = _performUpkeep(upkeep.forwarder, upkeep.performGas, performData); + return (success, gasUsed); + } + + // ================================================================ + // | UPKEEP MANAGEMENT | + // ================================================================ + + /** + * @notice adds fund to an upkeep + * @param id the upkeepID + * @param amount the amount of funds to add, in the upkeep's billing token + */ + function addFunds(uint256 id, uint96 amount) external payable { + Upkeep memory upkeep = s_upkeep[id]; + if (upkeep.maxValidBlocknumber != UINT32_MAX) revert UpkeepCancelled(); + + if (msg.value != 0) { + if (upkeep.billingToken != IERC20(i_wrappedNativeToken)) { + revert InvalidToken(); + } + amount = SafeCast.toUint96(msg.value); + } + + s_upkeep[id].balance = upkeep.balance + amount; + s_reserveAmounts[upkeep.billingToken] = s_reserveAmounts[upkeep.billingToken] + amount; + + if (msg.value == 0) { + // ERC20 payment + upkeep.billingToken.safeTransferFrom(msg.sender, address(this), amount); + } else { + // native payment + i_wrappedNativeToken.deposit{value: amount}(); + } + + emit FundsAdded(id, msg.sender, amount); + } + + /** + * @notice overrides the billing config for an upkeep + * @param id the upkeepID + * @param billingOverrides the override-able billing config + */ + function setBillingOverrides(uint256 id, BillingOverrides calldata billingOverrides) external { + _onlyPrivilegeManagerAllowed(); + if (s_upkeep[id].maxValidBlocknumber != UINT32_MAX) revert UpkeepCancelled(); + + s_upkeep[id].overridesEnabled = true; + s_billingOverrides[id] = billingOverrides; + emit BillingConfigOverridden(id, billingOverrides); + } + + /** + * @notice remove the overridden billing config for an upkeep + * @param id the upkeepID + */ + function removeBillingOverrides(uint256 id) external { + _onlyPrivilegeManagerAllowed(); + + s_upkeep[id].overridesEnabled = false; + delete s_billingOverrides[id]; + emit BillingConfigOverrideRemoved(id); + } + + /** + * @notice transfers the address of an admin for an upkeep + */ + function transferUpkeepAdmin(uint256 id, address proposed) external { + _requireAdminAndNotCancelled(id); + if (proposed == msg.sender) revert ValueNotChanged(); + + if (s_proposedAdmin[id] != proposed) { + s_proposedAdmin[id] = proposed; + emit UpkeepAdminTransferRequested(id, msg.sender, proposed); + } + } + + /** + * @notice accepts the transfer of an upkeep admin + */ + function acceptUpkeepAdmin(uint256 id) external { + Upkeep memory upkeep = s_upkeep[id]; + if (upkeep.maxValidBlocknumber != UINT32_MAX) revert UpkeepCancelled(); + if (s_proposedAdmin[id] != msg.sender) revert OnlyCallableByProposedAdmin(); + address past = s_upkeepAdmin[id]; + s_upkeepAdmin[id] = msg.sender; + s_proposedAdmin[id] = ZERO_ADDRESS; + + emit UpkeepAdminTransferred(id, past, msg.sender); + } + + /** + * @notice pauses an upkeep - an upkeep will be neither checked nor performed while paused + */ + function pauseUpkeep(uint256 id) external { + _requireAdminAndNotCancelled(id); + Upkeep memory upkeep = s_upkeep[id]; + if (upkeep.paused) revert OnlyUnpausedUpkeep(); + s_upkeep[id].paused = true; + s_upkeepIDs.remove(id); + emit UpkeepPaused(id); + } + + /** + * @notice unpauses an upkeep + */ + function unpauseUpkeep(uint256 id) external { + _requireAdminAndNotCancelled(id); + Upkeep memory upkeep = s_upkeep[id]; + if (!upkeep.paused) revert OnlyPausedUpkeep(); + s_upkeep[id].paused = false; + s_upkeepIDs.add(id); + emit UpkeepUnpaused(id); + } + + /** + * @notice updates the checkData for an upkeep + */ + function setUpkeepCheckData(uint256 id, bytes calldata newCheckData) external { + _requireAdminAndNotCancelled(id); + if (newCheckData.length > s_storage.maxCheckDataSize) revert CheckDataExceedsLimit(); + s_checkData[id] = newCheckData; + emit UpkeepCheckDataSet(id, newCheckData); + } + + /** + * @notice updates the gas limit for an upkeep + */ + function setUpkeepGasLimit(uint256 id, uint32 gasLimit) external { + if (gasLimit < PERFORM_GAS_MIN || gasLimit > s_storage.maxPerformGas) revert GasLimitOutsideRange(); + _requireAdminAndNotCancelled(id); + s_upkeep[id].performGas = gasLimit; + + emit UpkeepGasLimitSet(id, gasLimit); + } + + /** + * @notice updates the offchain config for an upkeep + */ + function setUpkeepOffchainConfig(uint256 id, bytes calldata config) external { + _requireAdminAndNotCancelled(id); + s_upkeepOffchainConfig[id] = config; + emit UpkeepOffchainConfigSet(id, config); + } + + /** + * @notice sets the upkeep trigger config + * @param id the upkeepID to change the trigger for + * @param triggerConfig the new trigger config + */ + function setUpkeepTriggerConfig(uint256 id, bytes calldata triggerConfig) external { + _requireAdminAndNotCancelled(id); + s_upkeepTriggerConfig[id] = triggerConfig; + emit UpkeepTriggerConfigSet(id, triggerConfig); + } + + /** + * @notice withdraws an upkeep's funds from an upkeep + * @dev note that an upkeep must be cancelled first!! + */ + function withdrawFunds(uint256 id, address to) external nonReentrant { + if (to == ZERO_ADDRESS) revert InvalidRecipient(); + Upkeep memory upkeep = s_upkeep[id]; + if (s_upkeepAdmin[id] != msg.sender) revert OnlyCallableByAdmin(); + if (upkeep.maxValidBlocknumber > s_hotVars.chainModule.blockNumber()) revert UpkeepNotCanceled(); + uint96 amountToWithdraw = s_upkeep[id].balance; + s_reserveAmounts[upkeep.billingToken] = s_reserveAmounts[upkeep.billingToken] - amountToWithdraw; + s_upkeep[id].balance = 0; + upkeep.billingToken.safeTransfer(to, amountToWithdraw); + emit FundsWithdrawn(id, amountToWithdraw, to); + } + + // ================================================================ + // | FINANCE ACTIONS | + // ================================================================ + + /** + * @notice withdraws excess LINK from the liquidity pool + * @param to the address to send the fees to + * @param amount the amount to withdraw + */ + function withdrawLink(address to, uint256 amount) external { + _onlyFinanceAdminAllowed(); + if (to == ZERO_ADDRESS) revert InvalidRecipient(); + + int256 available = _linkAvailableForPayment(); + if (available < 0) { + revert InsufficientBalance(0, amount); + } else if (amount > uint256(available)) { + revert InsufficientBalance(uint256(available), amount); + } + + bool transferStatus = i_link.transfer(to, amount); + if (!transferStatus) { + revert TransferFailed(); + } + emit FeesWithdrawn(address(i_link), to, amount); + } + + /** + * @notice withdraws non-LINK fees earned by the contract + * @param asset the asset to withdraw + * @param to the address to send the fees to + * @param amount the amount to withdraw + * @dev in ON_CHAIN mode, we prevent withdrawing non-LINK fees unless there is sufficient LINK liquidity + * to cover all outstanding debts on the registry + */ + function withdrawERC20Fees(IERC20 asset, address to, uint256 amount) external { + _onlyFinanceAdminAllowed(); + if (to == ZERO_ADDRESS) revert InvalidRecipient(); + if (address(asset) == address(i_link)) revert InvalidToken(); + if (_linkAvailableForPayment() < 0 && s_payoutMode == PayoutMode.ON_CHAIN) revert InsufficientLinkLiquidity(); + uint256 available = asset.balanceOf(address(this)) - s_reserveAmounts[asset]; + if (amount > available) revert InsufficientBalance(available, amount); + + asset.safeTransfer(to, amount); + emit FeesWithdrawn(address(asset), to, amount); + } +} diff --git a/contracts/src/v0.8/automation/v2_3_zksync/ZKSyncAutomationRegistryLogicC2_3.sol b/contracts/src/v0.8/automation/v2_3_zksync/ZKSyncAutomationRegistryLogicC2_3.sol new file mode 100644 index 0000000000..3b4b023c7a --- /dev/null +++ b/contracts/src/v0.8/automation/v2_3_zksync/ZKSyncAutomationRegistryLogicC2_3.sol @@ -0,0 +1,626 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.19; + +import {ZKSyncAutomationRegistryBase2_3} from "./ZKSyncAutomationRegistryBase2_3.sol"; +import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/structs/EnumerableSet.sol"; +import {Address} from "../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/Address.sol"; +import {IAutomationForwarder} from "../interfaces/IAutomationForwarder.sol"; +import {IChainModule} from "../interfaces/IChainModule.sol"; +import {IERC20Metadata as IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import {IAutomationV21PlusCommon} from "../interfaces/IAutomationV21PlusCommon.sol"; + +contract ZKSyncAutomationRegistryLogicC2_3 is ZKSyncAutomationRegistryBase2_3 { + using Address for address; + using EnumerableSet for EnumerableSet.UintSet; + using EnumerableSet for EnumerableSet.AddressSet; + + /** + * @dev see AutomationRegistry master contract for constructor description + */ + constructor( + address link, + address linkUSDFeed, + address nativeUSDFeed, + address fastGasFeed, + address automationForwarderLogic, + address allowedReadOnlyAddress, + PayoutMode payoutMode, + address wrappedNativeTokenAddress + ) + ZKSyncAutomationRegistryBase2_3( + link, + linkUSDFeed, + nativeUSDFeed, + fastGasFeed, + automationForwarderLogic, + allowedReadOnlyAddress, + payoutMode, + wrappedNativeTokenAddress + ) + {} + + // ================================================================ + // | NODE ACTIONS | + // ================================================================ + + /** + * @notice transfers the address of payee for a transmitter + */ + function transferPayeeship(address transmitter, address proposed) external { + if (s_transmitterPayees[transmitter] != msg.sender) revert OnlyCallableByPayee(); + if (proposed == msg.sender) revert ValueNotChanged(); + + if (s_proposedPayee[transmitter] != proposed) { + s_proposedPayee[transmitter] = proposed; + emit PayeeshipTransferRequested(transmitter, msg.sender, proposed); + } + } + + /** + * @notice accepts the transfer of the payee + */ + function acceptPayeeship(address transmitter) external { + if (s_proposedPayee[transmitter] != msg.sender) revert OnlyCallableByProposedPayee(); + address past = s_transmitterPayees[transmitter]; + s_transmitterPayees[transmitter] = msg.sender; + s_proposedPayee[transmitter] = ZERO_ADDRESS; + + emit PayeeshipTransferred(transmitter, past, msg.sender); + } + + /** + * @notice this is for NOPs to withdraw LINK received as payment for work performed + */ + function withdrawPayment(address from, address to) external { + if (to == ZERO_ADDRESS) revert InvalidRecipient(); + if (s_payoutMode == PayoutMode.OFF_CHAIN) revert MustSettleOffchain(); + if (s_transmitterPayees[from] != msg.sender) revert OnlyCallableByPayee(); + uint96 balance = _updateTransmitterBalanceFromPool(from, s_hotVars.totalPremium, uint96(s_transmittersList.length)); + s_transmitters[from].balance = 0; + s_reserveAmounts[IERC20(address(i_link))] = s_reserveAmounts[IERC20(address(i_link))] - balance; + bool transferStatus = i_link.transfer(to, balance); + if (!transferStatus) { + revert TransferFailed(); + } + emit PaymentWithdrawn(from, balance, to, msg.sender); + } + + // ================================================================ + // | OWNER / MANAGER ACTIONS | + // ================================================================ + + /** + * @notice sets the privilege config for an upkeep + */ + function setUpkeepPrivilegeConfig(uint256 upkeepId, bytes calldata newPrivilegeConfig) external { + _onlyPrivilegeManagerAllowed(); + s_upkeepPrivilegeConfig[upkeepId] = newPrivilegeConfig; + emit UpkeepPrivilegeConfigSet(upkeepId, newPrivilegeConfig); + } + + /** + * @notice this is used by the owner to set the initial payees for newly added transmitters. The owner is not allowed to change payees for existing transmitters. + * @dev the IGNORE_ADDRESS is a "helper" that makes it easier to construct a list of payees when you only care about setting the payee for a small number of transmitters. + */ + function setPayees(address[] calldata payees) external onlyOwner { + if (s_transmittersList.length != payees.length) revert ParameterLengthError(); + for (uint256 i = 0; i < s_transmittersList.length; i++) { + address transmitter = s_transmittersList[i]; + address oldPayee = s_transmitterPayees[transmitter]; + address newPayee = payees[i]; + + if ( + (newPayee == ZERO_ADDRESS) || (oldPayee != ZERO_ADDRESS && oldPayee != newPayee && newPayee != IGNORE_ADDRESS) + ) { + revert InvalidPayee(); + } + + if (newPayee != IGNORE_ADDRESS) { + s_transmitterPayees[transmitter] = newPayee; + } + } + emit PayeesUpdated(s_transmittersList, payees); + } + + /** + * @notice sets the migration permission for a peer registry + * @dev this must be done before upkeeps can be migrated to/from another registry + */ + function setPeerRegistryMigrationPermission(address peer, MigrationPermission permission) external onlyOwner { + s_peerRegistryMigrationPermission[peer] = permission; + } + + /** + * @notice pauses the entire registry + */ + function pause() external onlyOwner { + s_hotVars.paused = true; + emit Paused(msg.sender); + } + + /** + * @notice unpauses the entire registry + */ + function unpause() external onlyOwner { + s_hotVars.paused = false; + emit Unpaused(msg.sender); + } + + /** + * @notice sets a generic bytes field used to indicate the privilege that this admin address had + * @param admin the address to set privilege for + * @param newPrivilegeConfig the privileges that this admin has + */ + function setAdminPrivilegeConfig(address admin, bytes calldata newPrivilegeConfig) external { + _onlyPrivilegeManagerAllowed(); + s_adminPrivilegeConfig[admin] = newPrivilegeConfig; + emit AdminPrivilegeConfigSet(admin, newPrivilegeConfig); + } + + /** + * @notice settles NOPs' LINK payment offchain + */ + function settleNOPsOffchain() external { + _onlyFinanceAdminAllowed(); + if (s_payoutMode == PayoutMode.ON_CHAIN) revert MustSettleOnchain(); + + uint96 totalPremium = s_hotVars.totalPremium; + uint256 activeTransmittersLength = s_transmittersList.length; + uint256 deactivatedTransmittersLength = s_deactivatedTransmitters.length(); + uint256 length = activeTransmittersLength + deactivatedTransmittersLength; + uint256[] memory payments = new uint256[](length); + address[] memory payees = new address[](length); + + for (uint256 i = 0; i < activeTransmittersLength; i++) { + address transmitterAddr = s_transmittersList[i]; + uint96 balance = _updateTransmitterBalanceFromPool( + transmitterAddr, + totalPremium, + uint96(activeTransmittersLength) + ); + + payments[i] = balance; + payees[i] = s_transmitterPayees[transmitterAddr]; + s_transmitters[transmitterAddr].balance = 0; + } + + for (uint256 i = 0; i < deactivatedTransmittersLength; i++) { + address deactivatedAddr = s_deactivatedTransmitters.at(i); + Transmitter memory transmitter = s_transmitters[deactivatedAddr]; + + payees[i + activeTransmittersLength] = s_transmitterPayees[deactivatedAddr]; + payments[i + activeTransmittersLength] = transmitter.balance; + s_transmitters[deactivatedAddr].balance = 0; + } + + // reserve amount of LINK is reset to 0 since no user deposits of LINK are expected in offchain mode + s_reserveAmounts[IERC20(address(i_link))] = 0; + + for (uint256 idx = s_deactivatedTransmitters.length(); idx > 0; idx--) { + s_deactivatedTransmitters.remove(s_deactivatedTransmitters.at(idx - 1)); + } + + emit NOPsSettledOffchain(payees, payments); + } + + /** + * @notice disables offchain payment for NOPs + */ + function disableOffchainPayments() external onlyOwner { + s_payoutMode = PayoutMode.ON_CHAIN; + } + + // ================================================================ + // | GETTERS | + // ================================================================ + + function getConditionalGasOverhead() external pure returns (uint256) { + return REGISTRY_CONDITIONAL_OVERHEAD; + } + + function getLogGasOverhead() external pure returns (uint256) { + return REGISTRY_LOG_OVERHEAD; + } + + function getPerSignerGasOverhead() external pure returns (uint256) { + return REGISTRY_PER_SIGNER_GAS_OVERHEAD; + } + + function getCancellationDelay() external pure returns (uint256) { + return CANCELLATION_DELAY; + } + + function getLinkAddress() external view returns (address) { + return address(i_link); + } + + function getLinkUSDFeedAddress() external view returns (address) { + return address(i_linkUSDFeed); + } + + function getNativeUSDFeedAddress() external view returns (address) { + return address(i_nativeUSDFeed); + } + + function getFastGasFeedAddress() external view returns (address) { + return address(i_fastGasFeed); + } + + function getAutomationForwarderLogic() external view returns (address) { + return i_automationForwarderLogic; + } + + function getAllowedReadOnlyAddress() external view returns (address) { + return i_allowedReadOnlyAddress; + } + + function getWrappedNativeTokenAddress() external view returns (address) { + return address(i_wrappedNativeToken); + } + + function getBillingToken(uint256 upkeepID) external view returns (IERC20) { + return s_upkeep[upkeepID].billingToken; + } + + function getBillingTokens() external view returns (IERC20[] memory) { + return s_billingTokens; + } + + function supportsBillingToken(IERC20 token) external view returns (bool) { + return address(s_billingConfigs[token].priceFeed) != address(0); + } + + function getBillingTokenConfig(IERC20 token) external view returns (BillingConfig memory) { + return s_billingConfigs[token]; + } + + function getBillingOverridesEnabled(uint256 upkeepID) external view returns (bool) { + return s_upkeep[upkeepID].overridesEnabled; + } + + function getPayoutMode() external view returns (PayoutMode) { + return s_payoutMode; + } + + function upkeepVersion() public pure returns (uint8) { + return UPKEEP_VERSION_BASE; + } + + /** + * @notice gets the number of upkeeps on the registry + */ + function getNumUpkeeps() external view returns (uint256) { + return s_upkeepIDs.length(); + } + + /** + * @notice read all of the details about an upkeep + * @dev this function may be deprecated in a future version of automation in favor of individual + * getters for each field + */ + function getUpkeep(uint256 id) external view returns (IAutomationV21PlusCommon.UpkeepInfoLegacy memory upkeepInfo) { + Upkeep memory reg = s_upkeep[id]; + address target = address(reg.forwarder) == address(0) ? address(0) : reg.forwarder.getTarget(); + upkeepInfo = IAutomationV21PlusCommon.UpkeepInfoLegacy({ + target: target, + performGas: reg.performGas, + checkData: s_checkData[id], + balance: reg.balance, + admin: s_upkeepAdmin[id], + maxValidBlocknumber: reg.maxValidBlocknumber, + lastPerformedBlockNumber: reg.lastPerformedBlockNumber, + amountSpent: uint96(reg.amountSpent), // force casting to uint96 for backwards compatibility. Not an issue if it overflows. + paused: reg.paused, + offchainConfig: s_upkeepOffchainConfig[id] + }); + return upkeepInfo; + } + + /** + * @notice retrieve active upkeep IDs. Active upkeep is defined as an upkeep which is not paused and not canceled. + * @param startIndex starting index in list + * @param maxCount max count to retrieve (0 = unlimited) + * @dev the order of IDs in the list is **not guaranteed**, therefore, if making successive calls, one + * should consider keeping the blockheight constant to ensure a holistic picture of the contract state + */ + function getActiveUpkeepIDs(uint256 startIndex, uint256 maxCount) external view returns (uint256[] memory) { + uint256 numUpkeeps = s_upkeepIDs.length(); + if (startIndex >= numUpkeeps) revert IndexOutOfRange(); + uint256 endIndex = startIndex + maxCount; + endIndex = endIndex > numUpkeeps || maxCount == 0 ? numUpkeeps : endIndex; + uint256[] memory ids = new uint256[](endIndex - startIndex); + for (uint256 idx = 0; idx < ids.length; idx++) { + ids[idx] = s_upkeepIDs.at(idx + startIndex); + } + return ids; + } + + /** + * @notice returns the upkeep's trigger type + */ + function getTriggerType(uint256 upkeepId) external pure returns (Trigger) { + return _getTriggerType(upkeepId); + } + + /** + * @notice returns the trigger config for an upkeeep + */ + function getUpkeepTriggerConfig(uint256 upkeepId) public view returns (bytes memory) { + return s_upkeepTriggerConfig[upkeepId]; + } + + /** + * @notice read the current info about any transmitter address + */ + function getTransmitterInfo( + address query + ) external view returns (bool active, uint8 index, uint96 balance, uint96 lastCollected, address payee) { + Transmitter memory transmitter = s_transmitters[query]; + + uint96 pooledShare = 0; + if (transmitter.active) { + uint96 totalDifference = s_hotVars.totalPremium - transmitter.lastCollected; + pooledShare = totalDifference / uint96(s_transmittersList.length); + } + + return ( + transmitter.active, + transmitter.index, + (transmitter.balance + pooledShare), + transmitter.lastCollected, + s_transmitterPayees[query] + ); + } + + /** + * @notice read the current info about any signer address + */ + function getSignerInfo(address query) external view returns (bool active, uint8 index) { + Signer memory signer = s_signers[query]; + return (signer.active, signer.index); + } + + /** + * @notice read the current on-chain config of the registry + * @dev this function will change between versions, it should never be used where + * backwards compatibility matters! + */ + function getConfig() external view returns (OnchainConfig memory) { + return + OnchainConfig({ + checkGasLimit: s_storage.checkGasLimit, + stalenessSeconds: s_hotVars.stalenessSeconds, + gasCeilingMultiplier: s_hotVars.gasCeilingMultiplier, + maxPerformGas: s_storage.maxPerformGas, + maxCheckDataSize: s_storage.maxCheckDataSize, + maxPerformDataSize: s_storage.maxPerformDataSize, + maxRevertDataSize: s_storage.maxRevertDataSize, + fallbackGasPrice: s_fallbackGasPrice, + fallbackLinkPrice: s_fallbackLinkPrice, + fallbackNativePrice: s_fallbackNativePrice, + transcoder: s_storage.transcoder, + registrars: s_registrars.values(), + upkeepPrivilegeManager: s_storage.upkeepPrivilegeManager, + chainModule: s_hotVars.chainModule, + reorgProtectionEnabled: s_hotVars.reorgProtectionEnabled, + financeAdmin: s_storage.financeAdmin + }); + } + + /** + * @notice read the current state of the registry + * @dev this function is deprecated + */ + function getState() + external + view + returns ( + IAutomationV21PlusCommon.StateLegacy memory state, + IAutomationV21PlusCommon.OnchainConfigLegacy memory config, + address[] memory signers, + address[] memory transmitters, + uint8 f + ) + { + state = IAutomationV21PlusCommon.StateLegacy({ + nonce: s_storage.nonce, + ownerLinkBalance: 0, // deprecated + expectedLinkBalance: 0, // deprecated + totalPremium: s_hotVars.totalPremium, + numUpkeeps: s_upkeepIDs.length(), + configCount: s_storage.configCount, + latestConfigBlockNumber: s_storage.latestConfigBlockNumber, + latestConfigDigest: s_latestConfigDigest, + latestEpoch: s_hotVars.latestEpoch, + paused: s_hotVars.paused + }); + + config = IAutomationV21PlusCommon.OnchainConfigLegacy({ + paymentPremiumPPB: 0, // deprecated + flatFeeMicroLink: 0, // deprecated + checkGasLimit: s_storage.checkGasLimit, + stalenessSeconds: s_hotVars.stalenessSeconds, + gasCeilingMultiplier: s_hotVars.gasCeilingMultiplier, + minUpkeepSpend: 0, // deprecated + maxPerformGas: s_storage.maxPerformGas, + maxCheckDataSize: s_storage.maxCheckDataSize, + maxPerformDataSize: s_storage.maxPerformDataSize, + maxRevertDataSize: s_storage.maxRevertDataSize, + fallbackGasPrice: s_fallbackGasPrice, + fallbackLinkPrice: s_fallbackLinkPrice, + transcoder: s_storage.transcoder, + registrars: s_registrars.values(), + upkeepPrivilegeManager: s_storage.upkeepPrivilegeManager + }); + + return (state, config, s_signersList, s_transmittersList, s_hotVars.f); + } + + /** + * @notice read the Storage data + * @dev this function signature will change with each version of automation + * this should not be treated as a stable function + */ + function getStorage() external view returns (Storage memory) { + return s_storage; + } + + /** + * @notice read the HotVars data + * @dev this function signature will change with each version of automation + * this should not be treated as a stable function + */ + function getHotVars() external view returns (HotVars memory) { + return s_hotVars; + } + + /** + * @notice get the chain module + */ + function getChainModule() external view returns (IChainModule chainModule) { + return s_hotVars.chainModule; + } + + /** + * @notice if this registry has reorg protection enabled + */ + function getReorgProtectionEnabled() external view returns (bool reorgProtectionEnabled) { + return s_hotVars.reorgProtectionEnabled; + } + + /** + * @notice calculates the minimum balance required for an upkeep to remain eligible + * @param id the upkeep id to calculate minimum balance for + */ + function getBalance(uint256 id) external view returns (uint96 balance) { + return s_upkeep[id].balance; + } + + /** + * @notice calculates the minimum balance required for an upkeep to remain eligible + * @param id the upkeep id to calculate minimum balance for + */ + function getMinBalance(uint256 id) external view returns (uint96) { + return getMinBalanceForUpkeep(id); + } + + /** + * @notice calculates the minimum balance required for an upkeep to remain eligible + * @param id the upkeep id to calculate minimum balance for + * @dev this will be deprecated in a future version in favor of getMinBalance + */ + function getMinBalanceForUpkeep(uint256 id) public view returns (uint96 minBalance) { + Upkeep memory upkeep = s_upkeep[id]; + return getMaxPaymentForGas(id, _getTriggerType(id), upkeep.performGas, upkeep.billingToken); + } + + /** + * @notice calculates the maximum payment for a given gas limit + * @param gasLimit the gas to calculate payment for + */ + function getMaxPaymentForGas( + uint256 id, + Trigger triggerType, + uint32 gasLimit, + IERC20 billingToken + ) public view returns (uint96 maxPayment) { + HotVars memory hotVars = s_hotVars; + (uint256 fastGasWei, uint256 linkUSD, uint256 nativeUSD) = _getFeedData(hotVars); + return _getMaxPayment(id, hotVars, triggerType, gasLimit, fastGasWei, linkUSD, nativeUSD, billingToken); + } + + /** + * @notice retrieves the migration permission for a peer registry + */ + function getPeerRegistryMigrationPermission(address peer) external view returns (MigrationPermission) { + return s_peerRegistryMigrationPermission[peer]; + } + + /** + * @notice returns the upkeep privilege config + */ + function getUpkeepPrivilegeConfig(uint256 upkeepId) external view returns (bytes memory) { + return s_upkeepPrivilegeConfig[upkeepId]; + } + + /** + * @notice returns the admin's privilege config + */ + function getAdminPrivilegeConfig(address admin) external view returns (bytes memory) { + return s_adminPrivilegeConfig[admin]; + } + + /** + * @notice returns the upkeep's forwarder contract + */ + function getForwarder(uint256 upkeepID) external view returns (IAutomationForwarder) { + return s_upkeep[upkeepID].forwarder; + } + + /** + * @notice returns if the dedupKey exists or not + */ + function hasDedupKey(bytes32 dedupKey) external view returns (bool) { + return s_dedupKeys[dedupKey]; + } + + /** + * @notice returns the fallback native price + */ + function getFallbackNativePrice() external view returns (uint256) { + return s_fallbackNativePrice; + } + + /** + * @notice returns the amount of a particular token that is reserved as + * user deposits / NOP payments + */ + function getReserveAmount(IERC20 billingToken) external view returns (uint256) { + return s_reserveAmounts[billingToken]; + } + + /** + * @notice returns the amount of a particular token that is withdraw-able by finance admin + */ + function getAvailableERC20ForPayment(IERC20 billingToken) external view returns (uint256) { + return billingToken.balanceOf(address(this)) - s_reserveAmounts[IERC20(address(billingToken))]; + } + + /** + * @notice returns the size of the LINK liquidity pool + */ + function linkAvailableForPayment() public view returns (int256) { + return _linkAvailableForPayment(); + } + + /** + * @notice returns the BillingOverrides config for a given upkeep + */ + function getBillingOverrides(uint256 upkeepID) external view returns (BillingOverrides memory) { + return s_billingOverrides[upkeepID]; + } + + /** + * @notice returns the BillingConfig for a given billing token, this includes decimals and price feed etc + */ + function getBillingConfig(IERC20 billingToken) external view returns (BillingConfig memory) { + return s_billingConfigs[billingToken]; + } + + /** + * @notice returns all active transmitters with their associated payees + */ + function getTransmittersWithPayees() external view returns (TransmitterPayeeInfo[] memory) { + uint256 transmitterCount = s_transmittersList.length; + TransmitterPayeeInfo[] memory transmitters = new TransmitterPayeeInfo[](transmitterCount); + + for (uint256 i = 0; i < transmitterCount; i++) { + address transmitterAddress = s_transmittersList[i]; + address payeeAddress = s_transmitterPayees[transmitterAddress]; + + transmitters[i] = TransmitterPayeeInfo(transmitterAddress, payeeAddress); + } + + return transmitters; + } +} diff --git a/contracts/src/v0.8/keystone/CapabilitiesRegistry.sol b/contracts/src/v0.8/keystone/CapabilitiesRegistry.sol index ba61584d0a..461bbca898 100644 --- a/contracts/src/v0.8/keystone/CapabilitiesRegistry.sol +++ b/contracts/src/v0.8/keystone/CapabilitiesRegistry.sol @@ -3,8 +3,8 @@ pragma solidity 0.8.24; import {TypeAndVersionInterface} from "../interfaces/TypeAndVersionInterface.sol"; import {OwnerIsCreator} from "../shared/access/OwnerIsCreator.sol"; -import {IERC165} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; import {EnumerableSet} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol"; +import {ERC165Checker} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/ERC165Checker.sol"; import {ICapabilityConfiguration} from "./interfaces/ICapabilityConfiguration.sol"; /// @notice CapabilitiesRegistry is used to manage Nodes (including their links to Node @@ -775,7 +775,9 @@ contract CapabilitiesRegistry is OwnerIsCreator, TypeAndVersionInterface { /// @param nodes The nodes making up the DON /// @param capabilityConfigurations The list of configurations for the /// capabilities supported by the DON - /// @param isPublic True if the DON is public + /// @param isPublic True if the DON is can accept external capability requests + /// @param acceptsWorkflows True if the DON can accept workflows + /// @param f The maximum number of faulty nodes the DON can tolerate function addDON( bytes32[] calldata nodes, CapabilityConfiguration[] calldata capabilityConfigurations, @@ -797,24 +799,32 @@ contract CapabilitiesRegistry is OwnerIsCreator, TypeAndVersionInterface { /// the admin to reconfigure the list of capabilities supported /// by the DON, the list of nodes that make up the DON as well /// as whether or not the DON can accept external workflows + /// @param donId The ID of the DON to update /// @param nodes The nodes making up the DON /// @param capabilityConfigurations The list of configurations for the /// capabilities supported by the DON - /// @param isPublic True if the DON is can accept external workflows + /// @param isPublic True if the DON is can accept external capability requests + /// @param f The maximum number of nodes that can fail function updateDON( uint32 donId, bytes32[] calldata nodes, CapabilityConfiguration[] calldata capabilityConfigurations, bool isPublic, - bool acceptsWorkflows, uint8 f ) external onlyOwner { - uint32 configCount = s_dons[donId].configCount; + DON storage don = s_dons[donId]; + uint32 configCount = don.configCount; if (configCount == 0) revert DONDoesNotExist(donId); _setDONConfig( nodes, capabilityConfigurations, - DONParams({id: donId, configCount: ++configCount, isPublic: isPublic, acceptsWorkflows: acceptsWorkflows, f: f}) + DONParams({ + id: donId, + configCount: ++configCount, + isPublic: isPublic, + acceptsWorkflows: don.acceptsWorkflows, + f: f + }) ); } @@ -961,6 +971,11 @@ contract CapabilitiesRegistry is OwnerIsCreator, TypeAndVersionInterface { donCapabilityConfig.capabilityIds.push(configuration.capabilityId); donCapabilityConfig.capabilityConfigs[configuration.capabilityId] = configuration.config; + s_dons[donParams.id].isPublic = donParams.isPublic; + s_dons[donParams.id].acceptsWorkflows = donParams.acceptsWorkflows; + s_dons[donParams.id].f = donParams.f; + s_dons[donParams.id].configCount = donParams.configCount; + _setDONCapabilityConfig( donParams.id, donParams.configCount, @@ -969,10 +984,6 @@ contract CapabilitiesRegistry is OwnerIsCreator, TypeAndVersionInterface { configuration.config ); } - s_dons[donParams.id].isPublic = donParams.isPublic; - s_dons[donParams.id].acceptsWorkflows = donParams.acceptsWorkflows; - s_dons[donParams.id].f = donParams.f; - s_dons[donParams.id].configCount = donParams.configCount; emit ConfigSet(donParams.id, donParams.configCount); } @@ -1010,8 +1021,7 @@ contract CapabilitiesRegistry is OwnerIsCreator, TypeAndVersionInterface { /// by implementing both getCapabilityConfiguration and /// beforeCapabilityConfigSet if ( - capability.configurationContract.code.length == 0 || - !IERC165(capability.configurationContract).supportsInterface(type(ICapabilityConfiguration).interfaceId) + !ERC165Checker.supportsInterface(capability.configurationContract, type(ICapabilityConfiguration).interfaceId) ) revert InvalidCapabilityConfigurationContractInterface(capability.configurationContract); } s_capabilities[hashedCapabilityId] = capability; diff --git a/contracts/src/v0.8/keystone/KeystoneForwarder.sol b/contracts/src/v0.8/keystone/KeystoneForwarder.sol index 4b44feccbf..f92295cab9 100644 --- a/contracts/src/v0.8/keystone/KeystoneForwarder.sol +++ b/contracts/src/v0.8/keystone/KeystoneForwarder.sol @@ -1,12 +1,14 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.24; -import {IReceiver} from "./interfaces/IReceiver.sol"; -import {IRouter} from "./interfaces/IRouter.sol"; -import {ITypeAndVersion} from "../shared/interfaces/ITypeAndVersion.sol"; +import {IERC165} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; +import {ITypeAndVersion} from "../shared/interfaces/ITypeAndVersion.sol"; import {OwnerIsCreator} from "../shared/access/OwnerIsCreator.sol"; +import {IReceiver} from "./interfaces/IReceiver.sol"; +import {IRouter} from "./interfaces/IRouter.sol"; + /// @notice This is an entry point for `write_${chain}` Target capability. It /// allows nodes to determine if reports have been processed (successfully or /// not) in a decentralized and product-agnostic way by recording processed @@ -49,7 +51,7 @@ contract KeystoneForwarder is OwnerIsCreator, ITypeAndVersion, IRouter { error InvalidConfig(uint64 configId); /// @notice This error is thrown whenever a signer address is not in the - /// configuration. + /// configuration or when trying to set a zero address as a signer. /// @param signer The signer address that was not in the configuration error InvalidSigner(address signer); @@ -66,7 +68,7 @@ contract KeystoneForwarder is OwnerIsCreator, ITypeAndVersion, IRouter { /// @notice Contains the configuration for each DON ID // @param configId (uint64(donId) << 32) | configVersion - mapping(uint64 configId => OracleSet) internal s_configs; + mapping(uint64 configId => OracleSet oracleSet) internal s_configs; event ConfigSet(uint32 indexed donId, uint32 indexed configVersion, uint8 f, address[] signers); @@ -90,12 +92,16 @@ contract KeystoneForwarder is OwnerIsCreator, ITypeAndVersion, IRouter { uint256 internal constant FORWARDER_METADATA_LENGTH = 45; uint256 internal constant SIGNATURE_LENGTH = 65; + /// @dev The gas we require to revert in case of a revert in the call to the + /// receiver. This is more than enough and does not attempt to be exact. + uint256 internal constant REQUIRED_GAS_FOR_ROUTING = 40_000; + // ================================================================ // │ Router │ // ================================================================ - mapping(address forwarder => bool) internal s_forwarders; - mapping(bytes32 transmissionId => TransmissionInfo) internal s_transmissions; + mapping(address forwarder => bool isForwarder) internal s_forwarders; + mapping(bytes32 transmissionId => Transmission transmission) internal s_transmissions; function addForwarder(address forwarder) external onlyOwner { s_forwarders[forwarder] = true; @@ -114,19 +120,38 @@ contract KeystoneForwarder is OwnerIsCreator, ITypeAndVersion, IRouter { bytes calldata metadata, bytes calldata validatedReport ) public returns (bool) { - if (!s_forwarders[msg.sender]) { - revert UnauthorizedForwarder(); - } + if (!s_forwarders[msg.sender]) revert UnauthorizedForwarder(); + uint256 gasLeft = gasleft(); + if (gasLeft < REQUIRED_GAS_FOR_ROUTING) revert InsufficientGasForRouting(transmissionId); - if (s_transmissions[transmissionId].transmitter != address(0)) revert AlreadyAttempted(transmissionId); + Transmission memory transmission = s_transmissions[transmissionId]; + if (transmission.success || transmission.invalidReceiver) revert AlreadyAttempted(transmissionId); + + uint256 gasLimit = gasLeft - REQUIRED_GAS_FOR_ROUTING; s_transmissions[transmissionId].transmitter = transmitter; + s_transmissions[transmissionId].gasLimit = uint80(gasLimit); + + if (receiver.code.length == 0) { + s_transmissions[transmissionId].invalidReceiver = true; + return false; + } - if (receiver.code.length == 0) return false; + try IERC165(receiver).supportsInterface(type(IReceiver).interfaceId) { + bool success; + bytes memory payload = abi.encodeCall(IReceiver.onReport, (metadata, validatedReport)); - try IReceiver(receiver).onReport(metadata, validatedReport) { - s_transmissions[transmissionId].success = true; - return true; + assembly { + // call and return whether we succeeded. ignore return data + // call(gas,addr,value,argsOffset,argsLength,retOffset,retLength) + success := call(gasLimit, receiver, 0, add(payload, 0x20), mload(payload), 0x0, 0x0) + } + + if (success) { + s_transmissions[transmissionId].success = true; + } + return success; } catch { + s_transmissions[transmissionId].invalidReceiver = true; return false; } } @@ -141,26 +166,43 @@ contract KeystoneForwarder is OwnerIsCreator, ITypeAndVersion, IRouter { return keccak256(bytes.concat(bytes20(uint160(receiver)), workflowExecutionId, reportId)); } - /// @notice Get transmitter of a given report or 0x0 if it wasn't transmitted yet - function getTransmitter( + function getTransmissionInfo( address receiver, bytes32 workflowExecutionId, bytes2 reportId - ) external view returns (address) { - return s_transmissions[getTransmissionId(receiver, workflowExecutionId, reportId)].transmitter; + ) external view returns (TransmissionInfo memory) { + bytes32 transmissionId = getTransmissionId(receiver, workflowExecutionId, reportId); + + Transmission memory transmission = s_transmissions[transmissionId]; + + TransmissionState state; + + if (transmission.transmitter == address(0)) { + state = IRouter.TransmissionState.NOT_ATTEMPTED; + } else if (transmission.invalidReceiver) { + state = IRouter.TransmissionState.INVALID_RECEIVER; + } else { + state = transmission.success ? IRouter.TransmissionState.SUCCEEDED : IRouter.TransmissionState.FAILED; + } + + return + TransmissionInfo({ + gasLimit: transmission.gasLimit, + invalidReceiver: transmission.invalidReceiver, + state: state, + success: transmission.success, + transmissionId: transmissionId, + transmitter: transmission.transmitter + }); } - /// @notice Get delivery status of a given report - function getTransmissionState( + /// @notice Get transmitter of a given report or 0x0 if it wasn't transmitted yet + function getTransmitter( address receiver, bytes32 workflowExecutionId, bytes2 reportId - ) external view returns (IRouter.TransmissionState) { - bytes32 transmissionId = getTransmissionId(receiver, workflowExecutionId, reportId); - - if (s_transmissions[transmissionId].transmitter == address(0)) return IRouter.TransmissionState.NOT_ATTEMPTED; - return - s_transmissions[transmissionId].success ? IRouter.TransmissionState.SUCCEEDED : IRouter.TransmissionState.FAILED; + ) external view returns (address) { + return s_transmissions[getTransmissionId(receiver, workflowExecutionId, reportId)].transmitter; } function isForwarder(address forwarder) external view returns (bool) { @@ -187,6 +229,7 @@ contract KeystoneForwarder is OwnerIsCreator, ITypeAndVersion, IRouter { for (uint256 i = 0; i < signers.length; ++i) { // assign indices, detect duplicates address signer = signers[i]; + if (signer == address(0)) revert InvalidSigner(signer); if (s_configs[configId]._positions[signer] != 0) revert DuplicateSigner(signer); s_configs[configId]._positions[signer] = i + 1; } diff --git a/contracts/src/v0.8/keystone/OCR3Capability.sol b/contracts/src/v0.8/keystone/OCR3Capability.sol index 8613a803b2..1ba934b1c4 100644 --- a/contracts/src/v0.8/keystone/OCR3Capability.sol +++ b/contracts/src/v0.8/keystone/OCR3Capability.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity 0.8.24; import {OCR2Base} from "./ocr/OCR2Base.sol"; diff --git a/contracts/src/v0.8/keystone/interfaces/ICapabilityConfiguration.sol b/contracts/src/v0.8/keystone/interfaces/ICapabilityConfiguration.sol index 429c2a1d3a..702d55dba9 100644 --- a/contracts/src/v0.8/keystone/interfaces/ICapabilityConfiguration.sol +++ b/contracts/src/v0.8/keystone/interfaces/ICapabilityConfiguration.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity 0.8.24; /// @notice Interface for capability configuration contract. It MUST be /// implemented for a contract to be used as a capability configuration. diff --git a/contracts/src/v0.8/keystone/interfaces/IReceiver.sol b/contracts/src/v0.8/keystone/interfaces/IReceiver.sol index f58c2da7ae..debe58feea 100644 --- a/contracts/src/v0.8/keystone/interfaces/IReceiver.sol +++ b/contracts/src/v0.8/keystone/interfaces/IReceiver.sol @@ -1,7 +1,12 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity 0.8.24; /// @title IReceiver - receives keystone reports interface IReceiver { + /// @notice Handles incoming keystone reports. + /// @dev If this function call reverts, it can be retried with a higher gas + /// limit. The receiver is responsible for discarding stale reports. + /// @param metadata Report's metadata. + /// @param report Workflow report. function onReport(bytes calldata metadata, bytes calldata report) external; } diff --git a/contracts/src/v0.8/keystone/interfaces/IRouter.sol b/contracts/src/v0.8/keystone/interfaces/IRouter.sol index a36c17c14d..e40f331867 100644 --- a/contracts/src/v0.8/keystone/interfaces/IRouter.sol +++ b/contracts/src/v0.8/keystone/interfaces/IRouter.sol @@ -1,9 +1,12 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity 0.8.24; /// @title IRouter - delivers keystone reports to receiver interface IRouter { error UnauthorizedForwarder(); + /// @dev Thrown when the gas limit is insufficient for handling state after + /// calling the receiver function. + error InsufficientGasForRouting(bytes32 transmissionId); error AlreadyAttempted(bytes32 transmissionId); event ForwarderAdded(address indexed forwarder); @@ -12,12 +15,42 @@ interface IRouter { enum TransmissionState { NOT_ATTEMPTED, SUCCEEDED, + INVALID_RECEIVER, FAILED } + struct Transmission { + address transmitter; + // This is true if the receiver is not a contract or does not implement the + // `IReceiver` interface. + bool invalidReceiver; + // Whether the transmission attempt was successful. If `false`, the + // transmission can be retried with an increased gas limit. + bool success; + // The amount of gas allocated for the `IReceiver.onReport` call. uint80 + // allows storing gas for known EVM block gas limits. + // Ensures that the minimum gas requested by the user is available during + // the transmission attempt. If the transmission fails (indicated by a + // `false` success state), it can be retried with an increased gas limit. + uint80 gasLimit; + } + struct TransmissionInfo { + bytes32 transmissionId; + TransmissionState state; address transmitter; + // This is true if the receiver is not a contract or does not implement the + // `IReceiver` interface. + bool invalidReceiver; + // Whether the transmission attempt was successful. If `false`, the + // transmission can be retried with an increased gas limit. bool success; + // The amount of gas allocated for the `IReceiver.onReport` call. uint80 + // allows storing gas for known EVM block gas limits. + // Ensures that the minimum gas requested by the user is available during + // the transmission attempt. If the transmission fails (indicated by a + // `false` success state), it can be retried with an increased gas limit. + uint80 gasLimit; } function addForwarder(address forwarder) external; @@ -36,15 +69,14 @@ interface IRouter { bytes32 workflowExecutionId, bytes2 reportId ) external pure returns (bytes32); - function getTransmitter( + function getTransmissionInfo( address receiver, bytes32 workflowExecutionId, bytes2 reportId - ) external view returns (address); - function getTransmissionState( + ) external view returns (TransmissionInfo memory); + function getTransmitter( address receiver, bytes32 workflowExecutionId, bytes2 reportId - ) external view returns (TransmissionState); - function isForwarder(address forwarder) external view returns (bool); + ) external view returns (address); } diff --git a/contracts/src/v0.8/keystone/ocr/OCR2Abstract.sol b/contracts/src/v0.8/keystone/ocr/OCR2Abstract.sol index 083a404534..3c1e304748 100644 --- a/contracts/src/v0.8/keystone/ocr/OCR2Abstract.sol +++ b/contracts/src/v0.8/keystone/ocr/OCR2Abstract.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity 0.8.24; import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; diff --git a/contracts/src/v0.8/keystone/test/BaseTest.t.sol b/contracts/src/v0.8/keystone/test/BaseTest.t.sol index e637406c14..64dc018c3a 100644 --- a/contracts/src/v0.8/keystone/test/BaseTest.t.sol +++ b/contracts/src/v0.8/keystone/test/BaseTest.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity 0.8.24; import {Test} from "forge-std/Test.sol"; import {Constants} from "./Constants.t.sol"; diff --git a/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_AddDONTest.t.sol b/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_AddDONTest.t.sol index fff6623a59..dc0b85bfa3 100644 --- a/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_AddDONTest.t.sol +++ b/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_AddDONTest.t.sol @@ -1,9 +1,10 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity 0.8.24; import {BaseTest} from "./BaseTest.t.sol"; import {ICapabilityConfiguration} from "../interfaces/ICapabilityConfiguration.sol"; import {CapabilitiesRegistry} from "../CapabilitiesRegistry.sol"; +import {MaliciousConfigurationContract} from "./mocks/MaliciousConfigurationContract.sol"; contract CapabilitiesRegistry_AddDONTest is BaseTest { function setUp() public override { @@ -245,3 +246,75 @@ contract CapabilitiesRegistry_AddDONTest is BaseTest { assertEq(donInfo.nodeP2PIds[1], P2P_ID_THREE); } } + +contract CapabilitiesRegistry_AddDONTest_WhenMaliciousCapabilityConfigurationConfigured is BaseTest { + function setUp() public override { + BaseTest.setUp(); + CapabilitiesRegistry.Capability[] memory capabilities = new CapabilitiesRegistry.Capability[](2); + + address maliciousConfigContractAddr = address( + new MaliciousConfigurationContract(s_capabilityWithConfigurationContractId) + ); + s_basicCapability.configurationContract = maliciousConfigContractAddr; + capabilities[0] = s_basicCapability; + capabilities[1] = s_capabilityWithConfigurationContract; + + CapabilitiesRegistry.NodeOperator[] memory nodeOperators = _getNodeOperators(); + nodeOperators[0].admin = maliciousConfigContractAddr; + nodeOperators[1].admin = maliciousConfigContractAddr; + nodeOperators[2].admin = maliciousConfigContractAddr; + + s_CapabilitiesRegistry.addNodeOperators(nodeOperators); + s_CapabilitiesRegistry.addCapabilities(capabilities); + + CapabilitiesRegistry.NodeParams[] memory nodes = new CapabilitiesRegistry.NodeParams[](3); + bytes32[] memory capabilityIds = new bytes32[](1); + capabilityIds[0] = s_basicHashedCapabilityId; + + nodes[0] = CapabilitiesRegistry.NodeParams({ + nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, + p2pId: P2P_ID, + signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, + hashedCapabilityIds: capabilityIds + }); + + bytes32[] memory nodeTwoCapabilityIds = new bytes32[](1); + nodeTwoCapabilityIds[0] = s_basicHashedCapabilityId; + + nodes[1] = CapabilitiesRegistry.NodeParams({ + nodeOperatorId: TEST_NODE_OPERATOR_TWO_ID, + p2pId: P2P_ID_TWO, + signer: NODE_OPERATOR_TWO_SIGNER_ADDRESS, + hashedCapabilityIds: nodeTwoCapabilityIds + }); + + nodes[2] = CapabilitiesRegistry.NodeParams({ + nodeOperatorId: TEST_NODE_OPERATOR_THREE_ID, + p2pId: P2P_ID_THREE, + signer: NODE_OPERATOR_THREE_SIGNER_ADDRESS, + hashedCapabilityIds: capabilityIds + }); + + s_CapabilitiesRegistry.addNodes(nodes); + + changePrank(ADMIN); + } + + function test_RevertWhen_MaliciousCapabilitiesConfigContractTriesToRemoveCapabilitiesFromDONNodes() public { + bytes32[] memory nodes = new bytes32[](2); + nodes[0] = P2P_ID; + nodes[1] = P2P_ID_THREE; + + CapabilitiesRegistry.CapabilityConfiguration[] + memory capabilityConfigs = new CapabilitiesRegistry.CapabilityConfiguration[](1); + capabilityConfigs[0] = CapabilitiesRegistry.CapabilityConfiguration({ + capabilityId: s_basicHashedCapabilityId, + config: BASIC_CAPABILITY_CONFIG + }); + + vm.expectRevert( + abi.encodeWithSelector(CapabilitiesRegistry.CapabilityRequiredByDON.selector, s_basicHashedCapabilityId, DON_ID) + ); + s_CapabilitiesRegistry.addDON(nodes, capabilityConfigs, true, true, F_VALUE); + } +} diff --git a/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_DeprecateCapabilitiesTest.t.sol b/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_DeprecateCapabilitiesTest.t.sol index 4d289e7c74..e06fa4a703 100644 --- a/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_DeprecateCapabilitiesTest.t.sol +++ b/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_DeprecateCapabilitiesTest.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity 0.8.24; import {BaseTest} from "./BaseTest.t.sol"; import {CapabilitiesRegistry} from "../CapabilitiesRegistry.sol"; diff --git a/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_GetCapabilitiesTest.t.sol b/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_GetCapabilitiesTest.t.sol index 9702c62b9c..8f39183ee7 100644 --- a/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_GetCapabilitiesTest.t.sol +++ b/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_GetCapabilitiesTest.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity 0.8.24; import {BaseTest} from "./BaseTest.t.sol"; import {CapabilitiesRegistry} from "../CapabilitiesRegistry.sol"; diff --git a/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_GetDONsTest.t.sol b/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_GetDONsTest.t.sol index a83b1421d3..a79485abad 100644 --- a/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_GetDONsTest.t.sol +++ b/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_GetDONsTest.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity 0.8.24; import {BaseTest} from "./BaseTest.t.sol"; diff --git a/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_GetHashedCapabilityIdTest.t.sol b/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_GetHashedCapabilityIdTest.t.sol index b9a6e6dc97..cdfb0eb643 100644 --- a/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_GetHashedCapabilityIdTest.t.sol +++ b/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_GetHashedCapabilityIdTest.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity 0.8.24; import {BaseTest} from "./BaseTest.t.sol"; import {CapabilityConfigurationContract} from "./mocks/CapabilityConfigurationContract.sol"; diff --git a/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_GetNodeOperatorsTest.t.sol b/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_GetNodeOperatorsTest.t.sol index 36ef201a99..471f4a86ad 100644 --- a/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_GetNodeOperatorsTest.t.sol +++ b/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_GetNodeOperatorsTest.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity 0.8.24; import {BaseTest} from "./BaseTest.t.sol"; import {CapabilitiesRegistry} from "../CapabilitiesRegistry.sol"; diff --git a/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_GetNodesTest.t.sol b/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_GetNodesTest.t.sol index 901e7b9272..a5fe5fa1d1 100644 --- a/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_GetNodesTest.t.sol +++ b/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_GetNodesTest.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity 0.8.24; import {BaseTest} from "./BaseTest.t.sol"; import {CapabilitiesRegistry} from "../CapabilitiesRegistry.sol"; diff --git a/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_RemoveNodesTest.t.sol b/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_RemoveNodesTest.t.sol index 9622c23876..08646600a6 100644 --- a/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_RemoveNodesTest.t.sol +++ b/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_RemoveNodesTest.t.sol @@ -158,7 +158,7 @@ contract CapabilitiesRegistry_RemoveNodesTest is BaseTest { bytes32[] memory updatedNodes = new bytes32[](2); updatedNodes[0] = P2P_ID; updatedNodes[1] = P2P_ID_THREE; - s_CapabilitiesRegistry.updateDON(DON_ID, updatedNodes, capabilityConfigs, true, true, F_VALUE); + s_CapabilitiesRegistry.updateDON(DON_ID, updatedNodes, capabilityConfigs, true, F_VALUE); // Remove node s_CapabilitiesRegistry.removeNodes(removedNodes); diff --git a/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_UpdateDONTest.t.sol b/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_UpdateDONTest.t.sol index 8b21b29506..825524ebe8 100644 --- a/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_UpdateDONTest.t.sol +++ b/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_UpdateDONTest.t.sol @@ -71,7 +71,7 @@ contract CapabilitiesRegistry_UpdateDONTest is BaseTest { capabilityId: s_basicHashedCapabilityId, config: BASIC_CAPABILITY_CONFIG }); - s_CapabilitiesRegistry.updateDON(DON_ID, nodes, capabilityConfigs, true, true, F_VALUE); + s_CapabilitiesRegistry.updateDON(DON_ID, nodes, capabilityConfigs, true, F_VALUE); } function test_RevertWhen_NodeDoesNotSupportCapability() public { @@ -91,7 +91,7 @@ contract CapabilitiesRegistry_UpdateDONTest is BaseTest { s_capabilityWithConfigurationContractId ) ); - s_CapabilitiesRegistry.updateDON(DON_ID, nodes, capabilityConfigs, true, true, F_VALUE); + s_CapabilitiesRegistry.updateDON(DON_ID, nodes, capabilityConfigs, true, F_VALUE); } function test_RevertWhen_DONDoesNotExist() public { @@ -106,7 +106,7 @@ contract CapabilitiesRegistry_UpdateDONTest is BaseTest { config: BASIC_CAPABILITY_CONFIG }); vm.expectRevert(abi.encodeWithSelector(CapabilitiesRegistry.DONDoesNotExist.selector, nonExistentDONId)); - s_CapabilitiesRegistry.updateDON(nonExistentDONId, nodes, capabilityConfigs, true, true, F_VALUE); + s_CapabilitiesRegistry.updateDON(nonExistentDONId, nodes, capabilityConfigs, true, F_VALUE); } function test_RevertWhen_CapabilityDoesNotExist() public { @@ -122,7 +122,7 @@ contract CapabilitiesRegistry_UpdateDONTest is BaseTest { vm.expectRevert( abi.encodeWithSelector(CapabilitiesRegistry.CapabilityDoesNotExist.selector, s_nonExistentHashedCapabilityId) ); - s_CapabilitiesRegistry.updateDON(DON_ID, nodes, capabilityConfigs, true, true, F_VALUE); + s_CapabilitiesRegistry.updateDON(DON_ID, nodes, capabilityConfigs, true, F_VALUE); } function test_RevertWhen_DuplicateCapabilityAdded() public { @@ -144,7 +144,7 @@ contract CapabilitiesRegistry_UpdateDONTest is BaseTest { vm.expectRevert( abi.encodeWithSelector(CapabilitiesRegistry.DuplicateDONCapability.selector, 1, s_basicHashedCapabilityId) ); - s_CapabilitiesRegistry.updateDON(DON_ID, nodes, capabilityConfigs, true, true, F_VALUE); + s_CapabilitiesRegistry.updateDON(DON_ID, nodes, capabilityConfigs, true, F_VALUE); } function test_RevertWhen_DeprecatedCapabilityAdded() public { @@ -165,7 +165,7 @@ contract CapabilitiesRegistry_UpdateDONTest is BaseTest { }); vm.expectRevert(abi.encodeWithSelector(CapabilitiesRegistry.CapabilityIsDeprecated.selector, capabilityId)); - s_CapabilitiesRegistry.updateDON(DON_ID, nodes, capabilityConfigs, true, true, F_VALUE); + s_CapabilitiesRegistry.updateDON(DON_ID, nodes, capabilityConfigs, true, F_VALUE); } function test_RevertWhen_DuplicateNodeAdded() public { @@ -180,7 +180,7 @@ contract CapabilitiesRegistry_UpdateDONTest is BaseTest { config: BASIC_CAPABILITY_CONFIG }); vm.expectRevert(abi.encodeWithSelector(CapabilitiesRegistry.DuplicateDONNode.selector, 1, P2P_ID)); - s_CapabilitiesRegistry.updateDON(DON_ID, nodes, capabilityConfigs, true, true, F_VALUE); + s_CapabilitiesRegistry.updateDON(DON_ID, nodes, capabilityConfigs, true, F_VALUE); } function test_UpdatesDON() public { @@ -217,7 +217,7 @@ contract CapabilitiesRegistry_UpdateDONTest is BaseTest { ), 1 ); - s_CapabilitiesRegistry.updateDON(DON_ID, nodes, capabilityConfigs, expectedDONIsPublic, true, F_VALUE); + s_CapabilitiesRegistry.updateDON(DON_ID, nodes, capabilityConfigs, expectedDONIsPublic, F_VALUE); CapabilitiesRegistry.DONInfo memory donInfo = s_CapabilitiesRegistry.getDON(DON_ID); assertEq(donInfo.id, DON_ID); diff --git a/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_UpdateNodeOperatorsTest.t.sol b/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_UpdateNodeOperatorsTest.t.sol index 721fd35eae..8f6be580f4 100644 --- a/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_UpdateNodeOperatorsTest.t.sol +++ b/contracts/src/v0.8/keystone/test/CapabilitiesRegistry_UpdateNodeOperatorsTest.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity 0.8.24; import {BaseTest} from "./BaseTest.t.sol"; import {CapabilitiesRegistry} from "../CapabilitiesRegistry.sol"; diff --git a/contracts/src/v0.8/keystone/test/Constants.t.sol b/contracts/src/v0.8/keystone/test/Constants.t.sol index 23c80eea9f..a540a25572 100644 --- a/contracts/src/v0.8/keystone/test/Constants.t.sol +++ b/contracts/src/v0.8/keystone/test/Constants.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity 0.8.24; contract Constants { address internal constant ADMIN = address(1); diff --git a/contracts/src/v0.8/keystone/test/KeystoneForwarderBaseTest.t.sol b/contracts/src/v0.8/keystone/test/KeystoneForwarderBaseTest.t.sol index 3b3c406078..c106c2b2b2 100644 --- a/contracts/src/v0.8/keystone/test/KeystoneForwarderBaseTest.t.sol +++ b/contracts/src/v0.8/keystone/test/KeystoneForwarderBaseTest.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity 0.8.24; import {Test} from "forge-std/Test.sol"; import {Receiver} from "./mocks/Receiver.sol"; diff --git a/contracts/src/v0.8/keystone/test/KeystoneForwarder_ReportTest.t.sol b/contracts/src/v0.8/keystone/test/KeystoneForwarder_ReportTest.t.sol index ccb398fac5..5363d87e92 100644 --- a/contracts/src/v0.8/keystone/test/KeystoneForwarder_ReportTest.t.sol +++ b/contracts/src/v0.8/keystone/test/KeystoneForwarder_ReportTest.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity 0.8.24; import {BaseTest} from "./KeystoneForwarderBaseTest.t.sol"; import {IRouter} from "../interfaces/IRouter.sol"; @@ -141,15 +141,40 @@ contract KeystoneForwarder_ReportTest is BaseTest { s_forwarder.report(address(s_receiver), report, reportContext, signatures); } - function test_RevertWhen_AlreadyAttempted() public { - s_forwarder.report(address(s_receiver), report, reportContext, signatures); + function test_RevertWhen_RetryingSuccessfulTransmission() public { + s_forwarder.report{gas: 400_000}(address(s_receiver), report, reportContext, signatures); bytes32 transmissionId = s_forwarder.getTransmissionId(address(s_receiver), executionId, reportId); vm.expectRevert(abi.encodeWithSelector(IRouter.AlreadyAttempted.selector, transmissionId)); - s_forwarder.report(address(s_receiver), report, reportContext, signatures); + // Retyring with more gas + s_forwarder.report{gas: 450_000}(address(s_receiver), report, reportContext, signatures); + } + + function test_RevertWhen_RetryingInvalidContractTransmission() public { + // Receiver is not a contract + address receiver = address(404); + s_forwarder.report{gas: 400_000}(receiver, report, reportContext, signatures); + + bytes32 transmissionId = s_forwarder.getTransmissionId(receiver, executionId, reportId); + vm.expectRevert(abi.encodeWithSelector(IRouter.AlreadyAttempted.selector, transmissionId)); + // Retyring with more gas + s_forwarder.report{gas: 450_000}(receiver, report, reportContext, signatures); + } + + function test_RevertWhen_AttemptingTransmissionWithInsufficientGas() public { + bytes32 transmissionId = s_forwarder.getTransmissionId(address(s_receiver), executionId, reportId); + vm.expectRevert(abi.encodeWithSelector(IRouter.InsufficientGasForRouting.selector, transmissionId)); + s_forwarder.report{gas: 50_000}(address(s_receiver), report, reportContext, signatures); } function test_Report_SuccessfulDelivery() public { + IRouter.TransmissionInfo memory transmissionInfo = s_forwarder.getTransmissionInfo( + address(s_receiver), + executionId, + reportId + ); + assertEq(uint8(transmissionInfo.state), uint8(IRouter.TransmissionState.NOT_ATTEMPTED), "state mismatch"); + vm.expectEmit(address(s_receiver)); emit MessageReceived(metadata, mercuryReports); @@ -158,16 +183,31 @@ contract KeystoneForwarder_ReportTest is BaseTest { s_forwarder.report(address(s_receiver), report, reportContext, signatures); - assertEq( - s_forwarder.getTransmitter(address(s_receiver), executionId, reportId), - TRANSMITTER, - "transmitter mismatch" - ); - assertEq( - uint8(s_forwarder.getTransmissionState(address(s_receiver), executionId, reportId)), - uint8(IRouter.TransmissionState.SUCCEEDED), - "TransmissionState mismatch" + transmissionInfo = s_forwarder.getTransmissionInfo(address(s_receiver), executionId, reportId); + + assertEq(transmissionInfo.transmitter, TRANSMITTER, "transmitter mismatch"); + assertEq(uint8(transmissionInfo.state), uint8(IRouter.TransmissionState.SUCCEEDED), "state mismatch"); + assertGt(transmissionInfo.gasLimit, 100_000, "gas limit mismatch"); + } + + function test_Report_SuccessfulRetryWithMoreGas() public { + s_forwarder.report{gas: 200_000}(address(s_receiver), report, reportContext, signatures); + + IRouter.TransmissionInfo memory transmissionInfo = s_forwarder.getTransmissionInfo( + address(s_receiver), + executionId, + reportId ); + // Expect to fail with the receiver running out of gas + assertEq(uint8(transmissionInfo.state), uint8(IRouter.TransmissionState.FAILED), "state mismatch"); + assertGt(transmissionInfo.gasLimit, 100_000, "gas limit mismatch"); + + // Should succeed with more gas + s_forwarder.report{gas: 300_000}(address(s_receiver), report, reportContext, signatures); + + transmissionInfo = s_forwarder.getTransmissionInfo(address(s_receiver), executionId, reportId); + assertEq(uint8(transmissionInfo.state), uint8(IRouter.TransmissionState.SUCCEEDED), "state mismatch"); + assertGt(transmissionInfo.gasLimit, 200_000, "gas limit mismatch"); } function test_Report_FailedDeliveryWhenReceiverNotContract() public { @@ -179,29 +219,21 @@ contract KeystoneForwarder_ReportTest is BaseTest { s_forwarder.report(receiver, report, reportContext, signatures); - assertEq(s_forwarder.getTransmitter(receiver, executionId, reportId), TRANSMITTER, "transmitter mismatch"); - assertEq( - uint8(s_forwarder.getTransmissionState(receiver, executionId, reportId)), - uint8(IRouter.TransmissionState.FAILED), - "TransmissionState mismatch" - ); + IRouter.TransmissionInfo memory transmissionInfo = s_forwarder.getTransmissionInfo(receiver, executionId, reportId); + assertEq(uint8(transmissionInfo.state), uint8(IRouter.TransmissionState.INVALID_RECEIVER), "state mismatch"); } function test_Report_FailedDeliveryWhenReceiverInterfaceNotSupported() public { // Receiver is a contract but doesn't implement the required interface address receiver = address(s_forwarder); - vm.expectEmit(address(s_forwarder)); + vm.expectEmit(true, true, true, false); emit ReportProcessed(receiver, executionId, reportId, false); s_forwarder.report(receiver, report, reportContext, signatures); - assertEq(s_forwarder.getTransmitter(receiver, executionId, reportId), TRANSMITTER, "transmitter mismatch"); - assertEq( - uint8(s_forwarder.getTransmissionState(receiver, executionId, reportId)), - uint8(IRouter.TransmissionState.FAILED), - "TransmissionState mismatch" - ); + IRouter.TransmissionInfo memory transmissionInfo = s_forwarder.getTransmissionInfo(receiver, executionId, reportId); + assertEq(uint8(transmissionInfo.state), uint8(IRouter.TransmissionState.INVALID_RECEIVER), "state mismatch"); } function test_Report_ConfigVersion() public { diff --git a/contracts/src/v0.8/keystone/test/KeystoneForwarder_SetConfigTest.t.sol b/contracts/src/v0.8/keystone/test/KeystoneForwarder_SetConfigTest.t.sol index 4b908bb702..5dcf79b38e 100644 --- a/contracts/src/v0.8/keystone/test/KeystoneForwarder_SetConfigTest.t.sol +++ b/contracts/src/v0.8/keystone/test/KeystoneForwarder_SetConfigTest.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity 0.8.24; import {BaseTest} from "./KeystoneForwarderBaseTest.t.sol"; import {KeystoneForwarder} from "../KeystoneForwarder.sol"; @@ -41,6 +41,14 @@ contract KeystoneForwarder_SetConfigTest is BaseTest { s_forwarder.setConfig(DON_ID, CONFIG_VERSION, F, signers); } + function test_RevertWhen_ProvidingZeroAddressSigner() public { + address[] memory signers = _getSignerAddresses(); + signers[1] = address(0); + + vm.expectRevert(abi.encodeWithSelector(KeystoneForwarder.InvalidSigner.selector, signers[1])); + s_forwarder.setConfig(DON_ID, CONFIG_VERSION, F, signers); + } + function test_SetConfig_FirstTime() public { s_forwarder.setConfig(DON_ID, CONFIG_VERSION, F, _getSignerAddresses()); } diff --git a/contracts/src/v0.8/keystone/test/KeystoneForwarder_TypeAndVersionTest.t.sol b/contracts/src/v0.8/keystone/test/KeystoneForwarder_TypeAndVersionTest.t.sol index 8aad376649..5a5cc70d2b 100644 --- a/contracts/src/v0.8/keystone/test/KeystoneForwarder_TypeAndVersionTest.t.sol +++ b/contracts/src/v0.8/keystone/test/KeystoneForwarder_TypeAndVersionTest.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity 0.8.24; import {BaseTest} from "./KeystoneForwarderBaseTest.t.sol"; diff --git a/contracts/src/v0.8/keystone/test/KeystoneRouter_AccessTest.t.sol b/contracts/src/v0.8/keystone/test/KeystoneRouter_AccessTest.t.sol index c126f7ce31..0e43b72bdc 100644 --- a/contracts/src/v0.8/keystone/test/KeystoneRouter_AccessTest.t.sol +++ b/contracts/src/v0.8/keystone/test/KeystoneRouter_AccessTest.t.sol @@ -5,6 +5,7 @@ import {Test} from "forge-std/Test.sol"; import {IReceiver} from "../interfaces/IReceiver.sol"; import {IRouter} from "../interfaces/IRouter.sol"; import {KeystoneForwarder} from "../KeystoneForwarder.sol"; +import {Receiver} from "./mocks/Receiver.sol"; contract KeystoneRouter_SetConfigTest is Test { address internal ADMIN = address(1); @@ -18,10 +19,12 @@ contract KeystoneRouter_SetConfigTest is Test { bytes32 internal id = hex"6d795f657865637574696f6e5f69640000000000000000000000000000000000"; KeystoneForwarder internal s_router; + Receiver internal s_receiver; function setUp() public virtual { vm.prank(ADMIN); s_router = new KeystoneForwarder(); + s_receiver = new Receiver(); } function test_AddForwarder_RevertWhen_NotOwner() public { @@ -36,6 +39,13 @@ contract KeystoneRouter_SetConfigTest is Test { s_router.removeForwarder(FORWARDER); } + function test_RemoveForwarder_Success() public { + vm.prank(ADMIN); + vm.expectEmit(true, false, false, false); + emit IRouter.ForwarderRemoved(FORWARDER); + s_router.removeForwarder(FORWARDER); + } + function test_Route_RevertWhen_UnauthorizedForwarder() public { vm.prank(STRANGER); vm.expectRevert(IRouter.UnauthorizedForwarder.selector); @@ -50,8 +60,8 @@ contract KeystoneRouter_SetConfigTest is Test { assertEq(s_router.isForwarder(FORWARDER), true); vm.prank(FORWARDER); - vm.mockCall(RECEIVER, abi.encodeCall(IReceiver.onReport, (metadata, report)), abi.encode()); - vm.expectCall(RECEIVER, abi.encodeCall(IReceiver.onReport, (metadata, report))); - s_router.route(id, TRANSMITTER, RECEIVER, metadata, report); + vm.mockCall(address(s_receiver), abi.encodeCall(IReceiver.onReport, (metadata, report)), abi.encode()); + vm.expectCall(address(s_receiver), abi.encodeCall(IReceiver.onReport, (metadata, report))); + s_router.route(id, TRANSMITTER, address(s_receiver), metadata, report); } } diff --git a/contracts/src/v0.8/keystone/test/mocks/CapabilityConfigurationContract.sol b/contracts/src/v0.8/keystone/test/mocks/CapabilityConfigurationContract.sol index c2a916c87e..105c89006f 100644 --- a/contracts/src/v0.8/keystone/test/mocks/CapabilityConfigurationContract.sol +++ b/contracts/src/v0.8/keystone/test/mocks/CapabilityConfigurationContract.sol @@ -2,9 +2,9 @@ pragma solidity 0.8.24; import {ICapabilityConfiguration} from "../../interfaces/ICapabilityConfiguration.sol"; -import {ERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/ERC165.sol"; +import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; -contract CapabilityConfigurationContract is ICapabilityConfiguration, ERC165 { +contract CapabilityConfigurationContract is ICapabilityConfiguration, IERC165 { mapping(uint256 => bytes) private s_donConfiguration; function getCapabilityConfiguration(uint32 donId) external view returns (bytes memory configuration) { @@ -17,7 +17,7 @@ contract CapabilityConfigurationContract is ICapabilityConfiguration, ERC165 { s_donConfiguration[donId] = config; } - function supportsInterface(bytes4 interfaceId) public pure override returns (bool) { - return interfaceId == this.getCapabilityConfiguration.selector ^ this.beforeCapabilityConfigSet.selector; + function supportsInterface(bytes4 interfaceId) public pure returns (bool) { + return interfaceId == type(ICapabilityConfiguration).interfaceId || interfaceId == type(IERC165).interfaceId; } } diff --git a/contracts/src/v0.8/keystone/test/mocks/MaliciousConfigurationContract.sol b/contracts/src/v0.8/keystone/test/mocks/MaliciousConfigurationContract.sol new file mode 100644 index 0000000000..2a7fc4d18d --- /dev/null +++ b/contracts/src/v0.8/keystone/test/mocks/MaliciousConfigurationContract.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import {ICapabilityConfiguration} from "../../interfaces/ICapabilityConfiguration.sol"; +import {CapabilitiesRegistry} from "../../CapabilitiesRegistry.sol"; +import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; +import {Constants} from "../Constants.t.sol"; + +contract MaliciousConfigurationContract is ICapabilityConfiguration, IERC165, Constants { + bytes32 internal s_capabilityWithConfigurationContractId; + + constructor(bytes32 capabilityWithConfigContractId) { + s_capabilityWithConfigurationContractId = capabilityWithConfigContractId; + } + + function getCapabilityConfiguration(uint32) external pure returns (bytes memory configuration) { + return bytes(""); + } + + function beforeCapabilityConfigSet(bytes32[] calldata, bytes calldata, uint64, uint32) external { + CapabilitiesRegistry.NodeParams[] memory nodes = new CapabilitiesRegistry.NodeParams[](2); + bytes32[] memory hashedCapabilityIds = new bytes32[](1); + + hashedCapabilityIds[0] = s_capabilityWithConfigurationContractId; + + // Set node one's signer to another address + nodes[0] = CapabilitiesRegistry.NodeParams({ + nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, + p2pId: P2P_ID, + signer: NODE_OPERATOR_ONE_SIGNER_ADDRESS, + hashedCapabilityIds: hashedCapabilityIds + }); + + nodes[1] = CapabilitiesRegistry.NodeParams({ + nodeOperatorId: TEST_NODE_OPERATOR_ONE_ID, + p2pId: P2P_ID_THREE, + signer: NODE_OPERATOR_THREE_SIGNER_ADDRESS, + hashedCapabilityIds: hashedCapabilityIds + }); + + CapabilitiesRegistry(msg.sender).updateNodes(nodes); + } + + function supportsInterface(bytes4 interfaceId) public pure returns (bool) { + return interfaceId == type(ICapabilityConfiguration).interfaceId || interfaceId == type(IERC165).interfaceId; + } +} diff --git a/contracts/src/v0.8/keystone/test/mocks/Receiver.sol b/contracts/src/v0.8/keystone/test/mocks/Receiver.sol index 25e8755641..3c1f157bc4 100644 --- a/contracts/src/v0.8/keystone/test/mocks/Receiver.sol +++ b/contracts/src/v0.8/keystone/test/mocks/Receiver.sol @@ -1,16 +1,24 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity 0.8.24; +import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; import {IReceiver} from "../../interfaces/IReceiver.sol"; -contract Receiver is IReceiver { +contract Receiver is IReceiver, IERC165 { event MessageReceived(bytes metadata, bytes[] mercuryReports); + bytes public latestReport; constructor() {} function onReport(bytes calldata metadata, bytes calldata rawReport) external { + latestReport = rawReport; + // parse actual report bytes[] memory mercuryReports = abi.decode(rawReport, (bytes[])); emit MessageReceived(metadata, mercuryReports); } + + function supportsInterface(bytes4 interfaceId) public pure override returns (bool) { + return interfaceId == this.onReport.selector; + } } diff --git a/contracts/src/v0.8/llo-feeds/dev/ChannelConfigStore.sol b/contracts/src/v0.8/llo-feeds/dev/ChannelConfigStore.sol index 6c6a5ec9b3..086379b210 100644 --- a/contracts/src/v0.8/llo-feeds/dev/ChannelConfigStore.sol +++ b/contracts/src/v0.8/llo-feeds/dev/ChannelConfigStore.sol @@ -6,97 +6,21 @@ import {IChannelConfigStore} from "./interfaces/IChannelConfigStore.sol"; import {TypeAndVersionInterface} from "../../interfaces/TypeAndVersionInterface.sol"; contract ChannelConfigStore is ConfirmedOwner, IChannelConfigStore, TypeAndVersionInterface { - mapping(uint32 => ChannelDefinition) private s_channelDefinitions; - - // mapping(bytes32 => ChannelConfiguration) private s_channelProductionConfigurations; - // mapping(bytes32 => ChannelConfiguration) private s_channelStagingConfigurations; - - event NewChannelDefinition(uint32 channelId, ChannelDefinition channelDefinition); - event ChannelDefinitionRemoved(uint32 channelId); - // event NewProductionConfig(ChannelConfiguration channelConfig); - // event NewStagingConfig(ChannelConfiguration channelConfig); - event PromoteStagingConfig(uint32 channelId); - - error OnlyCallableByEOA(); - error StagingConfigAlreadyPromoted(); - error EmptyStreamIDs(); - error ZeroReportFormat(); - error ZeroChainSelector(); - error ChannelDefinitionNotFound(); + event NewChannelDefinition(uint256 indexed donId, uint32 version, string url, bytes32 sha); constructor() ConfirmedOwner(msg.sender) {} - // function setStagingConfig(bytes32 configDigest, ChannelConfiguration calldata channelConfig) external onlyOwner { - // s_channelStagingConfigurations[channelId] = channelConfig; - - // emit NewStagingConfig(channelConfig); - // } - - //// this will trigger the following: - //// - offchain ShouldRetireCache will start returning true for the old (production) - //// protocol instance - //// - once the old production instance retires it will generate a handover - //// retirement report - //// - the staging instance will become the new production instance once - //// any honest oracle that is on both instances forward the retirement - //// report from the old instance to the new instace via the - //// PredecessorRetirementReportCache - //// - //// Note: the promotion flow only works if the previous production instance - //// is working correctly & generating reports. If that's not the case, the - //// owner is expected to "setProductionConfig" directly instead. This will - //// cause "gaps" to be created, but that seems unavoidable in such a scenario. - // function promoteStagingConfig(bytes32 configDigest) external onlyOwner { - // ChannelConfiguration memory stagingConfig = s_channelStagingConfigurations[channelId]; - - // if(stagingConfig.channelConfigId.length == 0) { - // revert StagingConfigAlreadyPromoted(); - // } - - // s_channelProductionConfigurations[channelId] = s_channelStagingConfigurations[channelId]; - - // emit PromoteStagingConfig(channelId); - // } - - function addChannel(uint32 channelId, ChannelDefinition calldata channelDefinition) external onlyOwner { - if (channelDefinition.streamIDs.length == 0) { - revert EmptyStreamIDs(); - } - - if (channelDefinition.chainSelector == 0) { - revert ZeroChainSelector(); - } - - if (channelDefinition.reportFormat == 0) { - revert ZeroReportFormat(); - } - - s_channelDefinitions[channelId] = channelDefinition; - - emit NewChannelDefinition(channelId, channelDefinition); - } - - function removeChannel(uint32 channelId) external onlyOwner { - if (s_channelDefinitions[channelId].streamIDs.length == 0) { - revert ChannelDefinitionNotFound(); - } - - delete s_channelDefinitions[channelId]; - - emit ChannelDefinitionRemoved(channelId); - } - - function getChannelDefinitions(uint32 channelId) external view returns (ChannelDefinition memory) { - // solhint-disable-next-line avoid-tx-origin - if (msg.sender != tx.origin) { - revert OnlyCallableByEOA(); - } + /// @notice The version of a channel definition keyed by DON ID + // Increments by 1 on every update + mapping(uint256 => uint256) internal s_channelDefinitionVersions; - return s_channelDefinitions[channelId]; + function setChannelDefinitions(uint32 donId, string calldata url, bytes32 sha) external onlyOwner { + uint32 newVersion = uint32(++s_channelDefinitionVersions[uint256(donId)]); + emit NewChannelDefinition(donId, newVersion, url, sha); } function typeAndVersion() external pure override returns (string memory) { - return "ChannelConfigStore 0.0.0"; + return "ChannelConfigStore 0.0.1"; } function supportsInterface(bytes4 interfaceId) external pure returns (bool) { diff --git a/contracts/src/v0.8/llo-feeds/dev/interfaces/IChannelConfigStore.sol b/contracts/src/v0.8/llo-feeds/dev/interfaces/IChannelConfigStore.sol index 45e3ee313d..873928b6de 100644 --- a/contracts/src/v0.8/llo-feeds/dev/interfaces/IChannelConfigStore.sol +++ b/contracts/src/v0.8/llo-feeds/dev/interfaces/IChannelConfigStore.sol @@ -4,29 +4,5 @@ pragma solidity 0.8.19; import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; interface IChannelConfigStore is IERC165 { - // function setStagingConfig(bytes32 configDigest, ChannelConfiguration calldata channelConfig) external; - - // function promoteStagingConfig(bytes32 configDigest) external; - - function addChannel(uint32 channelId, ChannelDefinition calldata channelDefinition) external; - - function removeChannel(uint32 channelId) external; - - function getChannelDefinitions(uint32 channelId) external view returns (ChannelDefinition memory); - - // struct ChannelConfiguration { - // bytes32 configDigest; - // } - - struct ChannelDefinition { - // e.g. evm, solana, CosmWasm, kalechain, etc... - uint32 reportFormat; - // Specifies the chain on which this channel can be verified. Currently uses - // CCIP chain selectors, but lots of other schemes are possible as well. - uint64 chainSelector; - // We assume that StreamIDs is always non-empty and that the 0-th stream - // contains the verification price in LINK and the 1-st stream contains the - // verification price in the native coin. - uint32[] streamIDs; - } + function setChannelDefinitions(uint32 donId, string calldata url, bytes32 sha) external; } diff --git a/contracts/src/v0.8/llo-feeds/dev/test/ChannelConfigStore.t.sol b/contracts/src/v0.8/llo-feeds/dev/test/ChannelConfigStore.t.sol new file mode 100644 index 0000000000..70b033e66b --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/dev/test/ChannelConfigStore.t.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {IChannelConfigStore} from "../interfaces/IChannelConfigStore.sol"; +import {Test} from "forge-std/Test.sol"; +import {ChannelConfigStore} from "../ChannelConfigStore.sol"; +import {ExposedChannelConfigStore} from "./mocks/ExposedChannelConfigStore.sol"; + +/** + * @title ChannelConfigStoreTest + * @author samsondav + * @notice Base class for ChannelConfigStore tests + */ +contract ChannelConfigStoreTest is Test { + ExposedChannelConfigStore public channelConfigStore; + event NewChannelDefinition(uint256 indexed donId, uint32 version, string url, bytes32 sha); + + function setUp() public virtual { + channelConfigStore = new ExposedChannelConfigStore(); + } + + function testTypeAndVersion() public view { + assertEq(channelConfigStore.typeAndVersion(), "ChannelConfigStore 0.0.1"); + } + + function testSupportsInterface() public view { + assertTrue(channelConfigStore.supportsInterface(type(IChannelConfigStore).interfaceId)); + } + + function testSetChannelDefinitions() public { + vm.expectEmit(); + emit NewChannelDefinition(42, 1, "url", keccak256("sha")); + channelConfigStore.setChannelDefinitions(42, "url", keccak256("sha")); + + vm.expectEmit(); + emit NewChannelDefinition(42, 2, "url2", keccak256("sha2")); + channelConfigStore.setChannelDefinitions(42, "url2", keccak256("sha2")); + + assertEq(channelConfigStore.exposedReadChannelDefinitionStates(42), uint32(2)); + } +} diff --git a/contracts/src/v0.8/llo-feeds/dev/test/mocks/ExposedChannelConfigStore.sol b/contracts/src/v0.8/llo-feeds/dev/test/mocks/ExposedChannelConfigStore.sol new file mode 100644 index 0000000000..1ffd51210f --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/dev/test/mocks/ExposedChannelConfigStore.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {ChannelConfigStore} from "../../ChannelConfigStore.sol"; + +// Exposed ChannelConfigStore exposes certain internal ChannelConfigStore +// methods/structures so that golang code can access them, and we get +// reliable type checking on their usage +contract ExposedChannelConfigStore is ChannelConfigStore { + constructor() {} + + function exposedReadChannelDefinitionStates(uint256 donId) public view returns (uint256) { + return s_channelDefinitionVersions[donId]; + } +} diff --git a/contracts/src/v0.8/llo-feeds/libraries/Common.sol b/contracts/src/v0.8/llo-feeds/libraries/Common.sol index f732ced004..23418bf41a 100644 --- a/contracts/src/v0.8/llo-feeds/libraries/Common.sol +++ b/contracts/src/v0.8/llo-feeds/libraries/Common.sol @@ -19,6 +19,28 @@ library Common { uint64 weight; } + /** + * @notice Checks if an array of AddressAndWeight has duplicate addresses + * @param recipients The array of AddressAndWeight to check + * @return bool True if there are duplicates, false otherwise + */ + function _hasDuplicateAddresses(address[] memory recipients) internal pure returns (bool) { + for (uint256 i = 0; i < recipients.length; ) { + for (uint256 j = i + 1; j < recipients.length; ) { + if (recipients[i] == recipients[j]) { + return true; + } + unchecked { + ++j; + } + } + unchecked { + ++i; + } + } + return false; + } + /** * @notice Checks if an array of AddressAndWeight has duplicate addresses * @param recipients The array of AddressAndWeight to check @@ -40,4 +62,28 @@ library Common { } return false; } + + /** + * @notice sorts a list of addresses numerically + * @param arr The array of addresses to sort + * @param left the start index + * @param right the end index + */ + function _quickSort(address[] memory arr, int256 left, int256 right) internal pure { + int256 i = left; + int256 j = right; + if (i == j) return; + address pivot = arr[uint256(left + (right - left) / 2)]; + while (i <= j) { + while (uint160(arr[uint256(i)]) < uint160(pivot)) i++; + while (uint160(pivot) < uint160(arr[uint256(j)])) j--; + if (i <= j) { + (arr[uint256(i)], arr[uint256(j)]) = (arr[uint256(j)], arr[uint256(i)]); + i++; + j--; + } + } + if (left < j) _quickSort(arr, left, j); + if (i < right) _quickSort(arr, i, right); + } } diff --git a/contracts/src/v0.8/llo-feeds/test/ByteUtilTest.t.sol b/contracts/src/v0.8/llo-feeds/libraries/test/ByteUtilTest.t.sol similarity index 99% rename from contracts/src/v0.8/llo-feeds/test/ByteUtilTest.t.sol rename to contracts/src/v0.8/llo-feeds/libraries/test/ByteUtilTest.t.sol index fdabaaf3d9..8f11dab093 100644 --- a/contracts/src/v0.8/llo-feeds/test/ByteUtilTest.t.sol +++ b/contracts/src/v0.8/llo-feeds/libraries/test/ByteUtilTest.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.19; import {Test} from "forge-std/Test.sol"; -import {ByteUtil} from "../libraries/ByteUtil.sol"; +import {ByteUtil} from "../ByteUtil.sol"; contract ByteUtilTest is Test { using ByteUtil for bytes; diff --git a/contracts/src/v0.8/llo-feeds/FeeManager.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/FeeManager.sol similarity index 96% rename from contracts/src/v0.8/llo-feeds/FeeManager.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/FeeManager.sol index 665940d467..44f550e325 100644 --- a/contracts/src/v0.8/llo-feeds/FeeManager.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/FeeManager.sol @@ -1,16 +1,16 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.19; -import {ConfirmedOwner} from "../shared/access/ConfirmedOwner.sol"; +import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; import {IFeeManager} from "./interfaces/IFeeManager.sol"; -import {TypeAndVersionInterface} from "../interfaces/TypeAndVersionInterface.sol"; -import {IERC165} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; -import {Common} from "./libraries/Common.sol"; +import {TypeAndVersionInterface} from "../../interfaces/TypeAndVersionInterface.sol"; +import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; +import {Common} from "../libraries/Common.sol"; import {IRewardManager} from "./interfaces/IRewardManager.sol"; -import {IWERC20} from "../shared/interfaces/IWERC20.sol"; -import {IERC20} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC20.sol"; -import {Math} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/Math.sol"; -import {SafeERC20} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; +import {IWERC20} from "../../shared/interfaces/IWERC20.sol"; +import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC20.sol"; +import {Math} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/Math.sol"; +import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; import {IVerifierFeeManager} from "./interfaces/IVerifierFeeManager.sol"; /** diff --git a/contracts/src/v0.8/llo-feeds/RewardManager.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/RewardManager.sol similarity index 96% rename from contracts/src/v0.8/llo-feeds/RewardManager.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/RewardManager.sol index 1929289a36..49fef51c56 100644 --- a/contracts/src/v0.8/llo-feeds/RewardManager.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/RewardManager.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.19; -import {ConfirmedOwner} from "../shared/access/ConfirmedOwner.sol"; +import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; import {IRewardManager} from "./interfaces/IRewardManager.sol"; -import {IERC20} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC20.sol"; -import {TypeAndVersionInterface} from "../interfaces/TypeAndVersionInterface.sol"; -import {Common} from "./libraries/Common.sol"; -import {SafeERC20} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; +import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC20.sol"; +import {TypeAndVersionInterface} from "../../interfaces/TypeAndVersionInterface.sol"; +import {Common} from "../libraries/Common.sol"; +import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; /** * @title RewardManager diff --git a/contracts/src/v0.8/llo-feeds/Verifier.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/Verifier.sol similarity index 98% rename from contracts/src/v0.8/llo-feeds/Verifier.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/Verifier.sol index c8858999da..fe5742108a 100644 --- a/contracts/src/v0.8/llo-feeds/Verifier.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/Verifier.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.19; -import {ConfirmedOwner} from "../shared/access/ConfirmedOwner.sol"; +import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; import {IVerifier} from "./interfaces/IVerifier.sol"; import {IVerifierProxy} from "./interfaces/IVerifierProxy.sol"; -import {TypeAndVersionInterface} from "../interfaces/TypeAndVersionInterface.sol"; -import {IERC165} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; -import {Common} from "./libraries/Common.sol"; +import {TypeAndVersionInterface} from "../../interfaces/TypeAndVersionInterface.sol"; +import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; +import {Common} from "../libraries/Common.sol"; // OCR2 standard uint256 constant MAX_NUM_ORACLES = 31; diff --git a/contracts/src/v0.8/llo-feeds/VerifierProxy.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/VerifierProxy.sol similarity index 95% rename from contracts/src/v0.8/llo-feeds/VerifierProxy.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/VerifierProxy.sol index c32a27178c..c06312dd7b 100644 --- a/contracts/src/v0.8/llo-feeds/VerifierProxy.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/VerifierProxy.sol @@ -1,14 +1,14 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.19; -import {ConfirmedOwner} from "../shared/access/ConfirmedOwner.sol"; +import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; import {IVerifierProxy} from "./interfaces/IVerifierProxy.sol"; import {IVerifier} from "./interfaces/IVerifier.sol"; -import {TypeAndVersionInterface} from "../interfaces/TypeAndVersionInterface.sol"; -import {AccessControllerInterface} from "../shared/interfaces/AccessControllerInterface.sol"; -import {IERC165} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; +import {TypeAndVersionInterface} from "../../interfaces/TypeAndVersionInterface.sol"; +import {AccessControllerInterface} from "../../shared/interfaces/AccessControllerInterface.sol"; +import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; import {IVerifierFeeManager} from "./interfaces/IVerifierFeeManager.sol"; -import {Common} from "./libraries/Common.sol"; +import {Common} from "../libraries/Common.sol"; /** * The verifier proxy contract is the gateway for all report verification requests diff --git a/contracts/src/v0.8/llo-feeds/interfaces/IFeeManager.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IFeeManager.sol similarity index 94% rename from contracts/src/v0.8/llo-feeds/interfaces/IFeeManager.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IFeeManager.sol index 4095607b91..818a3a09a4 100644 --- a/contracts/src/v0.8/llo-feeds/interfaces/IFeeManager.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IFeeManager.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.19; -import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; -import {Common} from "../libraries/Common.sol"; +import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; +import {Common} from "../../libraries/Common.sol"; import {IVerifierFeeManager} from "./IVerifierFeeManager.sol"; interface IFeeManager is IERC165, IVerifierFeeManager { diff --git a/contracts/src/v0.8/llo-feeds/interfaces/IRewardManager.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IRewardManager.sol similarity index 94% rename from contracts/src/v0.8/llo-feeds/interfaces/IRewardManager.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IRewardManager.sol index 5a6e03f1c9..f08ce34db2 100644 --- a/contracts/src/v0.8/llo-feeds/interfaces/IRewardManager.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IRewardManager.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.19; -import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; -import {Common} from "../libraries/Common.sol"; +import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; +import {Common} from "../../libraries/Common.sol"; interface IRewardManager is IERC165 { /** diff --git a/contracts/src/v0.8/llo-feeds/interfaces/IVerifier.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IVerifier.sol similarity index 97% rename from contracts/src/v0.8/llo-feeds/interfaces/IVerifier.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IVerifier.sol index 617d702d3f..94b260399e 100644 --- a/contracts/src/v0.8/llo-feeds/interfaces/IVerifier.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IVerifier.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.19; -import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; -import {Common} from "../libraries/Common.sol"; +import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; +import {Common} from "../../libraries/Common.sol"; interface IVerifier is IERC165 { /** diff --git a/contracts/src/v0.8/llo-feeds/interfaces/IVerifierFeeManager.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IVerifierFeeManager.sol similarity index 88% rename from contracts/src/v0.8/llo-feeds/interfaces/IVerifierFeeManager.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IVerifierFeeManager.sol index 522db952e5..da3fdfac15 100644 --- a/contracts/src/v0.8/llo-feeds/interfaces/IVerifierFeeManager.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IVerifierFeeManager.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.19; -import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; -import {Common} from "../libraries/Common.sol"; +import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; +import {Common} from "../../libraries/Common.sol"; interface IVerifierFeeManager is IERC165 { /** diff --git a/contracts/src/v0.8/llo-feeds/interfaces/IVerifierProxy.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IVerifierProxy.sol similarity index 95% rename from contracts/src/v0.8/llo-feeds/interfaces/IVerifierProxy.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IVerifierProxy.sol index 2eb1b4aff4..6609e4869a 100644 --- a/contracts/src/v0.8/llo-feeds/interfaces/IVerifierProxy.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IVerifierProxy.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.19; -import {Common} from "../libraries/Common.sol"; -import {AccessControllerInterface} from "../../shared/interfaces/AccessControllerInterface.sol"; +import {Common} from "../../libraries/Common.sol"; +import {AccessControllerInterface} from "../../../shared/interfaces/AccessControllerInterface.sol"; import {IVerifierFeeManager} from "./IVerifierFeeManager.sol"; interface IVerifierProxy { diff --git a/contracts/src/v0.8/llo-feeds/test/fee-manager/BaseFeeManager.t.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/fee-manager/BaseFeeManager.t.sol similarity index 98% rename from contracts/src/v0.8/llo-feeds/test/fee-manager/BaseFeeManager.t.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/fee-manager/BaseFeeManager.t.sol index edde26b2ee..0d598cdac5 100644 --- a/contracts/src/v0.8/llo-feeds/test/fee-manager/BaseFeeManager.t.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/test/fee-manager/BaseFeeManager.t.sol @@ -4,9 +4,9 @@ pragma solidity 0.8.19; import {Test} from "forge-std/Test.sol"; import {FeeManager} from "../../FeeManager.sol"; import {RewardManager} from "../../RewardManager.sol"; -import {Common} from "../../libraries/Common.sol"; -import {ERC20Mock} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/mocks/ERC20Mock.sol"; -import {WERC20Mock} from "../../../shared/mocks/WERC20Mock.sol"; +import {Common} from "../../../libraries/Common.sol"; +import {ERC20Mock} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/mocks/ERC20Mock.sol"; +import {WERC20Mock} from "../../../../shared/mocks/WERC20Mock.sol"; import {IRewardManager} from "../../interfaces/IRewardManager.sol"; import {FeeManagerProxy} from "../mocks/FeeManagerProxy.sol"; diff --git a/contracts/src/v0.8/llo-feeds/test/fee-manager/FeeManager.general.t.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/fee-manager/FeeManager.general.t.sol similarity index 100% rename from contracts/src/v0.8/llo-feeds/test/fee-manager/FeeManager.general.t.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/fee-manager/FeeManager.general.t.sol diff --git a/contracts/src/v0.8/llo-feeds/test/fee-manager/FeeManager.getFeeAndReward.t.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/fee-manager/FeeManager.getFeeAndReward.t.sol similarity index 98% rename from contracts/src/v0.8/llo-feeds/test/fee-manager/FeeManager.getFeeAndReward.t.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/fee-manager/FeeManager.getFeeAndReward.t.sol index 299a7f09d5..1b0f95d51b 100644 --- a/contracts/src/v0.8/llo-feeds/test/fee-manager/FeeManager.getFeeAndReward.t.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/test/fee-manager/FeeManager.getFeeAndReward.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.19; -import {Common} from "../../libraries/Common.sol"; +import {Common} from "../../../libraries/Common.sol"; import "./BaseFeeManager.t.sol"; /** @@ -10,7 +10,7 @@ import "./BaseFeeManager.t.sol"; * @notice This contract will test the functionality of the feeManager's getFeeAndReward */ contract FeeManagerProcessFeeTest is BaseFeeManagerTest { - function test_baseFeeIsAppliedForNative() public { + function test_baseFeeIsAppliedForNative() public view { //get the fee required by the feeManager Common.Asset memory fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); @@ -18,7 +18,7 @@ contract FeeManagerProcessFeeTest is BaseFeeManagerTest { assertEq(fee.amount, DEFAULT_REPORT_NATIVE_FEE); } - function test_baseFeeIsAppliedForLink() public { + function test_baseFeeIsAppliedForLink() public view { //get the fee required by the feeManager Common.Asset memory fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getLinkQuote(), USER); @@ -378,7 +378,7 @@ contract FeeManagerProcessFeeTest is BaseFeeManagerTest { assertEq(fee.amount, DEFAULT_REPORT_NATIVE_FEE - expectedDiscount); } - function test_reportWithNoExpiryOrFeeReturnsZero() public { + function test_reportWithNoExpiryOrFeeReturnsZero() public view { //get the fee required by the feeManager Common.Asset memory fee = getFee(getV1Report(DEFAULT_FEED_1_V1), getNativeQuote(), USER); @@ -462,7 +462,7 @@ contract FeeManagerProcessFeeTest is BaseFeeManagerTest { setNativeSurcharge(nativeSurcharge, ADMIN); } - function test_getBaseRewardWithLinkQuote() public { + function test_getBaseRewardWithLinkQuote() public view { //get the fee required by the feeManager Common.Asset memory reward = getReward(getV3Report(DEFAULT_FEED_1_V3), getLinkQuote(), USER); @@ -481,7 +481,7 @@ contract FeeManagerProcessFeeTest is BaseFeeManagerTest { assertEq(reward.amount, DEFAULT_REPORT_LINK_FEE / 2); } - function test_getRewardWithNativeQuote() public { + function test_getRewardWithNativeQuote() public view { //get the fee required by the feeManager Common.Asset memory reward = getReward(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); diff --git a/contracts/src/v0.8/llo-feeds/test/fee-manager/FeeManager.processFee.t.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/fee-manager/FeeManager.processFee.t.sol similarity index 99% rename from contracts/src/v0.8/llo-feeds/test/fee-manager/FeeManager.processFee.t.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/fee-manager/FeeManager.processFee.t.sol index f8c1d47d4d..0e0ed8977b 100644 --- a/contracts/src/v0.8/llo-feeds/test/fee-manager/FeeManager.processFee.t.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/test/fee-manager/FeeManager.processFee.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.19; -import {Common} from "../../libraries/Common.sol"; +import {Common} from "../../../libraries/Common.sol"; import "./BaseFeeManager.t.sol"; import {IRewardManager} from "../../interfaces/IRewardManager.sol"; diff --git a/contracts/src/v0.8/llo-feeds/test/fee-manager/FeeManager.processFeeBulk.t.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/fee-manager/FeeManager.processFeeBulk.t.sol similarity index 100% rename from contracts/src/v0.8/llo-feeds/test/fee-manager/FeeManager.processFeeBulk.t.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/fee-manager/FeeManager.processFeeBulk.t.sol diff --git a/contracts/src/v0.8/llo-feeds/test/gas/Gas_VerifierTest.t.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/gas/Gas_VerifierTest.t.sol similarity index 98% rename from contracts/src/v0.8/llo-feeds/test/gas/Gas_VerifierTest.t.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/gas/Gas_VerifierTest.t.sol index 29b488fb32..ee8ba4c3e3 100644 --- a/contracts/src/v0.8/llo-feeds/test/gas/Gas_VerifierTest.t.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/test/gas/Gas_VerifierTest.t.sol @@ -2,8 +2,8 @@ pragma solidity 0.8.19; import {BaseTest, BaseTestWithConfiguredVerifierAndFeeManager} from "../verifier/BaseVerifierTest.t.sol"; -import {SimpleWriteAccessController} from "../../../shared/access/SimpleWriteAccessController.sol"; -import {Common} from "../../libraries/Common.sol"; +import {SimpleWriteAccessController} from "../../../../shared/access/SimpleWriteAccessController.sol"; +import {Common} from "../../../libraries/Common.sol"; import {IRewardManager} from "../../interfaces/IRewardManager.sol"; contract Verifier_setConfig is BaseTest { diff --git a/contracts/src/v0.8/llo-feeds/test/mocks/ErroredVerifier.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/mocks/ErroredVerifier.sol similarity index 66% rename from contracts/src/v0.8/llo-feeds/test/mocks/ErroredVerifier.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/mocks/ErroredVerifier.sol index 01cb1a5061..e9dcd589e2 100644 --- a/contracts/src/v0.8/llo-feeds/test/mocks/ErroredVerifier.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/test/mocks/ErroredVerifier.sol @@ -2,13 +2,24 @@ pragma solidity 0.8.19; import {IVerifier} from "../../interfaces/IVerifier.sol"; -import {Common} from "../../libraries/Common.sol"; +import {Common} from "../../../libraries/Common.sol"; contract ErroredVerifier is IVerifier { function supportsInterface(bytes4 interfaceId) public pure override returns (bool) { return interfaceId == this.verify.selector; } + //define each of the errors thrown in the revert below + + error FailedToVerify(); + error FailedToSetConfig(); + error FailedToActivateConfig(); + error FailedToDeactivateConfig(); + error FailedToActivateFeed(); + error FailedToDeactivateFeed(); + error FailedToGetLatestConfigDigestAndEpoch(); + error FailedToGetLatestConfigDetails(); + function verify( bytes memory, /** @@ -26,7 +37,7 @@ contract ErroredVerifier is IVerifier { bytes memory ) { - revert("Failed to verify"); + revert FailedToVerify(); } function setConfig( @@ -39,7 +50,7 @@ contract ErroredVerifier is IVerifier { bytes memory, Common.AddressAndWeight[] memory ) external pure override { - revert("Failed to set config"); + revert FailedToSetConfig(); } function setConfigFromSource( @@ -55,30 +66,30 @@ contract ErroredVerifier is IVerifier { bytes memory, Common.AddressAndWeight[] memory ) external pure override { - revert("Failed to set config"); + revert FailedToSetConfig(); } function activateConfig(bytes32, bytes32) external pure { - revert("Failed to activate config"); + revert FailedToActivateConfig(); } function deactivateConfig(bytes32, bytes32) external pure { - revert("Failed to deactivate config"); + revert FailedToDeactivateConfig(); } function activateFeed(bytes32) external pure { - revert("Failed to activate feed"); + revert FailedToActivateFeed(); } function deactivateFeed(bytes32) external pure { - revert("Failed to deactivate feed"); + revert FailedToDeactivateFeed(); } function latestConfigDigestAndEpoch(bytes32) external pure override returns (bool, bytes32, uint32) { - revert("Failed to get latest config digest and epoch"); + revert FailedToGetLatestConfigDigestAndEpoch(); } function latestConfigDetails(bytes32) external pure override returns (uint32, uint32, bytes32) { - revert("Failed to get latest config details"); + revert FailedToGetLatestConfigDetails(); } } diff --git a/contracts/src/v0.8/llo-feeds/test/mocks/ExposedVerifier.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/mocks/ExposedVerifier.sol similarity index 100% rename from contracts/src/v0.8/llo-feeds/test/mocks/ExposedVerifier.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/mocks/ExposedVerifier.sol diff --git a/contracts/src/v0.8/llo-feeds/test/mocks/FeeManagerProxy.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/mocks/FeeManagerProxy.sol similarity index 61% rename from contracts/src/v0.8/llo-feeds/test/mocks/FeeManagerProxy.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/mocks/FeeManagerProxy.sol index 16935f69d6..9abb4c50c2 100644 --- a/contracts/src/v0.8/llo-feeds/test/mocks/FeeManagerProxy.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/test/mocks/FeeManagerProxy.sol @@ -1,20 +1,20 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.19; -import "../../interfaces/IFeeManager.sol"; +import {IFeeManager} from "../../interfaces/IFeeManager.sol"; contract FeeManagerProxy { - IFeeManager internal i_feeManager; + IFeeManager internal s_feeManager; function processFee(bytes calldata payload, bytes calldata parameterPayload) public payable { - i_feeManager.processFee{value: msg.value}(payload, parameterPayload, msg.sender); + s_feeManager.processFee{value: msg.value}(payload, parameterPayload, msg.sender); } function processFeeBulk(bytes[] calldata payloads, bytes calldata parameterPayload) public payable { - i_feeManager.processFeeBulk{value: msg.value}(payloads, parameterPayload, msg.sender); + s_feeManager.processFeeBulk{value: msg.value}(payloads, parameterPayload, msg.sender); } function setFeeManager(IFeeManager feeManager) public { - i_feeManager = feeManager; + s_feeManager = feeManager; } } diff --git a/contracts/src/v0.8/llo-feeds/test/reward-manager/BaseRewardManager.t.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/reward-manager/BaseRewardManager.t.sol similarity index 97% rename from contracts/src/v0.8/llo-feeds/test/reward-manager/BaseRewardManager.t.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/reward-manager/BaseRewardManager.t.sol index 65481513f0..c7bd7528c3 100644 --- a/contracts/src/v0.8/llo-feeds/test/reward-manager/BaseRewardManager.t.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/test/reward-manager/BaseRewardManager.t.sol @@ -2,9 +2,9 @@ pragma solidity 0.8.19; import {Test} from "forge-std/Test.sol"; -import {ERC20Mock} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/mocks/ERC20Mock.sol"; -import {RewardManager} from "../../RewardManager.sol"; -import {Common} from "../../libraries/Common.sol"; +import {ERC20Mock} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/mocks/ERC20Mock.sol"; +import {RewardManager} from "../../../v0.3.0/RewardManager.sol"; +import {Common} from "../../../libraries/Common.sol"; import {IRewardManager} from "../../interfaces/IRewardManager.sol"; /** diff --git a/contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.claim.t.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/reward-manager/RewardManager.claim.t.sol similarity index 99% rename from contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.claim.t.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/reward-manager/RewardManager.claim.t.sol index 5f07d36c72..efbe9fd6b3 100644 --- a/contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.claim.t.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/test/reward-manager/RewardManager.claim.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.19; import {BaseRewardManagerTest} from "./BaseRewardManager.t.sol"; -import {Common} from "../../libraries/Common.sol"; +import {Common} from "../../../libraries/Common.sol"; /** * @title BaseRewardManagerTest @@ -574,7 +574,7 @@ contract RewardManagerRecipientClaimMultiplePoolsTest is BaseRewardManagerTest { assertEq(poolIds[1], ZERO_POOL_ID); } - function test_getRewardsAvailableToRecipientInNoPools() public { + function test_getRewardsAvailableToRecipientInNoPools() public view { //get index 0 as this recipient is in both default pools bytes32[] memory poolIds = rewardManager.getAvailableRewardPoolIds(FEE_MANAGER, 0, type(uint256).max); diff --git a/contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.general.t.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/reward-manager/RewardManager.general.t.sol similarity index 92% rename from contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.general.t.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/reward-manager/RewardManager.general.t.sol index 7fde76d528..baff388769 100644 --- a/contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.general.t.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/test/reward-manager/RewardManager.general.t.sol @@ -2,8 +2,8 @@ pragma solidity 0.8.19; import {BaseRewardManagerTest} from "./BaseRewardManager.t.sol"; -import {RewardManager} from "../../RewardManager.sol"; -import {ERC20Mock} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/mocks/ERC20Mock.sol"; +import {RewardManager} from "../../../v0.3.0/RewardManager.sol"; +import {ERC20Mock} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/mocks/ERC20Mock.sol"; import {IRewardManager} from "../../interfaces/IRewardManager.sol"; /** diff --git a/contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.payRecipients.t.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/reward-manager/RewardManager.payRecipients.t.sol similarity index 100% rename from contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.payRecipients.t.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/reward-manager/RewardManager.payRecipients.t.sol diff --git a/contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.setRecipients.t.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/reward-manager/RewardManager.setRecipients.t.sol similarity index 98% rename from contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.setRecipients.t.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/reward-manager/RewardManager.setRecipients.t.sol index 1cf5b51f62..d3e6990bd9 100644 --- a/contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.setRecipients.t.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/test/reward-manager/RewardManager.setRecipients.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.19; import {BaseRewardManagerTest} from "./BaseRewardManager.t.sol"; -import {Common} from "../../libraries/Common.sol"; +import {Common} from "../../../libraries/Common.sol"; /** * @title BaseRewardManagerTest diff --git a/contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.updateRewardRecipients.t.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/reward-manager/RewardManager.updateRewardRecipients.t.sol similarity index 99% rename from contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.updateRewardRecipients.t.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/reward-manager/RewardManager.updateRewardRecipients.t.sol index 6c51a0fbfd..0d3a2b69b3 100644 --- a/contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.updateRewardRecipients.t.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/test/reward-manager/RewardManager.updateRewardRecipients.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.19; import {BaseRewardManagerTest} from "./BaseRewardManager.t.sol"; -import {Common} from "../../libraries/Common.sol"; +import {Common} from "../../../libraries/Common.sol"; /** * @title BaseRewardManagerTest diff --git a/contracts/src/v0.8/llo-feeds/test/verifier/BaseVerifierTest.t.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/BaseVerifierTest.t.sol similarity index 97% rename from contracts/src/v0.8/llo-feeds/test/verifier/BaseVerifierTest.t.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/BaseVerifierTest.t.sol index daf3187503..4d65414676 100644 --- a/contracts/src/v0.8/llo-feeds/test/verifier/BaseVerifierTest.t.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/BaseVerifierTest.t.sol @@ -3,17 +3,16 @@ pragma solidity 0.8.19; import {Test} from "forge-std/Test.sol"; import {VerifierProxy} from "../../VerifierProxy.sol"; -import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; +import {IERC165} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; import {IVerifier} from "../../interfaces/IVerifier.sol"; import {ErroredVerifier} from "../mocks/ErroredVerifier.sol"; import {Verifier} from "../../Verifier.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; -import {AccessControllerInterface} from "../../../shared/interfaces/AccessControllerInterface.sol"; -import {FeeManager} from "../../FeeManager.sol"; -import {Common} from "../../libraries/Common.sol"; -import {ERC20Mock} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/mocks/ERC20Mock.sol"; -import {WERC20Mock} from "../../../shared/mocks/WERC20Mock.sol"; +import {AccessControllerInterface} from "../../../../shared/interfaces/AccessControllerInterface.sol"; import {FeeManager} from "../../FeeManager.sol"; +import {Common} from "../../../libraries/Common.sol"; +import {ERC20Mock} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/mocks/ERC20Mock.sol"; +import {WERC20Mock} from "../../../../shared/mocks/WERC20Mock.sol"; import {RewardManager} from "../../RewardManager.sol"; contract BaseTest is Test { diff --git a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierActivateConfigTest.t.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierActivateConfigTest.t.sol similarity index 97% rename from contracts/src/v0.8/llo-feeds/test/verifier/VerifierActivateConfigTest.t.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierActivateConfigTest.t.sol index f53c26ba19..99daabe206 100644 --- a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierActivateConfigTest.t.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierActivateConfigTest.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.19; import {BaseTestWithConfiguredVerifierAndFeeManager, BaseTestWithMultipleConfiguredDigests} from "./BaseVerifierTest.t.sol"; -import {Verifier} from "../../Verifier.sol"; +import {Verifier} from "../../../v0.3.0/Verifier.sol"; contract VerifierActivateConfigTest is BaseTestWithConfiguredVerifierAndFeeManager { function test_revertsIfNotOwner() public { diff --git a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierDeactivateFeedTest.t.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierDeactivateFeedTest.t.sol similarity index 98% rename from contracts/src/v0.8/llo-feeds/test/verifier/VerifierDeactivateFeedTest.t.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierDeactivateFeedTest.t.sol index 97647c8863..fb52c1c93e 100644 --- a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierDeactivateFeedTest.t.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierDeactivateFeedTest.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.19; import {BaseTestWithConfiguredVerifierAndFeeManager, BaseTestWithMultipleConfiguredDigests} from "./BaseVerifierTest.t.sol"; -import {Verifier} from "../../Verifier.sol"; +import {Verifier} from "../../../v0.3.0/Verifier.sol"; contract VerifierActivateFeedTest is BaseTestWithConfiguredVerifierAndFeeManager { function test_revertsIfNotOwnerActivateFeed() public { diff --git a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierProxyConstructorTest.t.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierProxyConstructorTest.t.sol similarity index 77% rename from contracts/src/v0.8/llo-feeds/test/verifier/VerifierProxyConstructorTest.t.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierProxyConstructorTest.t.sol index b085dc8a65..82efd8907b 100644 --- a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierProxyConstructorTest.t.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierProxyConstructorTest.t.sol @@ -2,8 +2,8 @@ pragma solidity 0.8.19; import {BaseTest} from "./BaseVerifierTest.t.sol"; -import {VerifierProxy} from "../../VerifierProxy.sol"; -import {AccessControllerInterface} from "../../../shared/interfaces/AccessControllerInterface.sol"; +import {VerifierProxy} from "../../../v0.3.0/VerifierProxy.sol"; +import {AccessControllerInterface} from "../../../../shared/interfaces/AccessControllerInterface.sol"; contract VerifierProxyConstructorTest is BaseTest { function test_correctlySetsTheOwner() public { @@ -17,7 +17,7 @@ contract VerifierProxyConstructorTest is BaseTest { assertEq(address(proxy.s_accessController()), accessControllerAddr); } - function test_correctlySetsVersion() public { + function test_correctlySetsVersion() public view { string memory version = s_verifierProxy.typeAndVersion(); assertEq(version, "VerifierProxy 2.0.0"); } diff --git a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierProxyInitializeVerifierTest.t.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierProxyInitializeVerifierTest.t.sol similarity index 93% rename from contracts/src/v0.8/llo-feeds/test/verifier/VerifierProxyInitializeVerifierTest.t.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierProxyInitializeVerifierTest.t.sol index e02b14fe56..5537d273be 100644 --- a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierProxyInitializeVerifierTest.t.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierProxyInitializeVerifierTest.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.19; import {BaseTest} from "./BaseVerifierTest.t.sol"; -import {VerifierProxy} from "../../VerifierProxy.sol"; +import {VerifierProxy} from "../../../v0.3.0/VerifierProxy.sol"; contract VerifierProxyInitializeVerifierTest is BaseTest { bytes32 latestDigest; diff --git a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierProxySetAccessControllerTest.t.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierProxySetAccessControllerTest.t.sol similarity index 92% rename from contracts/src/v0.8/llo-feeds/test/verifier/VerifierProxySetAccessControllerTest.t.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierProxySetAccessControllerTest.t.sol index 04889e0d5f..03bd6d97ee 100644 --- a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierProxySetAccessControllerTest.t.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierProxySetAccessControllerTest.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.19; import {BaseTest} from "./BaseVerifierTest.t.sol"; -import {AccessControllerInterface} from "../../../shared/interfaces/AccessControllerInterface.sol"; +import {AccessControllerInterface} from "../../../../shared/interfaces/AccessControllerInterface.sol"; contract VerifierProxySetAccessControllerTest is BaseTest { event AccessControllerSet(address oldAccessController, address newAccessController); diff --git a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierProxySetVerifierTest.t.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierProxySetVerifierTest.t.sol similarity index 88% rename from contracts/src/v0.8/llo-feeds/test/verifier/VerifierProxySetVerifierTest.t.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierProxySetVerifierTest.t.sol index ea23f880ba..78e5ff0766 100644 --- a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierProxySetVerifierTest.t.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierProxySetVerifierTest.t.sol @@ -3,9 +3,9 @@ pragma solidity 0.8.19; import {BaseTestWithConfiguredVerifierAndFeeManager} from "./BaseVerifierTest.t.sol"; import {IVerifier} from "../../interfaces/IVerifier.sol"; -import {VerifierProxy} from "../../VerifierProxy.sol"; -import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; -import {Common} from "../../libraries/Common.sol"; +import {VerifierProxy} from "../../../v0.3.0/VerifierProxy.sol"; +import {IERC165} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; +import {Common} from "../../../libraries/Common.sol"; contract VerifierProxyInitializeVerifierTest is BaseTestWithConfiguredVerifierAndFeeManager { function test_revertsIfNotCorrectVerifier() public { diff --git a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierProxyTest.t.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierProxyTest.t.sol similarity index 87% rename from contracts/src/v0.8/llo-feeds/test/verifier/VerifierProxyTest.t.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierProxyTest.t.sol index ea7e02d740..441626e575 100644 --- a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierProxyTest.t.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierProxyTest.t.sol @@ -2,8 +2,8 @@ pragma solidity 0.8.19; import {BaseTestWithConfiguredVerifierAndFeeManager} from "./BaseVerifierTest.t.sol"; -import {VerifierProxy} from "../../VerifierProxy.sol"; -import {FeeManager} from "../../FeeManager.sol"; +import {VerifierProxy} from "../../../v0.3.0/VerifierProxy.sol"; +import {FeeManager} from "../../../v0.3.0/FeeManager.sol"; contract VerifierProxyInitializeVerifierTest is BaseTestWithConfiguredVerifierAndFeeManager { function test_setFeeManagerZeroAddress() public { diff --git a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierProxyUnsetVerifierTest.t.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierProxyUnsetVerifierTest.t.sol similarity index 95% rename from contracts/src/v0.8/llo-feeds/test/verifier/VerifierProxyUnsetVerifierTest.t.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierProxyUnsetVerifierTest.t.sol index 746aa95574..a51c67e336 100644 --- a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierProxyUnsetVerifierTest.t.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierProxyUnsetVerifierTest.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.19; import {BaseTest, BaseTestWithConfiguredVerifierAndFeeManager} from "./BaseVerifierTest.t.sol"; -import {VerifierProxy} from "../../VerifierProxy.sol"; +import {VerifierProxy} from "../../../v0.3.0/VerifierProxy.sol"; contract VerifierProxyUnsetVerifierTest is BaseTest { function test_revertsIfNotAdmin() public { diff --git a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierSetConfigFromSourceTest.t.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierSetConfigFromSourceTest.t.sol similarity index 98% rename from contracts/src/v0.8/llo-feeds/test/verifier/VerifierSetConfigFromSourceTest.t.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierSetConfigFromSourceTest.t.sol index 0cd5902161..9ee9b5272a 100644 --- a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierSetConfigFromSourceTest.t.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierSetConfigFromSourceTest.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.19; import {BaseTest, BaseTestWithMultipleConfiguredDigests} from "./BaseVerifierTest.t.sol"; -import {Common} from "../../libraries/Common.sol"; +import {Common} from "../../../libraries/Common.sol"; contract VerifierSetConfigFromSourceTest is BaseTest { function setUp() public virtual override { diff --git a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierSetConfigTest.t.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierSetConfigTest.t.sol similarity index 98% rename from contracts/src/v0.8/llo-feeds/test/verifier/VerifierSetConfigTest.t.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierSetConfigTest.t.sol index a4e15dcdd4..972ead8123 100644 --- a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierSetConfigTest.t.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierSetConfigTest.t.sol @@ -2,8 +2,8 @@ pragma solidity 0.8.19; import {BaseTest, BaseTestWithMultipleConfiguredDigests} from "./BaseVerifierTest.t.sol"; -import {Verifier} from "../../Verifier.sol"; -import {Common} from "../../libraries/Common.sol"; +import {Verifier} from "../../../v0.3.0/Verifier.sol"; +import {Common} from "../../../libraries/Common.sol"; contract VerifierSetConfigTest is BaseTest { function setUp() public virtual override { diff --git a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierTest.t.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierTest.t.sol similarity index 88% rename from contracts/src/v0.8/llo-feeds/test/verifier/VerifierTest.t.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierTest.t.sol index 2857b8f4d3..81f65f0c6e 100644 --- a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierTest.t.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierTest.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.19; import {BaseTest} from "./BaseVerifierTest.t.sol"; -import {Verifier} from "../../Verifier.sol"; +import {Verifier} from "../../../v0.3.0/Verifier.sol"; contract VerifierConstructorTest is BaseTest { function test_revertsIfInitializedWithEmptyVerifierProxy() public { @@ -30,12 +30,12 @@ contract VerifierConstructorTest is BaseTest { } contract VerifierSupportsInterfaceTest is BaseTest { - function test_falseIfIsNotCorrectInterface() public { + function test_falseIfIsNotCorrectInterface() public view { bool isInterface = s_verifier.supportsInterface(bytes4("abcd")); assertEq(isInterface, false); } - function test_trueIfIsCorrectInterface() public { + function test_trueIfIsCorrectInterface() public view { bool isInterface = s_verifier.supportsInterface(Verifier.verify.selector); assertEq(isInterface, true); } diff --git a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierTestBillingReport.t.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierTestBillingReport.t.sol similarity index 100% rename from contracts/src/v0.8/llo-feeds/test/verifier/VerifierTestBillingReport.t.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierTestBillingReport.t.sol diff --git a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierUnsetConfigTest.t.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierUnsetConfigTest.t.sol similarity index 97% rename from contracts/src/v0.8/llo-feeds/test/verifier/VerifierUnsetConfigTest.t.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierUnsetConfigTest.t.sol index cc3c33331d..e192a2e9e0 100644 --- a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierUnsetConfigTest.t.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierUnsetConfigTest.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.19; import {BaseTestWithMultipleConfiguredDigests} from "./BaseVerifierTest.t.sol"; -import {Verifier} from "../../Verifier.sol"; +import {Verifier} from "../../../v0.3.0/Verifier.sol"; contract VerificationdeactivateConfigWhenThereAreMultipleDigestsTest is BaseTestWithMultipleConfiguredDigests { function test_revertsIfCalledByNonOwner() public { diff --git a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierVerifyTest.t.sol b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierVerifyTest.t.sol similarity index 97% rename from contracts/src/v0.8/llo-feeds/test/verifier/VerifierVerifyTest.t.sol rename to contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierVerifyTest.t.sol index db7be5ca54..1c14ba974c 100644 --- a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierVerifyTest.t.sol +++ b/contracts/src/v0.8/llo-feeds/v0.3.0/test/verifier/VerifierVerifyTest.t.sol @@ -2,10 +2,10 @@ pragma solidity 0.8.19; import {BaseTestWithConfiguredVerifierAndFeeManager} from "./BaseVerifierTest.t.sol"; -import {Verifier} from "../../Verifier.sol"; -import {VerifierProxy} from "../../VerifierProxy.sol"; -import {AccessControllerInterface} from "../../../shared/interfaces/AccessControllerInterface.sol"; -import {Common} from "../../libraries/Common.sol"; +import {Verifier} from "../../../v0.3.0/Verifier.sol"; +import {VerifierProxy} from "../../../v0.3.0/VerifierProxy.sol"; +import {AccessControllerInterface} from "../../../../shared/interfaces/AccessControllerInterface.sol"; +import {Common} from "../../../libraries/Common.sol"; contract VerifierVerifyTest is BaseTestWithConfiguredVerifierAndFeeManager { bytes32[3] internal s_reportContext; @@ -32,7 +32,7 @@ contract VerifierVerifyTest is BaseTestWithConfiguredVerifierAndFeeManager { ); } - function assertReportsEqual(bytes memory response, V1Report memory testReport) public { + function assertReportsEqual(bytes memory response, V1Report memory testReport) public pure { ( bytes32 feedId, uint32 timestamp, diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/DestinationFeeManager.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/DestinationFeeManager.sol new file mode 100644 index 0000000000..38d93de5cb --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/DestinationFeeManager.sol @@ -0,0 +1,557 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; +import {TypeAndVersionInterface} from "../../interfaces/TypeAndVersionInterface.sol"; +import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; +import {Common} from "../libraries/Common.sol"; +import {IWERC20} from "../../shared/interfaces/IWERC20.sol"; +import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC20.sol"; +import {Math} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/Math.sol"; +import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; +import {IDestinationRewardManager} from "./interfaces/IDestinationRewardManager.sol"; +import {IDestinationFeeManager} from "./interfaces/IDestinationFeeManager.sol"; + +/** + * @title FeeManager + * @author Michael Fletcher + * @author Austin Born + * @notice This contract is used for the handling of fees required for users verifying reports. + */ +contract DestinationFeeManager is IDestinationFeeManager, ConfirmedOwner, TypeAndVersionInterface { + using SafeERC20 for IERC20; + + /// @notice list of subscribers and their discounts subscriberDiscounts[subscriber][feedId][token] + mapping(address => mapping(bytes32 => mapping(address => uint256))) public s_subscriberDiscounts; + + /// @notice keep track of any subsidised link that is owed to the reward manager. + mapping(bytes32 => uint256) public s_linkDeficit; + + /// @notice the total discount that can be applied to a fee, 1e18 = 100% discount + uint64 private constant PERCENTAGE_SCALAR = 1e18; + + /// @notice the LINK token address + address public immutable i_linkAddress; + + /// @notice the native token address + address public immutable i_nativeAddress; + + /// @notice the verifier address + mapping(address => address) public s_verifierAddressList; + + /// @notice the reward manager address + IDestinationRewardManager public i_rewardManager; + + // @notice the mask to apply to get the report version + bytes32 private constant REPORT_VERSION_MASK = 0xffff000000000000000000000000000000000000000000000000000000000000; + + // @notice the different report versions + bytes32 private constant REPORT_V1 = 0x0001000000000000000000000000000000000000000000000000000000000000; + + /// @notice the surcharge fee to be paid if paying in native + uint256 public s_nativeSurcharge; + + /// @notice the error thrown if the discount or surcharge is invalid + error InvalidSurcharge(); + + /// @notice the error thrown if the discount is invalid + error InvalidDiscount(); + + /// @notice the error thrown if the address is invalid + error InvalidAddress(); + + /// @notice thrown if msg.value is supplied with a bad quote + error InvalidDeposit(); + + /// @notice thrown if a report has expired + error ExpiredReport(); + + /// @notice thrown if a report has no quote + error InvalidQuote(); + + // @notice thrown when the caller is not authorized + error Unauthorized(); + + // @notice thrown when trying to clear a zero deficit + error ZeroDeficit(); + + /// @notice thrown when trying to pay an address that cannot except funds + error InvalidReceivingAddress(); + + /// @notice thrown when trying to bulk verify reports where theres not a matching number of poolIds + error PoolIdMismatch(); + + /// @notice Emitted whenever a subscriber's discount is updated + /// @param subscriber address of the subscriber to update discounts for + /// @param feedId Feed ID for the discount + /// @param token Token address for the discount + /// @param discount Discount to apply, in relation to the PERCENTAGE_SCALAR + event SubscriberDiscountUpdated(address indexed subscriber, bytes32 indexed feedId, address token, uint64 discount); + + /// @notice Emitted when updating the native surcharge + /// @param newSurcharge Surcharge amount to apply relative to PERCENTAGE_SCALAR + event NativeSurchargeUpdated(uint64 newSurcharge); + + /// @notice Emits when this contract does not have enough LINK to send to the reward manager when paying in native + /// @param rewards Config digest and link fees which could not be subsidised + event InsufficientLink(IDestinationRewardManager.FeePayment[] rewards); + + /// @notice Emitted when funds are withdrawn + /// @param adminAddress Address of the admin + /// @param recipient Address of the recipient + /// @param assetAddress Address of the asset withdrawn + /// @param quantity Amount of the asset withdrawn + event Withdraw(address adminAddress, address recipient, address assetAddress, uint192 quantity); + + /// @notice Emits when a deficit has been cleared for a particular config digest + /// @param configDigest Config digest of the deficit cleared + /// @param linkQuantity Amount of LINK required to pay the deficit + event LinkDeficitCleared(bytes32 indexed configDigest, uint256 linkQuantity); + + /// @notice Emits when a fee has been processed + /// @param configDigest Config digest of the fee processed + /// @param subscriber Address of the subscriber who paid the fee + /// @param fee Fee paid + /// @param reward Reward paid + /// @param appliedDiscount Discount applied to the fee + event DiscountApplied( + bytes32 indexed configDigest, + address indexed subscriber, + Common.Asset fee, + Common.Asset reward, + uint256 appliedDiscount + ); + + /** + * @notice Construct the FeeManager contract + * @param _linkAddress The address of the LINK token + * @param _nativeAddress The address of the wrapped ERC-20 version of the native token (represents fee in native or wrapped) + * @param _verifierAddress The address of the verifier contract + * @param _rewardManagerAddress The address of the reward manager contract + */ + constructor( + address _linkAddress, + address _nativeAddress, + address _verifierAddress, + address _rewardManagerAddress + ) ConfirmedOwner(msg.sender) { + if ( + _linkAddress == address(0) || + _nativeAddress == address(0) || + _verifierAddress == address(0) || + _rewardManagerAddress == address(0) + ) revert InvalidAddress(); + + i_linkAddress = _linkAddress; + i_nativeAddress = _nativeAddress; + s_verifierAddressList[_verifierAddress] = _verifierAddress; + i_rewardManager = IDestinationRewardManager(_rewardManagerAddress); + + IERC20(i_linkAddress).approve(address(i_rewardManager), type(uint256).max); + } + + modifier onlyOwnerOrVerifier() { + if (msg.sender != s_verifierAddressList[msg.sender] && msg.sender != owner()) revert Unauthorized(); + _; + } + + modifier onlyVerifier() { + if (msg.sender != s_verifierAddressList[msg.sender]) revert Unauthorized(); + _; + } + + /// @inheritdoc TypeAndVersionInterface + function typeAndVersion() external pure override returns (string memory) { + return "DestinationFeeManager 1.0.0"; + } + + /// @inheritdoc IERC165 + function supportsInterface(bytes4 interfaceId) external pure override returns (bool) { + //for each function in IDestinationFeeManager we need to check if it matches the selector + return + interfaceId == this.getFeeAndReward.selector || + interfaceId == this.setNativeSurcharge.selector || + interfaceId == this.updateSubscriberDiscount.selector || + interfaceId == this.withdraw.selector || + interfaceId == this.linkAvailableForPayment.selector || + interfaceId == this.payLinkDeficit.selector || + interfaceId == this.addVerifier.selector || + interfaceId == this.removeVerifier.selector || + interfaceId == this.processFee.selector || + interfaceId == this.processFeeBulk.selector || + interfaceId == this.setFeeRecipients.selector; + } + + function processFee( + bytes32 recipient, + bytes calldata payload, + bytes calldata parameterPayload, + address subscriber + ) external payable override onlyVerifier { + (Common.Asset memory fee, Common.Asset memory reward, uint256 appliedDiscount) = _calculateFee( + payload, + parameterPayload, + subscriber + ); + + if (fee.amount == 0) { + _tryReturnChange(subscriber, msg.value); + return; + } + + IDestinationFeeManager.FeeAndReward[] memory feeAndReward = new IDestinationFeeManager.FeeAndReward[](1); + feeAndReward[0] = IDestinationFeeManager.FeeAndReward(recipient, fee, reward, appliedDiscount); + + if (fee.assetAddress == i_linkAddress) { + _handleFeesAndRewards(subscriber, feeAndReward, 1, 0); + } else { + _handleFeesAndRewards(subscriber, feeAndReward, 0, 1); + } + } + + /// @inheritdoc IDestinationFeeManager + function processFeeBulk( + bytes32[] memory poolIds, + bytes[] calldata payloads, + bytes calldata parameterPayload, + address subscriber + ) external payable override onlyVerifier { + //poolIDs are mapped to payloads, so they should be the same length + if (poolIds.length != payloads.length) revert PoolIdMismatch(); + + IDestinationFeeManager.FeeAndReward[] memory feesAndRewards = new IDestinationFeeManager.FeeAndReward[]( + payloads.length + ); + + //keep track of the number of fees to prevent over initialising the FeePayment array within _convertToLinkAndNativeFees + uint256 numberOfLinkFees; + uint256 numberOfNativeFees; + + uint256 feesAndRewardsIndex; + for (uint256 i; i < payloads.length; ++i) { + if (poolIds[i] == bytes32(0)) revert InvalidAddress(); + + (Common.Asset memory fee, Common.Asset memory reward, uint256 appliedDiscount) = _calculateFee( + payloads[i], + parameterPayload, + subscriber + ); + + if (fee.amount != 0) { + feesAndRewards[feesAndRewardsIndex++] = IDestinationFeeManager.FeeAndReward( + poolIds[i], + fee, + reward, + appliedDiscount + ); + + unchecked { + //keep track of some tallys to make downstream calculations more efficient + if (fee.assetAddress == i_linkAddress) { + ++numberOfLinkFees; + } else { + ++numberOfNativeFees; + } + } + } + } + + if (numberOfLinkFees != 0 || numberOfNativeFees != 0) { + _handleFeesAndRewards(subscriber, feesAndRewards, numberOfLinkFees, numberOfNativeFees); + } else { + _tryReturnChange(subscriber, msg.value); + } + } + + /// @inheritdoc IDestinationFeeManager + function getFeeAndReward( + address subscriber, + bytes memory report, + address quoteAddress + ) public view returns (Common.Asset memory, Common.Asset memory, uint256) { + Common.Asset memory fee; + Common.Asset memory reward; + + //get the feedId from the report + bytes32 feedId = bytes32(report); + + //the report needs to be a support version + bytes32 reportVersion = _getReportVersion(feedId); + + //version 1 of the reports don't require quotes, so the fee will be 0 + if (reportVersion == REPORT_V1) { + fee.assetAddress = i_nativeAddress; + reward.assetAddress = i_linkAddress; + return (fee, reward, 0); + } + + //verify the quote payload is a supported token + if (quoteAddress != i_nativeAddress && quoteAddress != i_linkAddress) { + revert InvalidQuote(); + } + + //decode the report depending on the version + uint256 linkQuantity; + uint256 nativeQuantity; + uint256 expiresAt; + (, , , nativeQuantity, linkQuantity, expiresAt) = abi.decode( + report, + (bytes32, uint32, uint32, uint192, uint192, uint32) + ); + + //read the timestamp bytes from the report data and verify it has not expired + if (expiresAt < block.timestamp) { + revert ExpiredReport(); + } + + //get the discount being applied + uint256 discount = s_subscriberDiscounts[subscriber][feedId][quoteAddress]; + + //the reward is always set in LINK + reward.assetAddress = i_linkAddress; + reward.amount = Math.ceilDiv(linkQuantity * (PERCENTAGE_SCALAR - discount), PERCENTAGE_SCALAR); + + //calculate either the LINK fee or native fee if it's within the report + if (quoteAddress == i_linkAddress) { + fee.assetAddress = i_linkAddress; + fee.amount = reward.amount; + } else { + uint256 surchargedFee = Math.ceilDiv(nativeQuantity * (PERCENTAGE_SCALAR + s_nativeSurcharge), PERCENTAGE_SCALAR); + + fee.assetAddress = i_nativeAddress; + fee.amount = Math.ceilDiv(surchargedFee * (PERCENTAGE_SCALAR - discount), PERCENTAGE_SCALAR); + } + + //return the fee + return (fee, reward, discount); + } + + /// @inheritdoc IDestinationFeeManager + function setFeeRecipients( + bytes32 configDigest, + Common.AddressAndWeight[] calldata rewardRecipientAndWeights + ) external onlyOwnerOrVerifier { + i_rewardManager.setRewardRecipients(configDigest, rewardRecipientAndWeights); + } + + /// @inheritdoc IDestinationFeeManager + function setNativeSurcharge(uint64 surcharge) external onlyOwner { + if (surcharge > PERCENTAGE_SCALAR) revert InvalidSurcharge(); + + s_nativeSurcharge = surcharge; + + emit NativeSurchargeUpdated(surcharge); + } + + /// @inheritdoc IDestinationFeeManager + function updateSubscriberDiscount( + address subscriber, + bytes32 feedId, + address token, + uint64 discount + ) external onlyOwner { + //make sure the discount is not greater than the total discount that can be applied + if (discount > PERCENTAGE_SCALAR) revert InvalidDiscount(); + //make sure the token is either LINK or native + if (token != i_linkAddress && token != i_nativeAddress) revert InvalidAddress(); + + s_subscriberDiscounts[subscriber][feedId][token] = discount; + + emit SubscriberDiscountUpdated(subscriber, feedId, token, discount); + } + + /// @inheritdoc IDestinationFeeManager + function withdraw(address assetAddress, address recipient, uint192 quantity) external onlyOwner { + //address 0 is used to withdraw native in the context of withdrawing + if (assetAddress == address(0)) { + (bool success, ) = payable(recipient).call{value: quantity}(""); + + if (!success) revert InvalidReceivingAddress(); + return; + } + + //withdraw the requested asset + IERC20(assetAddress).safeTransfer(recipient, quantity); + + //emit event when funds are withdrawn + emit Withdraw(msg.sender, recipient, assetAddress, uint192(quantity)); + } + + /// @inheritdoc IDestinationFeeManager + function linkAvailableForPayment() external view returns (uint256) { + //return the amount of LINK this contact has available to pay rewards + return IERC20(i_linkAddress).balanceOf(address(this)); + } + + /** + * @notice Gets the current version of the report that is encoded as the last two bytes of the feed + * @param feedId feed id to get the report version for + */ + function _getReportVersion(bytes32 feedId) internal pure returns (bytes32) { + return REPORT_VERSION_MASK & feedId; + } + + function _calculateFee( + bytes calldata payload, + bytes calldata parameterPayload, + address subscriber + ) internal view returns (Common.Asset memory, Common.Asset memory, uint256) { + if (subscriber == address(this)) revert InvalidAddress(); + + //decode the report from the payload + (, bytes memory report) = abi.decode(payload, (bytes32[3], bytes)); + + //get the feedId from the report + bytes32 feedId = bytes32(report); + + //v1 doesn't need a quote payload, so skip the decoding + address quote; + if (_getReportVersion(feedId) != REPORT_V1) { + //decode the quote from the bytes + (quote) = abi.decode(parameterPayload, (address)); + } + + //decode the fee, it will always be native or LINK + return getFeeAndReward(subscriber, report, quote); + } + + function _handleFeesAndRewards( + address subscriber, + IDestinationFeeManager.FeeAndReward[] memory feesAndRewards, + uint256 numberOfLinkFees, + uint256 numberOfNativeFees + ) internal { + IDestinationRewardManager.FeePayment[] memory linkRewards = new IDestinationRewardManager.FeePayment[]( + numberOfLinkFees + ); + IDestinationRewardManager.FeePayment[] memory nativeFeeLinkRewards = new IDestinationRewardManager.FeePayment[]( + numberOfNativeFees + ); + + uint256 totalNativeFee; + uint256 totalNativeFeeLinkValue; + + uint256 linkRewardsIndex; + uint256 nativeFeeLinkRewardsIndex; + + uint256 totalNumberOfFees = numberOfLinkFees + numberOfNativeFees; + for (uint256 i; i < totalNumberOfFees; ++i) { + if (feesAndRewards[i].fee.assetAddress == i_linkAddress) { + linkRewards[linkRewardsIndex++] = IDestinationRewardManager.FeePayment( + feesAndRewards[i].configDigest, + uint192(feesAndRewards[i].reward.amount) + ); + } else { + nativeFeeLinkRewards[nativeFeeLinkRewardsIndex++] = IDestinationRewardManager.FeePayment( + feesAndRewards[i].configDigest, + uint192(feesAndRewards[i].reward.amount) + ); + totalNativeFee += feesAndRewards[i].fee.amount; + totalNativeFeeLinkValue += feesAndRewards[i].reward.amount; + } + + if (feesAndRewards[i].appliedDiscount != 0) { + emit DiscountApplied( + feesAndRewards[i].configDigest, + subscriber, + feesAndRewards[i].fee, + feesAndRewards[i].reward, + feesAndRewards[i].appliedDiscount + ); + } + } + + //keep track of change in case of any over payment + uint256 change; + + if (msg.value != 0) { + //there must be enough to cover the fee + if (totalNativeFee > msg.value) revert InvalidDeposit(); + + //wrap the amount required to pay the fee & approve as the subscriber paid in wrapped native + IWERC20(i_nativeAddress).deposit{value: totalNativeFee}(); + + unchecked { + //msg.value is always >= to fee.amount + change = msg.value - totalNativeFee; + } + } else { + if (totalNativeFee != 0) { + //subscriber has paid in wrapped native, so transfer the native to this contract + IERC20(i_nativeAddress).safeTransferFrom(subscriber, address(this), totalNativeFee); + } + } + + if (linkRewards.length != 0) { + i_rewardManager.onFeePaid(linkRewards, subscriber); + } + + if (nativeFeeLinkRewards.length != 0) { + //distribute subsidised fees paid in Native + if (totalNativeFeeLinkValue > IERC20(i_linkAddress).balanceOf(address(this))) { + // If not enough LINK on this contract to forward for rewards, tally the deficit to be paid by out-of-band LINK + for (uint256 i; i < nativeFeeLinkRewards.length; ++i) { + unchecked { + //we have previously tallied the fees, any overflows would have already reverted + s_linkDeficit[nativeFeeLinkRewards[i].poolId] += nativeFeeLinkRewards[i].amount; + } + } + + emit InsufficientLink(nativeFeeLinkRewards); + } else { + //distribute the fees + i_rewardManager.onFeePaid(nativeFeeLinkRewards, address(this)); + } + } + + // a refund may be needed if the payee has paid in excess of the fee + _tryReturnChange(subscriber, change); + } + + function _tryReturnChange(address subscriber, uint256 quantity) internal { + if (quantity != 0) { + payable(subscriber).transfer(quantity); + } + } + + /// @inheritdoc IDestinationFeeManager + function payLinkDeficit(bytes32 configDigest) external onlyOwner { + uint256 deficit = s_linkDeficit[configDigest]; + + if (deficit == 0) revert ZeroDeficit(); + + delete s_linkDeficit[configDigest]; + + IDestinationRewardManager.FeePayment[] memory deficitFeePayment = new IDestinationRewardManager.FeePayment[](1); + + deficitFeePayment[0] = IDestinationRewardManager.FeePayment(configDigest, uint192(deficit)); + + i_rewardManager.onFeePaid(deficitFeePayment, address(this)); + + emit LinkDeficitCleared(configDigest, deficit); + } + + /// @inheritdoc IDestinationFeeManager + function addVerifier(address verifierAddress) external onlyOwner { + if (verifierAddress == address(0)) revert InvalidAddress(); + //check doesn't already exist + if (s_verifierAddressList[verifierAddress] != address(0)) revert InvalidAddress(); + s_verifierAddressList[verifierAddress] = verifierAddress; + } + + /// @inheritdoc IDestinationFeeManager + function removeVerifier(address verifierAddress) external onlyOwner { + if (verifierAddress == address(0)) revert InvalidAddress(); + //check doesn't already exist + if (s_verifierAddressList[verifierAddress] == address(0)) revert InvalidAddress(); + delete s_verifierAddressList[verifierAddress]; + } + + /// @inheritdoc IDestinationFeeManager + function setRewardManager(address rewardManagerAddress) external onlyOwner { + if (rewardManagerAddress == address(0)) revert InvalidAddress(); + IERC20(i_linkAddress).approve(address(i_rewardManager), 0); + i_rewardManager = IDestinationRewardManager(rewardManagerAddress); + IERC20(i_linkAddress).approve(address(i_rewardManager), type(uint256).max); + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/DestinationRewardManager.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/DestinationRewardManager.sol new file mode 100644 index 0000000000..ae40a2385c --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/DestinationRewardManager.sol @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; +import {IDestinationRewardManager} from "./interfaces/IDestinationRewardManager.sol"; +import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC20.sol"; +import {TypeAndVersionInterface} from "../../interfaces/TypeAndVersionInterface.sol"; +import {Common} from "../libraries/Common.sol"; +import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; + +/** + * @title DestinationRewardManager + * @author Michael Fletcher + * @author Austin Born + * @notice This contract will be used to reward any configured recipients within a pool. Recipients will receive a share of their pool relative to their configured weight. + */ +contract DestinationRewardManager is IDestinationRewardManager, ConfirmedOwner, TypeAndVersionInterface { + using SafeERC20 for IERC20; + + // @dev The mapping of total fees collected for a particular pot: s_totalRewardRecipientFees[poolId] + mapping(bytes32 => uint256) public s_totalRewardRecipientFees; + + // @dev The mapping of fee balances for each pot last time the recipient claimed: s_totalRewardRecipientFeesLastClaimedAmounts[poolId][recipient] + mapping(bytes32 => mapping(address => uint256)) public s_totalRewardRecipientFeesLastClaimedAmounts; + + // @dev The mapping of RewardRecipient weights for a particular poolId: s_rewardRecipientWeights[poolId][rewardRecipient]. + mapping(bytes32 => mapping(address => uint256)) public s_rewardRecipientWeights; + + // @dev Keep track of the reward recipient weights that have been set to prevent duplicates + mapping(bytes32 => bool) public s_rewardRecipientWeightsSet; + + // @dev Store a list of pool ids that have been registered, to make off chain lookups easier + bytes32[] public s_registeredPoolIds; + + // @dev The address for the LINK contract + address public immutable i_linkAddress; + + // The total weight of all RewardRecipients. 1e18 = 100% of the pool fees + uint64 private constant PERCENTAGE_SCALAR = 1e18; + + // The fee manager address + mapping(address => address) public s_feeManagerAddressList; + + // @notice Thrown whenever the RewardRecipient weights are invalid + error InvalidWeights(); + + // @notice Thrown when any given address is invalid + error InvalidAddress(); + + // @notice Thrown when the pool id is invalid + error InvalidPoolId(); + + // @notice Thrown when the calling contract is not within the authorized contracts + error Unauthorized(); + + // @notice Thrown when getAvailableRewardPoolIds parameters are incorrectly set + error InvalidPoolLength(); + + // Events emitted upon state change + event RewardRecipientsUpdated(bytes32 indexed poolId, Common.AddressAndWeight[] newRewardRecipients); + event RewardsClaimed(bytes32 indexed poolId, address indexed recipient, uint192 quantity); + event FeeManagerUpdated(address newFeeManagerAddress); + event FeePaid(FeePayment[] payments, address payer); + + /** + * @notice Constructor + * @param linkAddress address of the wrapped LINK token + */ + constructor(address linkAddress) ConfirmedOwner(msg.sender) { + //ensure that the address ia not zero + if (linkAddress == address(0)) revert InvalidAddress(); + + i_linkAddress = linkAddress; + } + + // @inheritdoc TypeAndVersionInterface + function typeAndVersion() external pure override returns (string memory) { + return "RewardManager 1.0.0"; + } + + // @inheritdoc IERC165 + function supportsInterface(bytes4 interfaceId) external pure override returns (bool) { + return + interfaceId == this.claimRewards.selector || + interfaceId == this.setRewardRecipients.selector || + interfaceId == this.updateRewardRecipients.selector || + interfaceId == this.payRecipients.selector || + interfaceId == this.addFeeManager.selector || + interfaceId == this.removeFeeManager.selector || + interfaceId == this.getAvailableRewardPoolIds.selector || + interfaceId == this.onFeePaid.selector; + } + + modifier onlyOwnerOrFeeManager() { + if (msg.sender != owner() && msg.sender != s_feeManagerAddressList[msg.sender]) revert Unauthorized(); + _; + } + + modifier onlyOwnerOrRecipientInPool(bytes32 poolId) { + if (msg.sender != owner() && s_rewardRecipientWeights[poolId][msg.sender] == 0) revert Unauthorized(); + _; + } + + modifier onlyFeeManager() { + if (msg.sender != s_feeManagerAddressList[msg.sender]) revert Unauthorized(); + _; + } + + /// @inheritdoc IDestinationRewardManager + function onFeePaid(FeePayment[] calldata payments, address payer) external override onlyFeeManager { + uint256 totalFeeAmount; + for (uint256 i; i < payments.length; ++i) { + unchecked { + //the total amount for any ERC-20 asset cannot exceed 2^256 - 1 + //see https://github.com/OpenZeppelin/openzeppelin-contracts/blob/36bf1e46fa811f0f07d38eb9cfbc69a955f300ce/contracts/token/ERC20/ERC20.sol#L266 + //for example implementation. + s_totalRewardRecipientFees[payments[i].poolId] += payments[i].amount; + + //tally the total payable fees + totalFeeAmount += payments[i].amount; + } + } + + //transfer the fees to this contract + IERC20(i_linkAddress).safeTransferFrom(payer, address(this), totalFeeAmount); + + emit FeePaid(payments, payer); + } + + /// @inheritdoc IDestinationRewardManager + function claimRewards(bytes32[] memory poolIds) external override { + _claimRewards(msg.sender, poolIds); + } + + // wrapper impl for claimRewards + function _claimRewards(address recipient, bytes32[] memory poolIds) internal returns (uint256) { + //get the total amount claimable for this recipient + uint256 claimAmount; + + //loop and claim all the rewards in the poolId pot + for (uint256 i; i < poolIds.length; ++i) { + //get the poolId to be claimed + bytes32 poolId = poolIds[i]; + + //get the total fees for the pot + uint256 totalFeesInPot = s_totalRewardRecipientFees[poolId]; + + unchecked { + //avoid unnecessary storage reads if there's no fees in the pot + if (totalFeesInPot == 0) continue; + + //get the claimable amount for this recipient, this calculation will never exceed the amount in the pot + uint256 claimableAmount = totalFeesInPot - s_totalRewardRecipientFeesLastClaimedAmounts[poolId][recipient]; + + //calculate the recipients share of the fees, which is their weighted share of the difference between the last amount they claimed and the current amount in the pot. This can never be more than the total amount in existence + uint256 recipientShare = (claimableAmount * s_rewardRecipientWeights[poolId][recipient]) / PERCENTAGE_SCALAR; + + //if there's no fees to claim, continue as there's nothing to update + if (recipientShare == 0) continue; + + //keep track of the total amount claimable, this can never be more than the total amount in existence + claimAmount += recipientShare; + + //set the current total amount of fees in the pot as it's used to calculate future claims + s_totalRewardRecipientFeesLastClaimedAmounts[poolId][recipient] = totalFeesInPot; + + //emit event if the recipient has rewards to claim + emit RewardsClaimed(poolIds[i], recipient, uint192(recipientShare)); + } + } + + //check if there's any rewards to claim in the given poolId + if (claimAmount != 0) { + //transfer the reward to the recipient + IERC20(i_linkAddress).safeTransfer(recipient, claimAmount); + } + + return claimAmount; + } + + /// @inheritdoc IDestinationRewardManager + function setRewardRecipients( + bytes32 poolId, + Common.AddressAndWeight[] calldata rewardRecipientAndWeights + ) external override onlyOwnerOrFeeManager { + //revert if there are no recipients to set + if (rewardRecipientAndWeights.length == 0) revert InvalidAddress(); + + //check that the weights have not been previously set + if (s_rewardRecipientWeightsSet[poolId]) revert InvalidPoolId(); + + //keep track of the registered poolIds to make off chain lookups easier + s_registeredPoolIds.push(poolId); + + //keep track of which pools have had their reward recipients set + s_rewardRecipientWeightsSet[poolId] = true; + + //set the reward recipients, this will only be called once and contain the full set of RewardRecipients with a total weight of 100% + _setRewardRecipientWeights(poolId, rewardRecipientAndWeights, PERCENTAGE_SCALAR); + + emit RewardRecipientsUpdated(poolId, rewardRecipientAndWeights); + } + + function _setRewardRecipientWeights( + bytes32 poolId, + Common.AddressAndWeight[] calldata rewardRecipientAndWeights, + uint256 expectedWeight + ) internal { + //we can't update the weights if it contains duplicates + if (Common._hasDuplicateAddresses(rewardRecipientAndWeights)) revert InvalidAddress(); + + //loop all the reward recipients and validate the weight and address + uint256 totalWeight; + for (uint256 i; i < rewardRecipientAndWeights.length; ++i) { + //get the weight + uint256 recipientWeight = rewardRecipientAndWeights[i].weight; + //get the address + address recipientAddress = rewardRecipientAndWeights[i].addr; + + //ensure the reward recipient address is not zero + if (recipientAddress == address(0)) revert InvalidAddress(); + + //save/overwrite the weight for the reward recipient + s_rewardRecipientWeights[poolId][recipientAddress] = recipientWeight; + + unchecked { + //keep track of the cumulative weight, this cannot overflow as the total weight is restricted at 1e18 + totalWeight += recipientWeight; + } + } + + //if total weight is not met, the fees will either be under or over distributed + if (totalWeight != expectedWeight) revert InvalidWeights(); + } + + /// @inheritdoc IDestinationRewardManager + function updateRewardRecipients( + bytes32 poolId, + Common.AddressAndWeight[] calldata newRewardRecipients + ) external override onlyOwner { + //create an array of poolIds to pass to _claimRewards if required + bytes32[] memory poolIds = new bytes32[](1); + poolIds[0] = poolId; + + //loop all the reward recipients and claim their rewards before updating their weights + uint256 existingTotalWeight; + for (uint256 i; i < newRewardRecipients.length; ++i) { + //get the address + address recipientAddress = newRewardRecipients[i].addr; + //get the existing weight + uint256 existingWeight = s_rewardRecipientWeights[poolId][recipientAddress]; + + //if a recipient is updated, the rewards must be claimed first as they can't claim previous fees at the new weight + _claimRewards(newRewardRecipients[i].addr, poolIds); + + unchecked { + //keep tally of the weights so that the expected collective weight is known + existingTotalWeight += existingWeight; + } + } + + //update the reward recipients, if the new collective weight isn't equal to the previous collective weight, the fees will either be under or over distributed + _setRewardRecipientWeights(poolId, newRewardRecipients, existingTotalWeight); + + //emit event + emit RewardRecipientsUpdated(poolId, newRewardRecipients); + } + + /// @inheritdoc IDestinationRewardManager + function payRecipients(bytes32 poolId, address[] calldata recipients) external onlyOwnerOrRecipientInPool(poolId) { + //convert poolIds to an array to match the interface of _claimRewards + bytes32[] memory poolIdsArray = new bytes32[](1); + poolIdsArray[0] = poolId; + + //loop each recipient and claim the rewards for each of the pools and assets + for (uint256 i; i < recipients.length; ++i) { + _claimRewards(recipients[i], poolIdsArray); + } + } + + /// @inheritdoc IDestinationRewardManager + function addFeeManager(address newFeeManagerAddress) external onlyOwner { + if (newFeeManagerAddress == address(0)) revert InvalidAddress(); + if (s_feeManagerAddressList[newFeeManagerAddress] != address(0)) revert InvalidAddress(); + + s_feeManagerAddressList[newFeeManagerAddress] = newFeeManagerAddress; + + emit FeeManagerUpdated(newFeeManagerAddress); + } + + /// @inheritdoc IDestinationRewardManager + function removeFeeManager(address feeManagerAddress) external onlyOwner { + if (s_feeManagerAddressList[feeManagerAddress] == address(0)) revert InvalidAddress(); + delete s_feeManagerAddressList[feeManagerAddress]; + } + + /// @inheritdoc IDestinationRewardManager + function getAvailableRewardPoolIds( + address recipient, + uint256 startIndex, + uint256 endIndex + ) external view returns (bytes32[] memory) { + //get the length of the pool ids which we will loop through and potentially return + uint256 registeredPoolIdsLength = s_registeredPoolIds.length; + + uint256 lastIndex = endIndex > registeredPoolIdsLength ? registeredPoolIdsLength : endIndex; + + if (startIndex > lastIndex) revert InvalidPoolLength(); + + //create a new array with the maximum amount of potential pool ids + bytes32[] memory claimablePoolIds = new bytes32[](lastIndex - startIndex); + //we want the pools which a recipient has funds for to be sequential, so we need to keep track of the index + uint256 poolIdArrayIndex; + + //loop all the pool ids, and check if the recipient has a registered weight and a claimable amount + for (uint256 i = startIndex; i < lastIndex; ++i) { + //get the poolId + bytes32 poolId = s_registeredPoolIds[i]; + + //if the recipient has a weight, they are a recipient of this poolId + if (s_rewardRecipientWeights[poolId][recipient] != 0) { + //get the total in this pool + uint256 totalPoolAmount = s_totalRewardRecipientFees[poolId]; + //if the recipient has any LINK, then add the poolId to the array + unchecked { + //s_totalRewardRecipientFeesLastClaimedAmounts can never exceed total pool amount, and the number of pools can't exceed the max array length + if (totalPoolAmount - s_totalRewardRecipientFeesLastClaimedAmounts[poolId][recipient] != 0) { + claimablePoolIds[poolIdArrayIndex++] = poolId; + } + } + } + } + + return claimablePoolIds; + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/DestinationVerifier.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/DestinationVerifier.sol new file mode 100644 index 0000000000..52b2bd7c9a --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/DestinationVerifier.sol @@ -0,0 +1,434 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; +import {IDestinationVerifier} from "./interfaces/IDestinationVerifier.sol"; +import {TypeAndVersionInterface} from "../../interfaces/TypeAndVersionInterface.sol"; +import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; +import {Common} from "../libraries/Common.sol"; +import {IAccessController} from "../../shared/interfaces/IAccessController.sol"; +import {IDestinationVerifierProxy} from "./interfaces/IDestinationVerifierProxy.sol"; +import {IDestinationFeeManager} from "./interfaces/IDestinationFeeManager.sol"; + +// OCR2 standard +uint256 constant MAX_NUM_ORACLES = 31; + +/** + * @title DestinationVerifier + * @author Michael Fletcher + * @notice This contract will be used to verify reports based on the oracle signatures. This is not the source verifier which required individual fee configurations, instead, this checks that a report has been signed by one of the configured oracles. + */ +contract DestinationVerifier is IDestinationVerifier, ConfirmedOwner, TypeAndVersionInterface, IERC165 { + /// @notice The list of DON configurations by hash(address|donConfigId) - set to true if the signer is part of the config + mapping(bytes32 => bool) private s_signerByAddressAndDonConfigId; + + /// array of DON configs + DonConfig[] private s_donConfigs; + + /// @notice The address of the verifierProxy + address public s_feeManager; + + /// @notice The address of the access controller + address public s_accessController; + + /// @notice The address of the verifierProxy + IDestinationVerifierProxy public immutable i_verifierProxy; + + /// @notice This error is thrown whenever trying to set a config + /// with a fault tolerance of 0 + error FaultToleranceMustBePositive(); + + /// @notice This error is thrown whenever a report is signed + /// with more than the max number of signers + /// @param numSigners The number of signers who have signed the report + /// @param maxSigners The maximum number of signers that can sign a report + error ExcessSigners(uint256 numSigners, uint256 maxSigners); + + /// @notice This error is thrown whenever a report is signed or expected to be signed with less than the minimum number of signers + /// @param numSigners The number of signers who have signed the report + /// @param minSigners The minimum number of signers that need to sign a report + error InsufficientSigners(uint256 numSigners, uint256 minSigners); + + /// @notice This error is thrown whenever a report is submitted with no signatures + error NoSigners(); + + /// @notice This error is thrown whenever a DonConfig already exists + /// @param donConfigId The ID of the DonConfig that already exists + error DonConfigAlreadyExists(bytes24 donConfigId); + + /// @notice This error is thrown whenever the R and S signer components + /// have different lengths + /// @param rsLength The number of r signature components + /// @param ssLength The number of s signature components + error MismatchedSignatures(uint256 rsLength, uint256 ssLength); + + /// @notice This error is thrown whenever setting a config with duplicate signatures + error NonUniqueSignatures(); + + /* @notice This error is thrown whenever a report fails to verify. This error be thrown for multiple reasons and it's purposely like + * this to prevent information being leaked about the verification process which could be used to enable free verifications maliciously + */ + error BadVerification(); + + /// @notice This error is thrown whenever a zero address is passed + error ZeroAddress(); + + /// @notice This error is thrown when the fee manager at an address does + /// not conform to the fee manager interface + error FeeManagerInvalid(); + + /// @notice This error is thrown whenever an address tries + /// to execute a verification that it is not authorized to do so + error AccessForbidden(); + + /// @notice This error is thrown whenever a config does not exist + error DonConfigDoesNotExist(); + + /// @notice this error is thrown when the verifierProxy is incorrect when initialising + error VerifierProxyInvalid(); + + /// @notice This error is thrown when the activation time is either in the future or less than the current configs + error BadActivationTime(); + + /// @notice This event is emitted when a new report is verified. + /// It is used to keep a historical record of verified reports. + event ReportVerified(bytes32 indexed feedId, address requester); + + /// @notice This event is emitted whenever a configuration is activated or deactivated + event ConfigActivated(bytes24 donConfigId, bool isActive); + + /// @notice This event is emitted whenever a configuration is removed + event ConfigRemoved(bytes24 donConfigId); + + /// @notice event is emitted whenever a new DON Config is set + event ConfigSet( + bytes24 indexed donConfigId, + address[] signers, + uint8 f, + Common.AddressAndWeight[] recipientAddressesAndWeights + ); + + /// @notice This event is emitted when a new fee manager is set + /// @param oldFeeManager The old fee manager address + /// @param newFeeManager The new fee manager address + event FeeManagerSet(address oldFeeManager, address newFeeManager); + + /// @notice This event is emitted when a new access controller is set + /// @param oldAccessController The old access controller address + /// @param newAccessController The new access controller address + event AccessControllerSet(address oldAccessController, address newAccessController); + + struct DonConfig { + // The ID of the DonConfig + bytes24 donConfigId; + // Fault tolerance of the DON + uint8 f; + // Whether the config is active + bool isActive; + // The time the config was set + uint32 activationTime; + } + + constructor(address verifierProxy) ConfirmedOwner(msg.sender) { + if (verifierProxy == address(0)) { + revert ZeroAddress(); + } + + i_verifierProxy = IDestinationVerifierProxy(verifierProxy); + } + + /// @inheritdoc IDestinationVerifier + function verify( + bytes calldata signedReport, + bytes calldata parameterPayload, + address sender + ) external payable override checkValidProxy checkAccess(sender) returns (bytes memory) { + (bytes memory verifierResponse, bytes32 donConfigId) = _verify(signedReport, sender); + + address fm = s_feeManager; + if (fm != address(0)) { + //process the fee and catch the error + try IDestinationFeeManager(fm).processFee{value: msg.value}(donConfigId, signedReport, parameterPayload, sender) { + //do nothing + } catch { + // we purposefully obfuscate the error here to prevent information leaking leading to free verifications + revert BadVerification(); + } + } + + return verifierResponse; + } + + /// @inheritdoc IDestinationVerifier + function verifyBulk( + bytes[] calldata signedReports, + bytes calldata parameterPayload, + address sender + ) external payable override checkValidProxy checkAccess(sender) returns (bytes[] memory) { + bytes[] memory verifierResponses = new bytes[](signedReports.length); + bytes32[] memory donConfigs = new bytes32[](signedReports.length); + + for (uint256 i; i < signedReports.length; ++i) { + (bytes memory report, bytes32 config) = _verify(signedReports[i], sender); + verifierResponses[i] = report; + donConfigs[i] = config; + } + + address fm = s_feeManager; + if (fm != address(0)) { + //process the fee and catch the error + try + IDestinationFeeManager(fm).processFeeBulk{value: msg.value}(donConfigs, signedReports, parameterPayload, sender) + { + //do nothing + } catch { + // we purposefully obfuscate the error here to prevent information leaking leading to free verifications + revert BadVerification(); + } + } + + return verifierResponses; + } + + function _verify(bytes calldata signedReport, address sender) internal returns (bytes memory, bytes32) { + ( + bytes32[3] memory reportContext, + bytes memory reportData, + bytes32[] memory rs, + bytes32[] memory ss, + bytes32 rawVs + ) = abi.decode(signedReport, (bytes32[3], bytes, bytes32[], bytes32[], bytes32)); + + // Signature lengths must match + if (rs.length != ss.length) revert MismatchedSignatures(rs.length, ss.length); + + //Must always be at least 1 signer + if (rs.length == 0) revert NoSigners(); + + // The payload is hashed and signed by the oracles - we need to recover the addresses + bytes32 signedPayload = keccak256(abi.encodePacked(keccak256(reportData), reportContext)); + address[] memory signers = new address[](rs.length); + for (uint256 i; i < rs.length; ++i) { + signers[i] = ecrecover(signedPayload, uint8(rawVs[i]) + 27, rs[i], ss[i]); + } + + // Duplicate signatures are not allowed + if (Common._hasDuplicateAddresses(signers)) { + revert BadVerification(); + } + + //We need to know the timestamp the report was generated to lookup the active activeDonConfig + uint256 reportTimestamp = _decodeReportTimestamp(reportData); + + // Find the latest config for this report + DonConfig memory activeDonConfig = _findActiveConfig(reportTimestamp); + + // Check a config has been set + if (activeDonConfig.donConfigId == bytes24(0)) { + revert BadVerification(); + } + + //check the config is active + if (!activeDonConfig.isActive) { + revert BadVerification(); + } + + //check we have enough signatures + if (signers.length <= activeDonConfig.f) { + revert BadVerification(); + } + + //check each signer is registered against the active DON + bytes32 signerDonConfigKey; + for (uint256 i; i < signers.length; ++i) { + signerDonConfigKey = keccak256(abi.encodePacked(signers[i], activeDonConfig.donConfigId)); + if (!s_signerByAddressAndDonConfigId[signerDonConfigKey]) { + revert BadVerification(); + } + } + + emit ReportVerified(bytes32(reportData), sender); + + return (reportData, activeDonConfig.donConfigId); + } + + /// @inheritdoc IDestinationVerifier + function setConfigWithActivationTime( + address[] memory signers, + uint8 f, + Common.AddressAndWeight[] memory recipientAddressesAndWeights, + uint32 activationTime + ) external override checkConfigValid(signers.length, f) onlyOwner { + _setConfig(signers, f, recipientAddressesAndWeights, activationTime); + } + + /// @inheritdoc IDestinationVerifier + function setConfig( + address[] memory signers, + uint8 f, + Common.AddressAndWeight[] memory recipientAddressesAndWeights + ) external override checkConfigValid(signers.length, f) onlyOwner { + _setConfig(signers, f, recipientAddressesAndWeights, uint32(block.timestamp)); + } + + function _setConfig( + address[] memory signers, + uint8 f, + Common.AddressAndWeight[] memory recipientAddressesAndWeights, + uint32 activationTime + ) internal checkConfigValid(signers.length, f) onlyOwner { + // Duplicate addresses would break protocol rules + if (Common._hasDuplicateAddresses(signers)) { + revert NonUniqueSignatures(); + } + + //activation time cannot be in the future + if (activationTime > block.timestamp) { + revert BadActivationTime(); + } + + // Sort signers to ensure donConfigId is deterministic + Common._quickSort(signers, 0, int256(signers.length - 1)); + + //DonConfig is made up of hash(signers|f) + bytes24 donConfigId = bytes24(keccak256(abi.encodePacked(signers, f))); + + // Register the signers for this DON + for (uint256 i; i < signers.length; ++i) { + if (signers[i] == address(0)) revert ZeroAddress(); + /** This index is registered so we can efficiently lookup whether a NOP is part of a config without having to + loop through the entire config each verification. It's effectively a DonConfig <-> Signer + composite key which keys track of all historic configs for a signer */ + s_signerByAddressAndDonConfigId[keccak256(abi.encodePacked(signers[i], donConfigId))] = true; + } + + // Check the activation time is greater than the latest config + uint256 donConfigLength = s_donConfigs.length; + if (donConfigLength > 0 && s_donConfigs[donConfigLength - 1].activationTime > activationTime) { + revert BadActivationTime(); + } + + // Check the config we're setting isn't already set as the current active config as this will increase search costs unnecessarily when verifying historic reports + if (donConfigLength > 0 && s_donConfigs[donConfigLength - 1].donConfigId == donConfigId) { + revert DonConfigAlreadyExists(donConfigId); + } + + // We may want to register these later or skip this step in the unlikely scenario they've previously been registered in the RewardsManager + if (recipientAddressesAndWeights.length != 0) { + IDestinationFeeManager(s_feeManager).setFeeRecipients(donConfigId, recipientAddressesAndWeights); + } + + // push the DonConfig + s_donConfigs.push(DonConfig(donConfigId, f, true, activationTime)); + + emit ConfigSet(donConfigId, signers, f, recipientAddressesAndWeights); + } + + /// @inheritdoc IDestinationVerifier + function setFeeManager(address feeManager) external override onlyOwner { + if ( + !IERC165(feeManager).supportsInterface(IDestinationFeeManager.processFee.selector) || + !IERC165(feeManager).supportsInterface(IDestinationFeeManager.processFeeBulk.selector) || + !IERC165(feeManager).supportsInterface(IDestinationFeeManager.setFeeRecipients.selector) + ) revert FeeManagerInvalid(); + + address oldFeeManager = s_feeManager; + s_feeManager = feeManager; + + emit FeeManagerSet(oldFeeManager, feeManager); + } + + /// @inheritdoc IDestinationVerifier + function setAccessController(address accessController) external override onlyOwner { + address oldAccessController = s_accessController; + s_accessController = accessController; + emit AccessControllerSet(oldAccessController, accessController); + } + + /// @inheritdoc IDestinationVerifier + function setConfigActive(uint256 donConfigIndex, bool isActive) external onlyOwner { + // Config must exist + if (donConfigIndex >= s_donConfigs.length) { + revert DonConfigDoesNotExist(); + } + + // Update the config + DonConfig storage config = s_donConfigs[donConfigIndex]; + config.isActive = isActive; + + emit ConfigActivated(config.donConfigId, isActive); + } + + /// @inheritdoc IDestinationVerifier + function removeLatestConfig() external onlyOwner { + if (s_donConfigs.length == 0) { + revert DonConfigDoesNotExist(); + } + + DonConfig memory config = s_donConfigs[s_donConfigs.length - 1]; + + s_donConfigs.pop(); + + emit ConfigRemoved(config.donConfigId); + } + + function _decodeReportTimestamp(bytes memory reportPayload) internal pure returns (uint256) { + (, , uint256 timestamp) = abi.decode(reportPayload, (bytes32, uint32, uint32)); + + return timestamp; + } + + function _findActiveConfig(uint256 timestamp) internal view returns (DonConfig memory) { + DonConfig memory activeDonConfig; + + // 99% of the time the signer config will be the last index, however for historic reports generated by a previous configuration we'll need to cycle back + uint256 i = s_donConfigs.length; + while (i > 0) { + --i; + if (s_donConfigs[i].activationTime <= timestamp) { + activeDonConfig = s_donConfigs[i]; + break; + } + } + return activeDonConfig; + } + + modifier checkConfigValid(uint256 numSigners, uint256 f) { + if (f == 0) revert FaultToleranceMustBePositive(); + if (numSigners > MAX_NUM_ORACLES) revert ExcessSigners(numSigners, MAX_NUM_ORACLES); + if (numSigners <= 3 * f) revert InsufficientSigners(numSigners, 3 * f + 1); + _; + } + + modifier checkValidProxy() { + if (address(i_verifierProxy) != msg.sender) { + revert AccessForbidden(); + } + _; + } + + modifier checkAccess(address sender) { + address ac = s_accessController; + if (address(ac) != address(0) && !IAccessController(ac).hasAccess(sender, msg.data)) revert AccessForbidden(); + _; + } + + /// @inheritdoc IERC165 + function supportsInterface(bytes4 interfaceId) public pure override returns (bool) { + return + interfaceId == this.verify.selector || + interfaceId == this.verifyBulk.selector || + interfaceId == this.s_accessController.selector || + interfaceId == this.s_feeManager.selector || + interfaceId == this.setConfig.selector || + interfaceId == this.setConfigWithActivationTime.selector || + interfaceId == this.setFeeManager.selector || + interfaceId == this.setAccessController.selector || + interfaceId == this.setConfigActive.selector; + } + + /// @inheritdoc TypeAndVersionInterface + function typeAndVersion() external pure override returns (string memory) { + return "DestinationVerifier 1.0.0"; + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/DestinationVerifierProxy.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/DestinationVerifierProxy.sol new file mode 100644 index 0000000000..1a5c62b429 --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/DestinationVerifierProxy.sol @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; +import {TypeAndVersionInterface} from "../../interfaces/TypeAndVersionInterface.sol"; +import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; +import {IDestinationVerifierProxy} from "./interfaces/IDestinationVerifierProxy.sol"; +import {IDestinationVerifier} from "./interfaces/IDestinationVerifier.sol"; + +/** + * @title DestinationVerifierProxy + * @author Michael Fletcher + * @notice This contract will be used to route all requests through to the assigned verifier contract. This contract does not support individual feed configurations and is aimed at being a simple proxy for the verifier contract on any destination chain. + */ +contract DestinationVerifierProxy is IDestinationVerifierProxy, ConfirmedOwner, TypeAndVersionInterface, IERC165 { + /// @notice The active verifier for this proxy + IDestinationVerifier private s_verifier; + + /// @notice This error is thrown whenever a zero address is passed + error ZeroAddress(); + + /// @notice This error is thrown when trying to set a verifier address that does not implement the expected interface + error VerifierInvalid(address verifierAddress); + + constructor() ConfirmedOwner(msg.sender) {} + + /// @inheritdoc TypeAndVersionInterface + function typeAndVersion() external pure override returns (string memory) { + return "DestinationVerifierProxy 1.0.0"; + } + + /// @inheritdoc IDestinationVerifierProxy + function verify(bytes calldata payload, bytes calldata parameterPayload) external payable returns (bytes memory) { + return s_verifier.verify{value: msg.value}(payload, parameterPayload, msg.sender); + } + + /// @inheritdoc IDestinationVerifierProxy + function verifyBulk( + bytes[] calldata payloads, + bytes calldata parameterPayload + ) external payable returns (bytes[] memory verifiedReports) { + return s_verifier.verifyBulk{value: msg.value}(payloads, parameterPayload, msg.sender); + } + + /// @inheritdoc IDestinationVerifierProxy + function setVerifier(address verifierAddress) external onlyOwner { + //check it supports the functions we need + if ( + !IERC165(verifierAddress).supportsInterface(IDestinationVerifier.s_accessController.selector) || + !IERC165(verifierAddress).supportsInterface(IDestinationVerifier.s_feeManager.selector) || + !IERC165(verifierAddress).supportsInterface(IDestinationVerifier.verify.selector) || + !IERC165(verifierAddress).supportsInterface(IDestinationVerifier.verifyBulk.selector) + ) revert VerifierInvalid(verifierAddress); + + s_verifier = IDestinationVerifier(verifierAddress); + } + + /// @inheritdoc IDestinationVerifierProxy + // solhint-disable-next-line func-name-mixedcase + function s_feeManager() external view override returns (address) { + return s_verifier.s_feeManager(); + } + + /// @inheritdoc IDestinationVerifierProxy + // solhint-disable-next-line func-name-mixedcase + function s_accessController() external view override returns (address) { + return s_verifier.s_accessController(); + } + + /// @inheritdoc IERC165 + function supportsInterface(bytes4 interfaceId) external pure override returns (bool) { + return + interfaceId == this.setVerifier.selector || + interfaceId == this.verify.selector || + interfaceId == this.verifyBulk.selector || + interfaceId == this.s_feeManager.selector || + interfaceId == this.s_accessController.selector; + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/interfaces/IDestinationFeeManager.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/interfaces/IDestinationFeeManager.sol new file mode 100644 index 0000000000..f92e7cd146 --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/interfaces/IDestinationFeeManager.sol @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; +import {Common} from "../../libraries/Common.sol"; + +interface IDestinationFeeManager is IERC165 { + /** + * @notice Calculate the applied fee and the reward from a report. If the sender is a subscriber, they will receive a discount. + * @param subscriber address trying to verify + * @param report report to calculate the fee for + * @param quoteAddress address of the quote payment token + * @return (fee, reward, totalDiscount) fee and the reward data with the discount applied + */ + function getFeeAndReward( + address subscriber, + bytes memory report, + address quoteAddress + ) external returns (Common.Asset memory, Common.Asset memory, uint256); + + /** + * @notice Sets the native surcharge + * @param surcharge surcharge to be paid if paying in native + */ + function setNativeSurcharge(uint64 surcharge) external; + + /** + * @notice Adds a subscriber to the fee manager + * @param subscriber address of the subscriber + * @param feedId feed id to apply the discount to + * @param token token to apply the discount to + * @param discount discount to be applied to the fee + */ + function updateSubscriberDiscount(address subscriber, bytes32 feedId, address token, uint64 discount) external; + + /** + * @notice Withdraws any native or LINK rewards to the owner address + * @param assetAddress address of the asset to withdraw + * @param recipientAddress address to withdraw to + * @param quantity quantity to withdraw + */ + function withdraw(address assetAddress, address recipientAddress, uint192 quantity) external; + + /** + * @notice Returns the link balance of the fee manager + * @return link balance of the fee manager + */ + function linkAvailableForPayment() external returns (uint256); + + /** + * @notice Admin function to pay the LINK deficit for a given config digest + * @param configDigest the config digest to pay the deficit for + */ + function payLinkDeficit(bytes32 configDigest) external; + + /** + * @notice Adds the verifier to the list of verifiers able to use the feeManager + * @param verifier address of the verifier + */ + function addVerifier(address verifier) external; + + /** + * @notice Removes the verifier from the list of verifiers able to use the feeManager + * @param verifier address of the verifier + */ + function removeVerifier(address verifier) external; + + /** + * @notice Sets the reward manager to the address + * @param rewardManager address of the reward manager + */ + function setRewardManager(address rewardManager) external; + + /** + * @notice Handles fees for a report from the subscriber and manages rewards + * @param poolId pool id of the pool to pay into + * @param payload report to process the fee for + * @param parameterPayload fee payload + * @param subscriber address of the fee will be applied + */ + function processFee( + bytes32 poolId, + bytes calldata payload, + bytes calldata parameterPayload, + address subscriber + ) external payable; + + /** + * @notice Processes the fees for each report in the payload, billing the subscriber and paying the reward manager + * @param poolIds pool ids of the pool to pay into + * @param payloads reports to process + * @param parameterPayload fee payload + * @param subscriber address of the user to process fee for + */ + function processFeeBulk( + bytes32[] memory poolIds, + bytes[] calldata payloads, + bytes calldata parameterPayload, + address subscriber + ) external payable; + + /** + * @notice Sets the fee recipients according to the fee manager + * @param configDigest digest of the configuration + * @param rewardRecipientAndWeights the address and weights of all the recipients to receive rewards + */ + function setFeeRecipients( + bytes32 configDigest, + Common.AddressAndWeight[] calldata rewardRecipientAndWeights + ) external; + + /** + * @notice The structure to hold a fee and reward to verify a report + * @param digest the digest linked to the fee and reward + * @param fee the fee paid to verify the report + * @param reward the reward paid upon verification + & @param appliedDiscount the discount applied to the reward + */ + struct FeeAndReward { + bytes32 configDigest; + Common.Asset fee; + Common.Asset reward; + uint256 appliedDiscount; + } + + /** + * @notice The structure to hold quote metadata + * @param quoteAddress the address of the quote + */ + struct Quote { + address quoteAddress; + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/interfaces/IDestinationRewardManager.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/interfaces/IDestinationRewardManager.sol new file mode 100644 index 0000000000..95f07937ae --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/interfaces/IDestinationRewardManager.sol @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; +import {Common} from "../../libraries/Common.sol"; + +interface IDestinationRewardManager is IERC165 { + /** + * @notice Record the fee received for a particular pool + * @param payments array of structs containing pool id and amount + * @param payee the user the funds should be retrieved from + */ + function onFeePaid(FeePayment[] calldata payments, address payee) external; + + /** + * @notice Claims the rewards in a specific pool + * @param poolIds array of poolIds to claim rewards for + */ + function claimRewards(bytes32[] calldata poolIds) external; + + /** + * @notice Set the RewardRecipients and weights for a specific pool. This should only be called once per pool Id. Else updateRewardRecipients should be used. + * @param poolId poolId to set RewardRecipients and weights for + * @param rewardRecipientAndWeights array of each RewardRecipient and associated weight + */ + function setRewardRecipients(bytes32 poolId, Common.AddressAndWeight[] calldata rewardRecipientAndWeights) external; + + /** + * @notice Updates a subset the reward recipients for a specific poolId. The collective weight of the recipients should add up to the recipients existing weights. Any recipients with a weight of 0 will be removed. + * @param poolId the poolId to update + * @param newRewardRecipients array of new reward recipients + */ + function updateRewardRecipients(bytes32 poolId, Common.AddressAndWeight[] calldata newRewardRecipients) external; + + /** + * @notice Pays all the recipients for each of the pool ids + * @param poolId the pool id to pay recipients for + * @param recipients array of recipients to pay within the pool + */ + function payRecipients(bytes32 poolId, address[] calldata recipients) external; + + /** + * @notice Add the fee manager to the list of feeManagers able to call the reward manager + * @param newFeeManager address of the new verifier proxy + */ + function addFeeManager(address newFeeManager) external; + + /** + * @notice Removes the fee manager. This needs to be done post construction to prevent a circular dependency. + * @param feeManager address of the verifier proxy to remove + */ + function removeFeeManager(address feeManager) external; + + /** + * @notice Gets a list of pool ids which have reward for a specific recipient. + * @param recipient address of the recipient to get pool ids for + * @param startIndex the index to start from + * @param endIndex the index to stop at + */ + function getAvailableRewardPoolIds( + address recipient, + uint256 startIndex, + uint256 endIndex + ) external view returns (bytes32[] memory); + + /** + * @notice The structure to hold a fee payment notice + * @param poolId the poolId receiving the payment + * @param amount the amount being paid + */ + struct FeePayment { + bytes32 poolId; + uint192 amount; + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/interfaces/IDestinationVerifier.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/interfaces/IDestinationVerifier.sol new file mode 100644 index 0000000000..69516f6e92 --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/interfaces/IDestinationVerifier.sol @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {Common} from "../../libraries/Common.sol"; + +interface IDestinationVerifier { + /** + * @notice Verifies that the data encoded has been signed correctly using the signatures included within the payload. + * @param signedReport The encoded data to be verified. + * @param parameterPayload The encoded parameters to be used in the verification and billing process. + * @param sender The address that requested to verify the contract.Used for logging and applying the fee. + * @dev Verification is typically only done through the proxy contract so we can't just use msg.sender. + * @return verifierResponse The encoded verified response. + */ + function verify( + bytes calldata signedReport, + bytes calldata parameterPayload, + address sender + ) external payable returns (bytes memory verifierResponse); + + /** + * @notice Bulk verifies that the data encoded has been signed correctly using the signatures included within the payload. + * @param signedReports The encoded data to be verified. + * @param parameterPayload The encoded parameters to be used in the verification and billing process. + * @param sender The address that requested to verify the contract. Used for logging and applying the fee. + * @dev Verification is typically only done through the proxy contract so we can't just use msg.sender. + * @return verifiedReports The encoded verified responses. + */ + function verifyBulk( + bytes[] calldata signedReports, + bytes calldata parameterPayload, + address sender + ) external payable returns (bytes[] memory verifiedReports); + + /** + * @notice sets off-chain reporting protocol configuration incl. participating oracles + * @param signers addresses with which oracles sign the reports + * @param f number of faulty oracles the system can tolerate + * @param recipientAddressesAndWeights the addresses and weights of all the recipients to receive rewards + */ + function setConfig( + address[] memory signers, + uint8 f, + Common.AddressAndWeight[] memory recipientAddressesAndWeights + ) external; + + /** + * @notice sets off-chain reporting protocol configuration incl. participating oracles + * @param signers addresses with which oracles sign the reports + * @param f number of faulty oracles the system can tolerate + * @param recipientAddressesAndWeights the addresses and weights of all the recipients to receive rewards + * @param activationTime the time at which the config was activated + */ + function setConfigWithActivationTime( + address[] memory signers, + uint8 f, + Common.AddressAndWeight[] memory recipientAddressesAndWeights, + uint32 activationTime + ) external; + + /** + * @notice Sets the fee manager address + * @param feeManager The address of the fee manager + */ + function setFeeManager(address feeManager) external; + + /** + * @notice Sets the access controller address + * @param accessController The address of the access controller + */ + function setAccessController(address accessController) external; + + /** + * @notice Updates the config active status + * @param donConfigId The ID of the config to update + * @param isActive The new config active status + */ + function setConfigActive(uint256 donConfigId, bool isActive) external; + + /** + * @notice Removes the latest config + */ + function removeLatestConfig() external; + + /* + * @notice Returns the reward manager + * @return IDestinationRewardManager + */ + // solhint-disable-next-line func-name-mixedcase + function s_feeManager() external view returns (address); + + /** + * @notice Returns the access controller + * @return IDestinationFeeManager + */ + // solhint-disable-next-line func-name-mixedcase + function s_accessController() external view returns (address); +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/interfaces/IDestinationVerifierProxy.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/interfaces/IDestinationVerifierProxy.sol new file mode 100644 index 0000000000..a88349b301 --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/interfaces/IDestinationVerifierProxy.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +interface IDestinationVerifierProxy { + /** + * @notice Verifies that the data encoded has been signed + * correctly by routing to the verifier, and bills the user if applicable. + * @param payload The encoded data to be verified, including the signed + * report. + * @param parameterPayload fee metadata for billing + * @return verifierResponse The encoded report from the verifier. + */ + function verify( + bytes calldata payload, + bytes calldata parameterPayload + ) external payable returns (bytes memory verifierResponse); + + /** + * @notice Bulk verifies that the data encoded has been signed + * correctly by routing to the correct verifier, and bills the user if applicable. + * @param payloads The encoded payloads to be verified, including the signed + * report. + * @param parameterPayload fee metadata for billing + * @return verifiedReports The encoded reports from the verifier. + */ + function verifyBulk( + bytes[] calldata payloads, + bytes calldata parameterPayload + ) external payable returns (bytes[] memory verifiedReports); + + /** + * @notice Sets the active verifier for this proxy + * @param verifierAddress The address of the verifier contract + */ + function setVerifier(address verifierAddress) external; + + /** + * @notice Used to honor the source verifierProxy feeManager interface + * @return IVerifierFeeManager + */ + // solhint-disable-next-line func-name-mixedcase + function s_feeManager() external view returns (address); + + /** + * @notice Used to honor the source verifierProxy feeManager interface + * @return AccessControllerInterface + */ + // solhint-disable-next-line func-name-mixedcase + function s_accessController() external view returns (address); +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/test/fee-manager/BaseDestinationFeeManager.t.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/test/fee-manager/BaseDestinationFeeManager.t.sol new file mode 100644 index 0000000000..8b70e5b2b3 --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/test/fee-manager/BaseDestinationFeeManager.t.sol @@ -0,0 +1,394 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {Test} from "forge-std/Test.sol"; +import {DestinationFeeManager} from "../../DestinationFeeManager.sol"; +import {DestinationRewardManager} from "../../DestinationRewardManager.sol"; +import {Common} from "../../../libraries/Common.sol"; +import {ERC20Mock} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/mocks/ERC20Mock.sol"; +import {WERC20Mock} from "../../../../shared/mocks/WERC20Mock.sol"; +import {IDestinationRewardManager} from "../../interfaces/IDestinationRewardManager.sol"; +import {DestinationFeeManagerProxy} from "../mocks/DestinationFeeManagerProxy.sol"; + +/** + * @title BaseDestinationFeeManagerTest + * @author Michael Fletcher + * @notice Base class for all feeManager tests + * @dev This contract is intended to be inherited from and not used directly. It contains functionality to setup the feeManager + */ +contract BaseDestinationFeeManagerTest is Test { + //contracts + DestinationFeeManager internal feeManager; + DestinationRewardManager internal rewardManager; + DestinationFeeManagerProxy internal feeManagerProxy; + + ERC20Mock internal link; + WERC20Mock internal native; + + //erc20 config + uint256 internal constant DEFAULT_LINK_MINT_QUANTITY = 100 ether; + uint256 internal constant DEFAULT_NATIVE_MINT_QUANTITY = 100 ether; + + //contract owner + address internal constant INVALID_ADDRESS = address(0); + address internal constant ADMIN = address(uint160(uint256(keccak256("ADMIN")))); + address internal constant USER = address(uint160(uint256(keccak256("USER")))); + address internal constant PROXY = address(uint160(uint256(keccak256("PROXY")))); + + //version masks + bytes32 internal constant V_MASK = 0x0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; + bytes32 internal constant V1_BITMASK = 0x0001000000000000000000000000000000000000000000000000000000000000; + bytes32 internal constant V2_BITMASK = 0x0002000000000000000000000000000000000000000000000000000000000000; + bytes32 internal constant V3_BITMASK = 0x0003000000000000000000000000000000000000000000000000000000000000; + + //feed ids & config digests + bytes32 internal constant DEFAULT_FEED_1_V1 = (keccak256("ETH-USD") & V_MASK) | V1_BITMASK; + bytes32 internal constant DEFAULT_FEED_1_V2 = (keccak256("ETH-USD") & V_MASK) | V2_BITMASK; + bytes32 internal constant DEFAULT_FEED_1_V3 = (keccak256("ETH-USD") & V_MASK) | V3_BITMASK; + + bytes32 internal constant DEFAULT_FEED_2_V3 = (keccak256("LINK-USD") & V_MASK) | V3_BITMASK; + bytes32 internal constant DEFAULT_CONFIG_DIGEST = keccak256("DEFAULT_CONFIG_DIGEST"); + bytes32 internal constant DEFAULT_CONFIG_DIGEST2 = keccak256("DEFAULT_CONFIG_DIGEST2"); + + //report + uint256 internal constant DEFAULT_REPORT_LINK_FEE = 1e10; + uint256 internal constant DEFAULT_REPORT_NATIVE_FEE = 1e12; + + //rewards + uint64 internal constant FEE_SCALAR = 1e18; + + address internal constant NATIVE_WITHDRAW_ADDRESS = address(0); + + //the selector for each error + bytes4 internal immutable INVALID_DISCOUNT_ERROR = DestinationFeeManager.InvalidDiscount.selector; + bytes4 internal immutable INVALID_ADDRESS_ERROR = DestinationFeeManager.InvalidAddress.selector; + bytes4 internal immutable INVALID_SURCHARGE_ERROR = DestinationFeeManager.InvalidSurcharge.selector; + bytes4 internal immutable EXPIRED_REPORT_ERROR = DestinationFeeManager.ExpiredReport.selector; + bytes4 internal immutable INVALID_DEPOSIT_ERROR = DestinationFeeManager.InvalidDeposit.selector; + bytes4 internal immutable INVALID_QUOTE_ERROR = DestinationFeeManager.InvalidQuote.selector; + bytes4 internal immutable UNAUTHORIZED_ERROR = DestinationFeeManager.Unauthorized.selector; + bytes4 internal immutable POOLID_MISMATCH_ERROR = DestinationFeeManager.PoolIdMismatch.selector; + bytes internal constant ONLY_CALLABLE_BY_OWNER_ERROR = "Only callable by owner"; + bytes internal constant INSUFFICIENT_ALLOWANCE_ERROR = "ERC20: insufficient allowance"; + bytes4 internal immutable ZERO_DEFICIT = DestinationFeeManager.ZeroDeficit.selector; + + //events emitted + event SubscriberDiscountUpdated(address indexed subscriber, bytes32 indexed feedId, address token, uint64 discount); + event NativeSurchargeUpdated(uint64 newSurcharge); + event InsufficientLink(IDestinationRewardManager.FeePayment[] feesAndRewards); + event Withdraw(address adminAddress, address recipient, address assetAddress, uint192 quantity); + event LinkDeficitCleared(bytes32 indexed configDigest, uint256 linkQuantity); + event DiscountApplied( + bytes32 indexed configDigest, + address indexed subscriber, + Common.Asset fee, + Common.Asset reward, + uint256 appliedDiscountQuantity + ); + + function setUp() public virtual { + //change to admin user + vm.startPrank(ADMIN); + + //init required contracts + _initializeContracts(); + } + + function _initializeContracts() internal { + link = new ERC20Mock("LINK", "LINK", ADMIN, 0); + native = new WERC20Mock(); + + feeManagerProxy = new DestinationFeeManagerProxy(); + rewardManager = new DestinationRewardManager(address(link)); + feeManager = new DestinationFeeManager( + address(link), + address(native), + address(feeManagerProxy), + address(rewardManager) + ); + + //link the feeManager to the proxy + feeManagerProxy.setDestinationFeeManager(feeManager); + + //link the feeManager to the reward manager + rewardManager.addFeeManager(address(feeManager)); + + //mint some tokens to the admin + link.mint(ADMIN, DEFAULT_LINK_MINT_QUANTITY); + native.mint(ADMIN, DEFAULT_NATIVE_MINT_QUANTITY); + vm.deal(ADMIN, DEFAULT_NATIVE_MINT_QUANTITY); + + //mint some tokens to the user + link.mint(USER, DEFAULT_LINK_MINT_QUANTITY); + native.mint(USER, DEFAULT_NATIVE_MINT_QUANTITY); + vm.deal(USER, DEFAULT_NATIVE_MINT_QUANTITY); + + //mint some tokens to the proxy + link.mint(PROXY, DEFAULT_LINK_MINT_QUANTITY); + native.mint(PROXY, DEFAULT_NATIVE_MINT_QUANTITY); + vm.deal(PROXY, DEFAULT_NATIVE_MINT_QUANTITY); + } + + function setSubscriberDiscount( + address subscriber, + bytes32 feedId, + address token, + uint256 discount, + address sender + ) internal { + //record the current address and switch to the recipient + address originalAddr = msg.sender; + changePrank(sender); + + //set the discount + feeManager.updateSubscriberDiscount(subscriber, feedId, token, uint64(discount)); + + //change back to the original address + changePrank(originalAddr); + } + + function setNativeSurcharge(uint256 surcharge, address sender) public { + //record the current address and switch to the recipient + address originalAddr = msg.sender; + changePrank(sender); + + //set the surcharge + feeManager.setNativeSurcharge(uint64(surcharge)); + + //change back to the original address + changePrank(originalAddr); + } + + // solium-disable-next-line no-unused-vars + function getFee(bytes memory report, address quote, address subscriber) public view returns (Common.Asset memory) { + //get the fee + (Common.Asset memory fee, , ) = feeManager.getFeeAndReward(subscriber, report, quote); + + return fee; + } + + function getReward(bytes memory report, address quote, address subscriber) public view returns (Common.Asset memory) { + //get the reward + (, Common.Asset memory reward, ) = feeManager.getFeeAndReward(subscriber, report, quote); + + return reward; + } + + function getAppliedDiscount(bytes memory report, address quote, address subscriber) public view returns (uint256) { + //get the reward + (, , uint256 appliedDiscount) = feeManager.getFeeAndReward(subscriber, report, quote); + + return appliedDiscount; + } + + function getV1Report(bytes32 feedId) public pure returns (bytes memory) { + return abi.encode(feedId, uint32(0), int192(0), int192(0), int192(0), uint64(0), bytes32(0), uint64(0), uint64(0)); + } + + function getV2Report(bytes32 feedId) public view returns (bytes memory) { + return + abi.encode( + feedId, + uint32(0), + uint32(0), + uint192(DEFAULT_REPORT_NATIVE_FEE), + uint192(DEFAULT_REPORT_LINK_FEE), + uint32(block.timestamp), + int192(0) + ); + } + + function getV3Report(bytes32 feedId) public view returns (bytes memory) { + return + abi.encode( + feedId, + uint32(0), + uint32(0), + uint192(DEFAULT_REPORT_NATIVE_FEE), + uint192(DEFAULT_REPORT_LINK_FEE), + uint32(block.timestamp), + int192(0), + int192(0), + int192(0) + ); + } + + function getV3ReportWithCustomExpiryAndFee( + bytes32 feedId, + uint256 expiry, + uint256 linkFee, + uint256 nativeFee + ) public pure returns (bytes memory) { + return + abi.encode( + feedId, + uint32(0), + uint32(0), + uint192(nativeFee), + uint192(linkFee), + uint32(expiry), + int192(0), + int192(0), + int192(0) + ); + } + + function getLinkQuote() public view returns (address) { + return address(link); + } + + function getNativeQuote() public view returns (address) { + return address(native); + } + + function withdraw(address assetAddress, address recipient, uint256 amount, address sender) public { + //record the current address and switch to the recipient + address originalAddr = msg.sender; + changePrank(sender); + + //set the surcharge + feeManager.withdraw(assetAddress, recipient, uint192(amount)); + + //change back to the original address + changePrank(originalAddr); + } + + function getLinkBalance(address balanceAddress) public view returns (uint256) { + return link.balanceOf(balanceAddress); + } + + function getNativeBalance(address balanceAddress) public view returns (uint256) { + return native.balanceOf(balanceAddress); + } + + function getNativeUnwrappedBalance(address balanceAddress) public view returns (uint256) { + return balanceAddress.balance; + } + + function mintLink(address recipient, uint256 amount) public { + //record the current address and switch to the recipient + address originalAddr = msg.sender; + changePrank(ADMIN); + + //mint the link to the recipient + link.mint(recipient, amount); + + //change back to the original address + changePrank(originalAddr); + } + + function mintNative(address recipient, uint256 amount, address sender) public { + //record the current address and switch to the recipient + address originalAddr = msg.sender; + changePrank(sender); + + //mint the native to the recipient + native.mint(recipient, amount); + + //change back to the original address + changePrank(originalAddr); + } + + function issueUnwrappedNative(address recipient, uint256 quantity) public { + vm.deal(recipient, quantity); + } + + function ProcessFeeAsUser( + bytes32 poolId, + bytes memory payload, + address subscriber, + address tokenAddress, + uint256 wrappedNativeValue, + address sender + ) public { + //record the current address and switch to the recipient + address originalAddr = msg.sender; + changePrank(sender); + + //process the fee + feeManager.processFee{value: wrappedNativeValue}(poolId, payload, abi.encode(tokenAddress), subscriber); + + //change ProcessFeeAsUserback to the original address + changePrank(originalAddr); + } + + function processFee( + bytes32 poolId, + bytes memory payload, + address subscriber, + address feeAddress, + uint256 wrappedNativeValue + ) public { + //record the current address and switch to the recipient + address originalAddr = msg.sender; + changePrank(subscriber); + + //process the fee + feeManagerProxy.processFee{value: wrappedNativeValue}(poolId, payload, abi.encode(feeAddress)); + + //change back to the original address + changePrank(originalAddr); + } + + function processFee( + bytes32[] memory poolIds, + bytes[] memory payloads, + address subscriber, + address feeAddress, + uint256 wrappedNativeValue + ) public { + //record the current address and switch to the recipient + address originalAddr = msg.sender; + changePrank(subscriber); + + //process the fee + feeManagerProxy.processFeeBulk{value: wrappedNativeValue}(poolIds, payloads, abi.encode(feeAddress)); + + //change back to the original address + changePrank(originalAddr); + } + + function getPayload(bytes memory reportPayload) public pure returns (bytes memory) { + return abi.encode([DEFAULT_CONFIG_DIGEST, 0, 0], reportPayload, new bytes32[](1), new bytes32[](1), bytes32("")); + } + + function approveLink(address spender, uint256 quantity, address sender) public { + //record the current address and switch to the recipient + address originalAddr = msg.sender; + changePrank(sender); + + //approve the link to be transferred + link.approve(spender, quantity); + + //change back to the original address + changePrank(originalAddr); + } + + function approveNative(address spender, uint256 quantity, address sender) public { + //record the current address and switch to the recipient + address originalAddr = msg.sender; + changePrank(sender); + + //approve the link to be transferred + native.approve(spender, quantity); + + //change back to the original address + changePrank(originalAddr); + } + + function payLinkDeficit(bytes32 configDigest, address sender) public { + //record the current address and switch to the recipient + address originalAddr = msg.sender; + changePrank(sender); + + //approve the link to be transferred + feeManager.payLinkDeficit(configDigest); + + //change back to the original address + changePrank(originalAddr); + } + + function getLinkDeficit(bytes32 configDigest) public view returns (uint256) { + return feeManager.s_linkDeficit(configDigest); + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/test/fee-manager/DestinationFeeManager.general.t.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/test/fee-manager/DestinationFeeManager.general.t.sol new file mode 100644 index 0000000000..305125c332 --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/test/fee-manager/DestinationFeeManager.general.t.sol @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import "./BaseDestinationFeeManager.t.sol"; + +/** + * @title BaseDestinationFeeManagerTest + * @author Michael Fletcher + * @notice This contract will test the setup functionality of the feemanager + */ +contract DestinationFeeManagerProcessFeeTest is BaseDestinationFeeManagerTest { + function setUp() public override { + super.setUp(); + } + + function test_WithdrawERC20() public { + //simulate a fee + mintLink(address(feeManager), DEFAULT_LINK_MINT_QUANTITY); + + //get the balances to ne used for comparison + uint256 contractBalance = getLinkBalance(address(feeManager)); + uint256 adminBalance = getLinkBalance(ADMIN); + + //the amount to withdraw + uint256 withdrawAmount = contractBalance / 2; + + //withdraw some balance + withdraw(address(link), ADMIN, withdrawAmount, ADMIN); + + //check the balance has been reduced + uint256 newContractBalance = getLinkBalance(address(feeManager)); + uint256 newAdminBalance = getLinkBalance(ADMIN); + + //check the balance is greater than zero + assertGt(newContractBalance, 0); + //check the balance has been reduced by the correct amount + assertEq(newContractBalance, contractBalance - withdrawAmount); + //check the admin balance has increased by the correct amount + assertEq(newAdminBalance, adminBalance + withdrawAmount); + } + + function test_WithdrawUnwrappedNative() public { + //issue funds straight to the contract to bypass the lack of fallback function + issueUnwrappedNative(address(feeManager), DEFAULT_NATIVE_MINT_QUANTITY); + + //get the balances to be used for comparison + uint256 contractBalance = getNativeUnwrappedBalance(address(feeManager)); + uint256 adminBalance = getNativeUnwrappedBalance(ADMIN); + + //the amount to withdraw + uint256 withdrawAmount = contractBalance / 2; + + //withdraw some balance + withdraw(NATIVE_WITHDRAW_ADDRESS, ADMIN, withdrawAmount, ADMIN); + + //check the balance has been reduced + uint256 newContractBalance = getNativeUnwrappedBalance(address(feeManager)); + uint256 newAdminBalance = getNativeUnwrappedBalance(ADMIN); + + //check the balance is greater than zero + assertGt(newContractBalance, 0); + //check the balance has been reduced by the correct amount + assertEq(newContractBalance, contractBalance - withdrawAmount); + //check the admin balance has increased by the correct amount + assertEq(newAdminBalance, adminBalance + withdrawAmount); + } + + function test_WithdrawNonAdminAddr() public { + //simulate a fee + mintLink(address(feeManager), DEFAULT_LINK_MINT_QUANTITY); + + //should revert if not admin + vm.expectRevert(ONLY_CALLABLE_BY_OWNER_ERROR); + + //withdraw some balance + withdraw(address(link), ADMIN, DEFAULT_LINK_MINT_QUANTITY, USER); + } + + function test_eventIsEmittedAfterSurchargeIsSet() public { + //native surcharge + uint64 nativeSurcharge = FEE_SCALAR / 5; + + //expect an emit + vm.expectEmit(); + + //emit the event that is expected to be emitted + emit NativeSurchargeUpdated(nativeSurcharge); + + //set the surcharge + setNativeSurcharge(nativeSurcharge, ADMIN); + } + + function test_subscriberDiscountEventIsEmittedOnUpdate() public { + //native surcharge + uint64 discount = FEE_SCALAR / 3; + + //an event should be emitted + vm.expectEmit(); + + //emit the event that is expected to be emitted + emit SubscriberDiscountUpdated(USER, DEFAULT_FEED_1_V3, address(native), discount); + + //set the surcharge + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(native), discount, ADMIN); + } + + function test_eventIsEmittedUponWithdraw() public { + //simulate a fee + mintLink(address(feeManager), DEFAULT_LINK_MINT_QUANTITY); + + //the amount to withdraw + uint192 withdrawAmount = 1; + + //expect an emit + vm.expectEmit(); + + //the event to be emitted + emit Withdraw(ADMIN, ADMIN, address(link), withdrawAmount); + + //withdraw some balance + withdraw(address(link), ADMIN, withdrawAmount, ADMIN); + } + + function test_linkAvailableForPaymentReturnsLinkBalance() public { + //simulate a deposit of link for the conversion pool + mintLink(address(feeManager), DEFAULT_REPORT_LINK_FEE); + + //check there's a balance + assertGt(getLinkBalance(address(feeManager)), 0); + + //check the link available for payment is the link balance + assertEq(feeManager.linkAvailableForPayment(), getLinkBalance(address(feeManager))); + } + + function test_payLinkDeficit() public { + //get the default payload + bytes memory payload = getPayload(getV2Report(DEFAULT_FEED_1_V3)); + + approveNative(address(feeManager), DEFAULT_REPORT_NATIVE_FEE, USER); + + //not enough funds in the reward pool should trigger an insufficient link event + vm.expectEmit(); + + IDestinationRewardManager.FeePayment[] memory contractFees = new IDestinationRewardManager.FeePayment[](1); + contractFees[0] = IDestinationRewardManager.FeePayment(DEFAULT_CONFIG_DIGEST, uint192(DEFAULT_REPORT_LINK_FEE)); + + emit InsufficientLink(contractFees); + + //process the fee + processFee(contractFees[0].poolId, payload, USER, address(native), 0); + + //double check the rewardManager balance is 0 + assertEq(getLinkBalance(address(rewardManager)), 0); + + //simulate a deposit of link to cover the deficit + mintLink(address(feeManager), DEFAULT_REPORT_LINK_FEE); + + vm.expectEmit(); + emit LinkDeficitCleared(DEFAULT_CONFIG_DIGEST, DEFAULT_REPORT_LINK_FEE); + + //pay the deficit which will transfer link from the rewardManager to the rewardManager + payLinkDeficit(DEFAULT_CONFIG_DIGEST, ADMIN); + + //check the rewardManager received the link + assertEq(getLinkBalance(address(rewardManager)), DEFAULT_REPORT_LINK_FEE); + } + + function test_payLinkDeficitTwice() public { + //get the default payload + bytes memory payload = getPayload(getV2Report(DEFAULT_FEED_1_V3)); + + approveNative(address(feeManager), DEFAULT_REPORT_NATIVE_FEE, USER); + + //not enough funds in the reward pool should trigger an insufficient link event + vm.expectEmit(); + + IDestinationRewardManager.FeePayment[] memory contractFees = new IDestinationRewardManager.FeePayment[](1); + contractFees[0] = IDestinationRewardManager.FeePayment(DEFAULT_CONFIG_DIGEST, uint192(DEFAULT_REPORT_LINK_FEE)); + + //emit the event that is expected to be emitted + emit InsufficientLink(contractFees); + + //process the fee + processFee(contractFees[0].poolId, payload, USER, address(native), 0); + + //double check the rewardManager balance is 0 + assertEq(getLinkBalance(address(rewardManager)), 0); + + //simulate a deposit of link to cover the deficit + mintLink(address(feeManager), DEFAULT_REPORT_LINK_FEE); + + vm.expectEmit(); + emit LinkDeficitCleared(DEFAULT_CONFIG_DIGEST, DEFAULT_REPORT_LINK_FEE); + + //pay the deficit which will transfer link from the rewardManager to the rewardManager + payLinkDeficit(DEFAULT_CONFIG_DIGEST, ADMIN); + + //check the rewardManager received the link + assertEq(getLinkBalance(address(rewardManager)), DEFAULT_REPORT_LINK_FEE); + + //paying again should revert with 0 + vm.expectRevert(ZERO_DEFICIT); + + payLinkDeficit(DEFAULT_CONFIG_DIGEST, ADMIN); + } + + function test_payLinkDeficitPaysAllFeesProcessed() public { + //get the default payload + bytes memory payload = getPayload(getV2Report(DEFAULT_FEED_1_V3)); + + //approve the native to be transferred from the user + approveNative(address(feeManager), DEFAULT_REPORT_NATIVE_FEE * 2, USER); + + //processing the fee will transfer the native from the user to the feeManager + processFee(DEFAULT_CONFIG_DIGEST, payload, USER, address(native), 0); + processFee(DEFAULT_CONFIG_DIGEST, payload, USER, address(native), 0); + + //check the deficit has been increased twice + assertEq(getLinkDeficit(DEFAULT_CONFIG_DIGEST), DEFAULT_REPORT_LINK_FEE * 2); + + //double check the rewardManager balance is 0 + assertEq(getLinkBalance(address(rewardManager)), 0); + + //simulate a deposit of link to cover the deficit + mintLink(address(feeManager), DEFAULT_REPORT_LINK_FEE * 2); + + vm.expectEmit(); + emit LinkDeficitCleared(DEFAULT_CONFIG_DIGEST, DEFAULT_REPORT_LINK_FEE * 2); + + //pay the deficit which will transfer link from the rewardManager to the rewardManager + payLinkDeficit(DEFAULT_CONFIG_DIGEST, ADMIN); + + //check the rewardManager received the link + assertEq(getLinkBalance(address(rewardManager)), DEFAULT_REPORT_LINK_FEE * 2); + } + + function test_payLinkDeficitOnlyCallableByAdmin() public { + vm.expectRevert(ONLY_CALLABLE_BY_OWNER_ERROR); + + payLinkDeficit(DEFAULT_CONFIG_DIGEST, USER); + } + + function test_revertOnSettingAnAddressZeroVerifier() public { + vm.expectRevert(INVALID_ADDRESS_ERROR); + feeManager.addVerifier(address(0)); + } + + function test_onlyCallableByOwnerReverts() public { + address STRANGER = address(999); + changePrank(STRANGER); + vm.expectRevert(bytes("Only callable by owner")); + feeManager.addVerifier(address(0)); + } + + function test_addVerifierExistingAddress() public { + address dummyAddress = address(998); + feeManager.addVerifier(dummyAddress); + vm.expectRevert(INVALID_ADDRESS_ERROR); + feeManager.addVerifier(dummyAddress); + } + + function test_addVerifier() public { + address dummyAddress = address(998); + feeManager.addVerifier(dummyAddress); + vm.expectRevert(INVALID_ADDRESS_ERROR); + feeManager.addVerifier(dummyAddress); + + // check calls to setFeeRecipients it should not error unauthorized + changePrank(dummyAddress); + bytes32 dummyConfigDigest = 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef; + Common.AddressAndWeight[] memory recipients = new Common.AddressAndWeight[](1); + recipients[0] = Common.AddressAndWeight(address(991), 1e18); + feeManager.setFeeRecipients(dummyConfigDigest, recipients); + + // removing this verifier should result in unauthorized when calling setFeeRecipients + changePrank(ADMIN); + feeManager.removeVerifier(dummyAddress); + changePrank(dummyAddress); + vm.expectRevert(UNAUTHORIZED_ERROR); + feeManager.setFeeRecipients(dummyConfigDigest, recipients); + } + + function test_removeVerifierZeroAaddress() public { + address dummyAddress = address(0); + vm.expectRevert(INVALID_ADDRESS_ERROR); + feeManager.removeVerifier(dummyAddress); + } + + function test_removeVerifierNonExistentAddress() public { + address dummyAddress = address(991); + vm.expectRevert(INVALID_ADDRESS_ERROR); + feeManager.removeVerifier(dummyAddress); + } + + function test_setRewardManagerZeroAddress() public { + vm.expectRevert(INVALID_ADDRESS_ERROR); + feeManager.setRewardManager(address(0)); + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/test/fee-manager/DestinationFeeManager.getFeeAndReward.t.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/test/fee-manager/DestinationFeeManager.getFeeAndReward.t.sol new file mode 100644 index 0000000000..30be694df2 --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/test/fee-manager/DestinationFeeManager.getFeeAndReward.t.sol @@ -0,0 +1,606 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {Common} from "../../../libraries/Common.sol"; +import "./BaseDestinationFeeManager.t.sol"; + +/** + * @title BaseFeeManagerTest + * @author Michael Fletcher + * @notice This contract will test the functionality of the feeManager's getFeeAndReward + */ +contract DestinationFeeManagerProcessFeeTest is BaseDestinationFeeManagerTest { + function test_baseFeeIsAppliedForNative() public view { + //get the fee required by the feeManager + Common.Asset memory fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + + //fee should be the default + assertEq(fee.amount, DEFAULT_REPORT_NATIVE_FEE); + } + + function test_baseFeeIsAppliedForLink() public view { + //get the fee required by the feeManager + Common.Asset memory fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getLinkQuote(), USER); + + //fee should be the default + assertEq(fee.amount, DEFAULT_REPORT_LINK_FEE); + } + + function test_discountAIsNotAppliedWhenSetForOtherUsers() public { + //set the subscriber discount for another user + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(link), FEE_SCALAR / 2, ADMIN); + + //get the fee required by the feeManager + Common.Asset memory fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), INVALID_ADDRESS); + + //fee should be the default + assertEq(fee.amount, DEFAULT_REPORT_NATIVE_FEE); + } + + function test_discountIsNotAppliedForInvalidTokenAddress() public { + //should revert with invalid address as it's not a configured token + vm.expectRevert(INVALID_ADDRESS_ERROR); + + //set the subscriber discount for another user + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, INVALID_ADDRESS, FEE_SCALAR / 2, ADMIN); + } + + function test_discountIsAppliedForLink() public { + //set the subscriber discount to 50% + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(link), FEE_SCALAR / 2, ADMIN); + + //get the fee required by the feeManager + Common.Asset memory fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getLinkQuote(), USER); + + //fee should be half the default + assertEq(fee.amount, DEFAULT_REPORT_LINK_FEE / 2); + } + + function test_DiscountIsAppliedForNative() public { + //set the subscriber discount to 50% + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(native), FEE_SCALAR / 2, ADMIN); + + //get the fee required by the feeManager + Common.Asset memory fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + + //fee should be half the default + assertEq(fee.amount, DEFAULT_REPORT_NATIVE_FEE / 2); + } + + function test_discountIsNoLongerAppliedAfterRemoving() public { + //set the subscriber discount to 50% + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(link), FEE_SCALAR / 2, ADMIN); + + //get the fee required by the feeManager + Common.Asset memory fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getLinkQuote(), USER); + + //fee should be half the default + assertEq(fee.amount, DEFAULT_REPORT_LINK_FEE / 2); + + //remove the discount + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(link), 0, ADMIN); + + //get the fee required by the feeManager + fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getLinkQuote(), USER); + + //fee should be the default + assertEq(fee.amount, DEFAULT_REPORT_LINK_FEE); + } + + function test_surchargeIsApplied() public { + //native surcharge + uint256 nativeSurcharge = FEE_SCALAR / 5; + + //set the surcharge + setNativeSurcharge(nativeSurcharge, ADMIN); + + //get the fee required by the feeManager + Common.Asset memory fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + + //calculate the expected surcharge + uint256 expectedSurcharge = ((DEFAULT_REPORT_NATIVE_FEE * nativeSurcharge) / FEE_SCALAR); + + //expected fee should the base fee offset by the surcharge and discount + assertEq(fee.amount, DEFAULT_REPORT_NATIVE_FEE + expectedSurcharge); + } + + function test_surchargeIsNotAppliedForLinkFee() public { + //native surcharge + uint256 nativeSurcharge = FEE_SCALAR / 5; + + //set the surcharge + setNativeSurcharge(nativeSurcharge, ADMIN); + + //get the fee required by the feeManager + Common.Asset memory fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getLinkQuote(), USER); + + //fee should be the default + assertEq(fee.amount, DEFAULT_REPORT_LINK_FEE); + } + + function test_surchargeIsNoLongerAppliedAfterRemoving() public { + //native surcharge + uint256 nativeSurcharge = FEE_SCALAR / 5; + + //set the surcharge + setNativeSurcharge(nativeSurcharge, ADMIN); + + //get the fee required by the feeManager + Common.Asset memory fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + + //calculate the expected surcharge + uint256 expectedSurcharge = ((DEFAULT_REPORT_NATIVE_FEE * nativeSurcharge) / FEE_SCALAR); + + //expected fee should be the base fee offset by the surcharge and discount + assertEq(fee.amount, DEFAULT_REPORT_NATIVE_FEE + expectedSurcharge); + + //remove the surcharge + setNativeSurcharge(0, ADMIN); + + //get the fee required by the feeManager + fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + + //fee should be the default + assertEq(fee.amount, DEFAULT_REPORT_NATIVE_FEE); + } + + function test_feeIsUpdatedAfterNewSurchargeIsApplied() public { + //native surcharge + uint256 nativeSurcharge = FEE_SCALAR / 5; + + //set the surcharge + setNativeSurcharge(nativeSurcharge, ADMIN); + + //get the fee required by the feeManager + Common.Asset memory fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + + //calculate the expected surcharge + uint256 expectedSurcharge = ((DEFAULT_REPORT_NATIVE_FEE * nativeSurcharge) / FEE_SCALAR); + + //expected fee should the base fee offset by the surcharge and discount + assertEq(fee.amount, DEFAULT_REPORT_NATIVE_FEE + expectedSurcharge); + + //change the surcharge + setNativeSurcharge(nativeSurcharge, ADMIN); + + //get the fee required by the feeManager + fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + + //calculate the expected surcharge + expectedSurcharge = ((DEFAULT_REPORT_NATIVE_FEE * nativeSurcharge) / FEE_SCALAR); + + //expected fee should the base fee offset by the surcharge and discount + assertEq(fee.amount, DEFAULT_REPORT_NATIVE_FEE + expectedSurcharge); + } + + function test_surchargeIsAppliedForNativeFeeWithDiscount() public { + //native surcharge + uint256 nativeSurcharge = FEE_SCALAR / 5; + + //set the subscriber discount to 50% + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(native), FEE_SCALAR / 2, ADMIN); + + //set the surcharge + setNativeSurcharge(nativeSurcharge, ADMIN); + + //get the fee required by the feeManager + Common.Asset memory fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + + //calculate the expected surcharge quantity + uint256 expectedSurcharge = ((DEFAULT_REPORT_NATIVE_FEE * nativeSurcharge) / FEE_SCALAR); + + //calculate the expected discount quantity + uint256 expectedDiscount = ((DEFAULT_REPORT_NATIVE_FEE + expectedSurcharge) / 2); + + //expected fee should the base fee offset by the surcharge and discount + assertEq(fee.amount, DEFAULT_REPORT_NATIVE_FEE + expectedSurcharge - expectedDiscount); + } + + function test_emptyQuoteRevertsWithError() public { + //expect a revert + vm.expectRevert(INVALID_QUOTE_ERROR); + + //get the fee required by the feeManager + getFee(getV3Report(DEFAULT_FEED_1_V3), address(0), USER); + } + + function test_nativeSurcharge100Percent() public { + //set the surcharge + setNativeSurcharge(FEE_SCALAR, ADMIN); + + //get the fee required by the feeManager + Common.Asset memory fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + + //fee should be twice the base fee + assertEq(fee.amount, DEFAULT_REPORT_NATIVE_FEE * 2); + } + + function test_nativeSurcharge0Percent() public { + //set the surcharge + setNativeSurcharge(0, ADMIN); + + //get the fee required by the feeManager + Common.Asset memory fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + + //fee should base fee + assertEq(fee.amount, DEFAULT_REPORT_NATIVE_FEE); + } + + function test_nativeSurchargeCannotExceed100Percent() public { + //should revert if surcharge is greater than 100% + vm.expectRevert(INVALID_SURCHARGE_ERROR); + + //set the surcharge above the max + setNativeSurcharge(FEE_SCALAR + 1, ADMIN); + } + + function test_discountIsAppliedWith100PercentSurcharge() public { + //set the subscriber discount to 50% + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(native), FEE_SCALAR / 2, ADMIN); + + //set the surcharge + setNativeSurcharge(FEE_SCALAR, ADMIN); + + //get the fee required by the feeManager + Common.Asset memory fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + + //calculate the expected discount quantity + uint256 expectedDiscount = DEFAULT_REPORT_NATIVE_FEE; + + //fee should be twice the surcharge minus the discount + assertEq(fee.amount, DEFAULT_REPORT_NATIVE_FEE * 2 - expectedDiscount); + } + + function test_feeIsZeroWith100PercentDiscount() public { + //set the subscriber discount to 100% + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(native), FEE_SCALAR, ADMIN); + + //get the fee required by the feeManager + Common.Asset memory fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + + //fee should be zero + assertEq(fee.amount, 0); + } + + function test_feeIsUpdatedAfterDiscountIsRemoved() public { + //set the subscriber discount to 50% + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(native), FEE_SCALAR / 2, ADMIN); + + //get the fee required by the feeManager + Common.Asset memory fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + + //calculate the expected discount quantity + uint256 expectedDiscount = DEFAULT_REPORT_NATIVE_FEE / 2; + + //fee should be 50% of the base fee + assertEq(fee.amount, DEFAULT_REPORT_NATIVE_FEE - expectedDiscount); + + //remove the discount + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(native), 0, ADMIN); + + //get the fee required by the feeManager + fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + + //fee should be the base fee + assertEq(fee.amount, DEFAULT_REPORT_NATIVE_FEE); + } + + function test_feeIsUpdatedAfterNewDiscountIsApplied() public { + //set the subscriber discount to 50% + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(native), FEE_SCALAR / 2, ADMIN); + + //get the fee required by the feeManager + Common.Asset memory fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + + //calculate the expected discount quantity + uint256 expectedDiscount = DEFAULT_REPORT_NATIVE_FEE / 2; + + //fee should be 50% of the base fee + assertEq(fee.amount, DEFAULT_REPORT_NATIVE_FEE - expectedDiscount); + + //change the discount to 25% + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(native), FEE_SCALAR / 4, ADMIN); + + //get the fee required by the feeManager + fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + + //expected discount is now 25% + expectedDiscount = DEFAULT_REPORT_NATIVE_FEE / 4; + + //fee should be the base fee minus the expected discount + assertEq(fee.amount, DEFAULT_REPORT_NATIVE_FEE - expectedDiscount); + } + + function test_setDiscountOver100Percent() public { + //should revert with invalid discount + vm.expectRevert(INVALID_DISCOUNT_ERROR); + + //set the subscriber discount to over 100% + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(native), FEE_SCALAR + 1, ADMIN); + } + + function test_surchargeIsNotAppliedWith100PercentDiscount() public { + //native surcharge + uint256 nativeSurcharge = FEE_SCALAR / 5; + + //set the subscriber discount to 100% + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(native), FEE_SCALAR, ADMIN); + + //set the surcharge + setNativeSurcharge(nativeSurcharge, ADMIN); + + //get the fee required by the feeManager + Common.Asset memory fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + + //fee should be zero + assertEq(fee.amount, 0); + } + + function test_nonAdminUserCanNotSetDiscount() public { + //should revert with unauthorized + vm.expectRevert(ONLY_CALLABLE_BY_OWNER_ERROR); + + //set the subscriber discount to 50% + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(native), FEE_SCALAR, USER); + } + + function test_surchargeFeeRoundsUpWhenUneven() public { + //native surcharge + uint256 nativeSurcharge = FEE_SCALAR / 3; + + //set the surcharge + setNativeSurcharge(nativeSurcharge, ADMIN); + + //get the fee required by the feeManager + Common.Asset memory fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + + //calculate the expected surcharge quantity + uint256 expectedSurcharge = (DEFAULT_REPORT_NATIVE_FEE * nativeSurcharge) / FEE_SCALAR; + + //expected fee should the base fee offset by the expected surcharge + assertEq(fee.amount, DEFAULT_REPORT_NATIVE_FEE + expectedSurcharge + 1); + } + + function test_discountFeeRoundsDownWhenUneven() public { + //native surcharge + uint256 discount = FEE_SCALAR / 3; + + //set the subscriber discount to 33.333% + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(native), discount, ADMIN); + + //get the fee required by the feeManager + Common.Asset memory fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + + //calculate the expected quantity + uint256 expectedDiscount = ((DEFAULT_REPORT_NATIVE_FEE * discount) / FEE_SCALAR); + + //expected fee should the base fee offset by the expected surcharge + assertEq(fee.amount, DEFAULT_REPORT_NATIVE_FEE - expectedDiscount); + } + + function test_reportWithNoExpiryOrFeeReturnsZero() public view { + //get the fee required by the feeManager + Common.Asset memory fee = getFee(getV1Report(DEFAULT_FEED_1_V1), getNativeQuote(), USER); + + //fee should be zero + assertEq(fee.amount, 0); + } + + function test_correctDiscountIsAppliedWhenBothTokensAreDiscounted() public { + //set the subscriber and native discounts + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(link), FEE_SCALAR / 4, ADMIN); + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(native), FEE_SCALAR / 2, ADMIN); + + //get the fee required by the feeManager for both tokens + Common.Asset memory linkFee = getFee(getV3Report(DEFAULT_FEED_1_V3), getLinkQuote(), USER); + Common.Asset memory nativeFee = getFee(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + + //calculate the expected discount quantity for each token + uint256 expectedDiscountLink = (DEFAULT_REPORT_LINK_FEE * FEE_SCALAR) / 4 / FEE_SCALAR; + uint256 expectedDiscountNative = (DEFAULT_REPORT_NATIVE_FEE * FEE_SCALAR) / 2 / FEE_SCALAR; + + //check the fee calculation for each token + assertEq(linkFee.amount, DEFAULT_REPORT_LINK_FEE - expectedDiscountLink); + assertEq(nativeFee.amount, DEFAULT_REPORT_NATIVE_FEE - expectedDiscountNative); + } + + function test_discountIsNotAppliedToOtherFeeds() public { + //set the subscriber discount to 50% + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(native), FEE_SCALAR / 2, ADMIN); + + //get the fee required by the feeManager + Common.Asset memory fee = getFee(getV3Report(DEFAULT_FEED_2_V3), getNativeQuote(), USER); + + //fee should be the base fee + assertEq(fee.amount, DEFAULT_REPORT_NATIVE_FEE); + } + + function test_noFeeIsAppliedWhenReportHasZeroFee() public { + //set the subscriber discount to 50% + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(native), FEE_SCALAR / 2, ADMIN); + + //get the fee required by the feeManager + Common.Asset memory fee = getFee( + getV3ReportWithCustomExpiryAndFee(DEFAULT_FEED_1_V3, uint32(block.timestamp), 0, 0), + getNativeQuote(), + USER + ); + + //fee should be zero + assertEq(fee.amount, 0); + } + + function test_noFeeIsAppliedWhenReportHasZeroFeeAndDiscountAndSurchargeIsSet() public { + //set the subscriber discount to 50% + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(native), FEE_SCALAR / 2, ADMIN); + + //set the surcharge + setNativeSurcharge(FEE_SCALAR / 2, ADMIN); + + //get the fee required by the feeManager + Common.Asset memory fee = getFee( + getV3ReportWithCustomExpiryAndFee(DEFAULT_FEED_1_V3, uint32(block.timestamp), 0, 0), + getNativeQuote(), + USER + ); + + //fee should be zero + assertEq(fee.amount, 0); + } + + function test_nativeSurchargeEventIsEmittedOnUpdate() public { + //native surcharge + uint64 nativeSurcharge = FEE_SCALAR / 3; + + //an event should be emitted + vm.expectEmit(); + + //emit the event that is expected to be emitted + emit NativeSurchargeUpdated(nativeSurcharge); + + //set the surcharge + setNativeSurcharge(nativeSurcharge, ADMIN); + } + + function test_getBaseRewardWithLinkQuote() public view { + //get the fee required by the feeManager + Common.Asset memory reward = getReward(getV3Report(DEFAULT_FEED_1_V3), getLinkQuote(), USER); + + //the reward should equal the base fee + assertEq(reward.amount, DEFAULT_REPORT_LINK_FEE); + } + + function test_getRewardWithLinkQuoteAndLinkDiscount() public { + //set the link discount + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(link), FEE_SCALAR / 2, ADMIN); + + //get the fee required by the feeManager + Common.Asset memory reward = getReward(getV3Report(DEFAULT_FEED_1_V3), getLinkQuote(), USER); + + //the reward should equal the discounted base fee + assertEq(reward.amount, DEFAULT_REPORT_LINK_FEE / 2); + } + + function test_getRewardWithNativeQuote() public view { + //get the fee required by the feeManager + Common.Asset memory reward = getReward(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + + //the reward should equal the base fee in link + assertEq(reward.amount, DEFAULT_REPORT_LINK_FEE); + } + + function test_getRewardWithNativeQuoteAndSurcharge() public { + //set the native surcharge + setNativeSurcharge(FEE_SCALAR / 2, ADMIN); + + //get the fee required by the feeManager + Common.Asset memory reward = getReward(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + + //the reward should equal the base fee in link regardless of the surcharge + assertEq(reward.amount, DEFAULT_REPORT_LINK_FEE); + } + + function test_getRewardWithLinkDiscount() public { + //set the link discount + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(link), FEE_SCALAR / 2, ADMIN); + + //get the fee required by the feeManager + Common.Asset memory reward = getReward(getV3Report(DEFAULT_FEED_1_V3), getLinkQuote(), USER); + + //the reward should equal the discounted base fee + assertEq(reward.amount, DEFAULT_REPORT_LINK_FEE / 2); + } + + function test_getLinkFeeIsRoundedUp() public { + //set the link discount + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(link), FEE_SCALAR / 3, ADMIN); + + //get the fee required by the feeManager + Common.Asset memory fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getLinkQuote(), USER); + + //the reward should equal .66% + 1 of the base fee due to a 33% discount rounded up + assertEq(fee.amount, (DEFAULT_REPORT_LINK_FEE * 2) / 3 + 1); + } + + function test_getLinkRewardIsSameAsFee() public { + //set the link discount + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(link), FEE_SCALAR / 3, ADMIN); + + //get the fee required by the feeManager + Common.Asset memory fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getLinkQuote(), USER); + Common.Asset memory reward = getReward(getV3Report(DEFAULT_FEED_1_V3), getLinkQuote(), USER); + + //check the reward is in link + assertEq(fee.assetAddress, address(link)); + + //the reward should equal .66% of the base fee due to a 33% discount rounded down + assertEq(reward.amount, fee.amount); + } + + function test_getLinkRewardWithNativeQuoteAndSurchargeWithLinkDiscount() public { + //set the native surcharge + setNativeSurcharge(FEE_SCALAR / 2, ADMIN); + + //set the link discount + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(link), FEE_SCALAR / 3, ADMIN); + + //get the fee required by the feeManager + Common.Asset memory reward = getReward(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + + //the reward should equal the base fee in link regardless of the surcharge + assertEq(reward.amount, DEFAULT_REPORT_LINK_FEE); + } + + function test_testRevertIfReportHasExpired() public { + //expect a revert + vm.expectRevert(EXPIRED_REPORT_ERROR); + + //get the fee required by the feeManager + getFee( + getV3ReportWithCustomExpiryAndFee( + DEFAULT_FEED_1_V3, + block.timestamp - 1, + DEFAULT_REPORT_LINK_FEE, + DEFAULT_REPORT_NATIVE_FEE + ), + getNativeQuote(), + USER + ); + } + + function test_discountIsReturnedForLink() public { + //set the subscriber discount to 50% + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(link), FEE_SCALAR / 2, ADMIN); + + //get the fee applied + uint256 discount = getAppliedDiscount(getV3Report(DEFAULT_FEED_1_V3), getLinkQuote(), USER); + + //fee should be half the default + assertEq(discount, FEE_SCALAR / 2); + } + + function test_DiscountIsReturnedForNative() public { + //set the subscriber discount to 50% + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(native), FEE_SCALAR / 2, ADMIN); + + //get the discount applied + uint256 discount = getAppliedDiscount(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + + //fee should be half the default + assertEq(discount, FEE_SCALAR / 2); + } + + function test_DiscountIsReturnedForNativeWithSurcharge() public { + //set the subscriber discount to 50% + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(native), FEE_SCALAR / 2, ADMIN); + + //set the surcharge + setNativeSurcharge(FEE_SCALAR / 5, ADMIN); + + //get the discount applied + uint256 discount = getAppliedDiscount(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + + //fee should be half the default + assertEq(discount, FEE_SCALAR / 2); + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/test/fee-manager/DestinationFeeManager.processFee.t.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/test/fee-manager/DestinationFeeManager.processFee.t.sol new file mode 100644 index 0000000000..0880352dca --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/test/fee-manager/DestinationFeeManager.processFee.t.sol @@ -0,0 +1,492 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {Common} from "../../../libraries/Common.sol"; +import "./BaseDestinationFeeManager.t.sol"; +import {IDestinationRewardManager} from "../../interfaces/IDestinationRewardManager.sol"; + +/** + * @title BaseFeeManagerTest + * @author Michael Fletcher + * @notice This contract will test the functionality of the feeManager processFee + */ +contract DestinationFeeManagerProcessFeeTest is BaseDestinationFeeManagerTest { + function setUp() public override { + super.setUp(); + } + + function test_nonAdminProxyUserCannotProcessFee() public { + //get the default payload + bytes memory payload = getPayload(getV3Report(DEFAULT_FEED_1_V3)); + + //should revert as the user is not the owner + vm.expectRevert(UNAUTHORIZED_ERROR); + + //process the fee + ProcessFeeAsUser(DEFAULT_CONFIG_DIGEST, payload, USER, address(link), 0, USER); + } + + function test_processFeeAsProxy() public { + //get the default payload + bytes memory payload = getPayload(getV3Report(DEFAULT_FEED_1_V3)); + + //approve the link to be transferred from the from the subscriber to the rewardManager + approveLink(address(rewardManager), DEFAULT_REPORT_LINK_FEE, USER); + + //processing the fee will transfer the link from the user to the rewardManager + processFee(DEFAULT_CONFIG_DIGEST, payload, USER, address(link), 0); + + //check the link has been transferred + assertEq(getLinkBalance(address(rewardManager)), DEFAULT_REPORT_LINK_FEE); + + //check the user has had the link fee deducted + assertEq(getLinkBalance(USER), DEFAULT_LINK_MINT_QUANTITY - DEFAULT_REPORT_LINK_FEE); + } + + function test_processFeeIfSubscriberIsSelf() public { + //get the default payload + bytes memory payload = getPayload(getV3Report(DEFAULT_FEED_1_V3)); + + //expect a revert due to the feeManager being the subscriber + vm.expectRevert(INVALID_ADDRESS_ERROR); + + //process the fee will fail due to assertion + processFee(DEFAULT_CONFIG_DIGEST, payload, address(feeManager), address(native), 0); + } + + function test_processFeeWithWithEmptyQuotePayload() public { + //get the default payload + bytes memory payload = getPayload(getV3Report(DEFAULT_FEED_1_V3)); + + //expect a revert as the quote is invalid + vm.expectRevert(); + + //processing the fee will transfer the link by default + processFee(DEFAULT_CONFIG_DIGEST, payload, USER, address(0), 0); + } + + function test_processFeeWithWithZeroQuotePayload() public { + //get the default payload + bytes memory payload = getPayload(getV3Report(DEFAULT_FEED_1_V3)); + + //expect a revert as the quote is invalid + vm.expectRevert(INVALID_QUOTE_ERROR); + + //processing the fee will transfer the link by default + processFee(DEFAULT_CONFIG_DIGEST, payload, USER, INVALID_ADDRESS, 0); + } + + function test_processFeeWithWithCorruptQuotePayload() public { + //get the default payload + bytes memory payload = abi.encode( + [DEFAULT_CONFIG_DIGEST, 0, 0], + getV3Report(DEFAULT_FEED_1_V3), + new bytes32[](1), + new bytes32[](1), + bytes32("") + ); + + //expect an evm revert as the quote is corrupt + vm.expectRevert(); + + //processing the fee will not withdraw anything as there is no fee to collect + processFee(DEFAULT_CONFIG_DIGEST, payload, USER, address(link), 0); + } + + function test_processFeeDefaultReportsStillVerifiesWithEmptyQuote() public { + //get the default payload + bytes memory payload = getPayload(getV1Report(DEFAULT_FEED_1_V1)); + + //processing the fee will transfer the link from the user to the rewardManager + processFee(DEFAULT_CONFIG_DIGEST, payload, USER, address(0), 0); + } + + function test_processFeeWithDefaultReportPayloadAndQuoteStillVerifies() public { + //get the default payload + bytes memory payload = getPayload(getV1Report(DEFAULT_FEED_1_V1)); + + //processing the fee will not withdraw anything as there is no fee to collect + processFee(DEFAULT_CONFIG_DIGEST, payload, USER, address(link), 0); + } + + function test_processFeeNative() public { + //simulate a deposit of link for the conversion pool + mintLink(address(feeManager), DEFAULT_REPORT_LINK_FEE); + + //get the default payload + bytes memory payload = getPayload(getV3Report(DEFAULT_FEED_1_V3)); + + //approve the native to be transferred from the user + approveNative(address(feeManager), DEFAULT_REPORT_NATIVE_FEE, USER); + + //processing the fee will transfer the native from the user to the feeManager + processFee(DEFAULT_CONFIG_DIGEST, payload, USER, address(native), 0); + + //check the native has been transferred + assertEq(getNativeBalance(address(feeManager)), DEFAULT_REPORT_NATIVE_FEE); + + //check the link has been transferred to the rewardManager + assertEq(getLinkBalance(address(rewardManager)), DEFAULT_REPORT_LINK_FEE); + + //check the feeManager has had the link deducted, the remaining balance should be 0 + assertEq(getLinkBalance(address(feeManager)), 0); + + //check the subscriber has had the native deducted + assertEq(getNativeBalance(USER), DEFAULT_NATIVE_MINT_QUANTITY - DEFAULT_REPORT_NATIVE_FEE); + } + + function test_processFeeEmitsEventIfNotEnoughLink() public { + //simulate a deposit of half the link required for the fee + mintLink(address(feeManager), DEFAULT_REPORT_LINK_FEE / 2); + + //get the default payload + bytes memory payload = getPayload(getV3Report(DEFAULT_FEED_1_V3)); + + //approve the native to be transferred from the user + approveNative(address(feeManager), DEFAULT_REPORT_NATIVE_FEE, USER); + + //expect an emit as there's not enough link + vm.expectEmit(); + + IDestinationRewardManager.FeePayment[] memory contractFees = new IDestinationRewardManager.FeePayment[](1); + contractFees[0] = IDestinationRewardManager.FeePayment(DEFAULT_CONFIG_DIGEST, uint192(DEFAULT_REPORT_LINK_FEE)); + + //emit the event that is expected to be emitted + emit InsufficientLink(contractFees); + + //processing the fee will transfer the native from the user to the feeManager + processFee(DEFAULT_CONFIG_DIGEST, payload, USER, address(native), 0); + + //check the native has been transferred + assertEq(getNativeBalance(address(feeManager)), DEFAULT_REPORT_NATIVE_FEE); + + //check no link has been transferred to the rewardManager + assertEq(getLinkBalance(address(rewardManager)), 0); + assertEq(getLinkBalance(address(feeManager)), DEFAULT_REPORT_LINK_FEE / 2); + + //check the subscriber has had the native deducted + assertEq(getNativeBalance(USER), DEFAULT_NATIVE_MINT_QUANTITY - DEFAULT_REPORT_NATIVE_FEE); + } + + function test_processFeeWithUnwrappedNative() public { + //simulate a deposit of link for the conversion pool + mintLink(address(feeManager), DEFAULT_REPORT_LINK_FEE); + + //get the default payload + bytes memory payload = getPayload(getV3Report(DEFAULT_FEED_1_V3)); + + //only the proxy or admin can call processFee, they will pass in the native value on the users behalf + processFee(DEFAULT_CONFIG_DIGEST, payload, USER, address(native), DEFAULT_REPORT_NATIVE_FEE); + + //check the native has been transferred and converted to wrapped native + assertEq(getNativeBalance(address(feeManager)), DEFAULT_REPORT_NATIVE_FEE); + assertEq(getNativeUnwrappedBalance(address(feeManager)), 0); + + //check the link has been transferred to the rewardManager + assertEq(getLinkBalance(address(rewardManager)), DEFAULT_REPORT_LINK_FEE); + + //check the feeManager has had the link deducted, the remaining balance should be 0 + assertEq(getLinkBalance(address(feeManager)), 0); + + //check the subscriber has had the native deducted + assertEq(getNativeUnwrappedBalance(USER), DEFAULT_NATIVE_MINT_QUANTITY - DEFAULT_REPORT_NATIVE_FEE); + } + + function test_processFeeWithUnwrappedNativeShortFunds() public { + //simulate a deposit of link for the conversion pool + mintLink(address(feeManager), DEFAULT_REPORT_LINK_FEE); + + //get the default payload + bytes memory payload = getPayload(getV3Report(DEFAULT_FEED_1_V3)); + + //expect a revert as not enough funds + vm.expectRevert(INVALID_DEPOSIT_ERROR); + + //only the proxy or admin can call processFee, they will pass in the native value on the users behalf + processFee(DEFAULT_CONFIG_DIGEST, payload, USER, address(native), DEFAULT_REPORT_NATIVE_FEE - 1); + } + + function test_processFeeWithUnwrappedNativeLinkAddress() public { + //simulate a deposit of link for the conversion pool + mintLink(address(feeManager), DEFAULT_REPORT_LINK_FEE); + + //get the default payload + bytes memory payload = getPayload(getV3Report(DEFAULT_FEED_1_V3)); + + //expect a revert as not enough funds + vm.expectRevert(INSUFFICIENT_ALLOWANCE_ERROR); + + //the change will be returned and the user will attempted to be billed in LINK + processFee(DEFAULT_CONFIG_DIGEST, payload, USER, address(link), DEFAULT_REPORT_NATIVE_FEE - 1); + } + + function test_processFeeWithUnwrappedNativeLinkAddressExcessiveFee() public { + //approve the link to be transferred from the from the subscriber to the rewardManager + approveLink(address(rewardManager), DEFAULT_REPORT_LINK_FEE, PROXY); + + //get the default payload + bytes memory payload = getPayload(getV3Report(DEFAULT_FEED_1_V3)); + + //call processFee from the proxy to test whether the funds are returned to the subscriber. In reality, the funds would be returned to the caller of the proxy. + processFee(DEFAULT_CONFIG_DIGEST, payload, PROXY, address(link), DEFAULT_REPORT_NATIVE_FEE); + + //check the native unwrapped is no longer in the account + assertEq(getNativeBalance(address(feeManager)), 0); + assertEq(getNativeUnwrappedBalance(address(feeManager)), 0); + + //check the link has been transferred to the rewardManager + assertEq(getLinkBalance(address(rewardManager)), DEFAULT_REPORT_LINK_FEE); + + //check the feeManager has had the link deducted, the remaining balance should be 0 + assertEq(getLinkBalance(address(feeManager)), 0); + + //native should not be deducted + assertEq(getNativeUnwrappedBalance(PROXY), DEFAULT_NATIVE_MINT_QUANTITY); + } + + function test_processFeeWithUnwrappedNativeWithExcessiveFee() public { + //simulate a deposit of link for the conversion pool + mintLink(address(feeManager), DEFAULT_REPORT_LINK_FEE); + + //get the default payload + bytes memory payload = getPayload(getV3Report(DEFAULT_FEED_1_V3)); + + //call processFee from the proxy to test whether the funds are returned to the subscriber. In reality, the funds would be returned to the caller of the proxy. + processFee(DEFAULT_CONFIG_DIGEST, payload, PROXY, address(native), DEFAULT_REPORT_NATIVE_FEE * 2); + + //check the native has been transferred and converted to wrapped native + assertEq(getNativeBalance(address(feeManager)), DEFAULT_REPORT_NATIVE_FEE); + assertEq(getNativeUnwrappedBalance(address(feeManager)), 0); + + //check the link has been transferred to the rewardManager + assertEq(getLinkBalance(address(rewardManager)), DEFAULT_REPORT_LINK_FEE); + + //check the feeManager has had the link deducted, the remaining balance should be 0 + assertEq(getLinkBalance(address(feeManager)), 0); + + //check the subscriber has had the native deducted + assertEq(getNativeUnwrappedBalance(PROXY), DEFAULT_NATIVE_MINT_QUANTITY - DEFAULT_REPORT_NATIVE_FEE); + } + + function test_processFeeUsesCorrectDigest() public { + //get the default payload + bytes memory payload = getPayload(getV3Report(DEFAULT_FEED_1_V3)); + + //approve the link to be transferred from the from the subscriber to the rewardManager + approveLink(address(rewardManager), DEFAULT_REPORT_LINK_FEE, USER); + + //processing the fee will transfer the link from the user to the rewardManager + processFee(DEFAULT_CONFIG_DIGEST, payload, USER, address(link), 0); + + //check the link has been transferred + assertEq(getLinkBalance(address(rewardManager)), DEFAULT_REPORT_LINK_FEE); + + //check the user has had the link fee deducted + assertEq(getLinkBalance(USER), DEFAULT_LINK_MINT_QUANTITY - DEFAULT_REPORT_LINK_FEE); + + //check funds have been paid to the reward manager + assertEq(rewardManager.s_totalRewardRecipientFees(DEFAULT_CONFIG_DIGEST), DEFAULT_REPORT_LINK_FEE); + } + + function test_V1PayloadVerifies() public { + //replicate a default payload + bytes memory payload = abi.encode( + [DEFAULT_CONFIG_DIGEST, 0, 0], + getV2Report(DEFAULT_FEED_1_V1), + new bytes32[](1), + new bytes32[](1), + bytes32("") + ); + + //processing the fee will transfer the link from the user to the rewardManager + processFee(DEFAULT_CONFIG_DIGEST, payload, USER, address(0), 0); + } + + function test_V2PayloadVerifies() public { + //get the default payload + bytes memory payload = getPayload(getV2Report(DEFAULT_FEED_1_V2)); + + //approve the link to be transferred from the from the subscriber to the rewardManager + approveLink(address(rewardManager), DEFAULT_REPORT_LINK_FEE, USER); + + //processing the fee will transfer the link from the user to the rewardManager + processFee(DEFAULT_CONFIG_DIGEST, payload, USER, address(link), 0); + + //check the link has been transferred + assertEq(getLinkBalance(address(rewardManager)), DEFAULT_REPORT_LINK_FEE); + + //check the user has had the link fee deducted + assertEq(getLinkBalance(USER), DEFAULT_LINK_MINT_QUANTITY - DEFAULT_REPORT_LINK_FEE); + } + + function test_V2PayloadWithoutQuoteFails() public { + //get the default payload + bytes memory payload = getPayload(getV2Report(DEFAULT_FEED_1_V2)); + + //expect a revert as the quote is invalid + vm.expectRevert(); + + //processing the fee will transfer the link from the user to the rewardManager + processFee(DEFAULT_CONFIG_DIGEST, payload, USER, address(0), 0); + } + + function test_V2PayloadWithoutZeroFee() public { + //get the default payload + bytes memory payload = getPayload(getV2Report(DEFAULT_FEED_1_V2)); + + //expect a revert as the quote is invalid + vm.expectRevert(); + + //processing the fee will transfer the link from the user to the rewardManager + processFee(DEFAULT_CONFIG_DIGEST, payload, USER, address(link), 0); + } + + function test_processFeeWithInvalidReportVersionFailsToDecode() public { + bytes memory data = abi.encode(0x0000100000000000000000000000000000000000000000000000000000000000); + + //get the default payload + bytes memory payload = getPayload(data); + + //serialization will fail as there is no report to decode + vm.expectRevert(); + + //processing the fee will not withdraw anything as there is no fee to collect + processFee(DEFAULT_CONFIG_DIGEST, payload, USER, address(link), 0); + } + + function test_processFeeWithZeroNativeNonZeroLinkWithNativeQuote() public { + //get the default payload + bytes memory payload = getPayload( + getV3ReportWithCustomExpiryAndFee(DEFAULT_FEED_1_V3, block.timestamp, DEFAULT_REPORT_LINK_FEE, 0) + ); + + //call processFee should not revert as the fee is 0 + processFee(DEFAULT_CONFIG_DIGEST, payload, PROXY, address(native), 0); + } + + function test_processFeeWithZeroNativeNonZeroLinkWithLinkQuote() public { + //get the default payload + bytes memory payload = getPayload( + getV3ReportWithCustomExpiryAndFee(DEFAULT_FEED_1_V3, block.timestamp, DEFAULT_REPORT_LINK_FEE, 0) + ); + + //approve the link to be transferred from the from the subscriber to the rewardManager + approveLink(address(rewardManager), DEFAULT_REPORT_LINK_FEE, USER); + + //processing the fee will transfer the link to the rewardManager from the user + processFee(DEFAULT_CONFIG_DIGEST, payload, USER, address(link), 0); + + //check the link has been transferred + assertEq(getLinkBalance(address(rewardManager)), DEFAULT_REPORT_LINK_FEE); + + //check the user has had the link fee deducted + assertEq(getLinkBalance(USER), DEFAULT_LINK_MINT_QUANTITY - DEFAULT_REPORT_LINK_FEE); + } + + function test_processFeeWithZeroLinkNonZeroNativeWithNativeQuote() public { + //simulate a deposit of link for the conversion pool + mintLink(address(feeManager), DEFAULT_REPORT_LINK_FEE); + + //get the default payload + bytes memory payload = getPayload( + getV3ReportWithCustomExpiryAndFee(DEFAULT_FEED_1_V3, block.timestamp, 0, DEFAULT_REPORT_NATIVE_FEE) + ); + + //approve the native to be transferred from the user + approveNative(address(feeManager), DEFAULT_REPORT_NATIVE_FEE, USER); + + //processing the fee will transfer the native from the user to the feeManager + processFee(DEFAULT_CONFIG_DIGEST, payload, USER, address(native), 0); + + //check the native has been transferred + assertEq(getNativeBalance(address(feeManager)), DEFAULT_REPORT_NATIVE_FEE); + + //check no link has been transferred to the rewardManager + assertEq(getLinkBalance(address(rewardManager)), 0); + + //check the feeManager has had no link deducted + assertEq(getLinkBalance(address(feeManager)), DEFAULT_REPORT_LINK_FEE); + + //check the subscriber has had the native deducted + assertEq(getNativeBalance(USER), DEFAULT_NATIVE_MINT_QUANTITY - DEFAULT_REPORT_NATIVE_FEE); + } + + function test_processFeeWithZeroLinkNonZeroNativeWithLinkQuote() public { + //get the default payload + bytes memory payload = getPayload( + getV3ReportWithCustomExpiryAndFee(DEFAULT_FEED_1_V3, block.timestamp, 0, DEFAULT_REPORT_NATIVE_FEE) + ); + + //call processFee should not revert as the fee is 0 + processFee(DEFAULT_CONFIG_DIGEST, payload, USER, address(link), 0); + } + + function test_processFeeWithZeroNativeNonZeroLinkReturnsChange() public { + //get the default payload + bytes memory payload = getPayload( + getV3ReportWithCustomExpiryAndFee(DEFAULT_FEED_1_V3, block.timestamp, 0, DEFAULT_REPORT_NATIVE_FEE) + ); + + //call processFee should not revert as the fee is 0 + processFee(DEFAULT_CONFIG_DIGEST, payload, USER, address(link), DEFAULT_REPORT_NATIVE_FEE); + + //check the change has been returned + assertEq(USER.balance, DEFAULT_NATIVE_MINT_QUANTITY); + } + + function test_V1PayloadVerifiesAndReturnsChange() public { + //emulate a V1 payload with no quote + bytes memory payload = getPayload(getV1Report(DEFAULT_FEED_1_V1)); + + processFee(DEFAULT_CONFIG_DIGEST, payload, USER, address(0), DEFAULT_REPORT_NATIVE_FEE); + + //Fee manager should not contain any native + assertEq(address(feeManager).balance, 0); + assertEq(getNativeBalance(address(feeManager)), 0); + + //check the unused native passed in is returned + assertEq(USER.balance, DEFAULT_NATIVE_MINT_QUANTITY); + } + + function test_processFeeWithDiscountEmitsEvent() public { + //simulate a deposit of link for the conversion pool + mintLink(address(feeManager), DEFAULT_REPORT_LINK_FEE); + + //set the subscriber discount to 50% + setSubscriberDiscount(USER, DEFAULT_FEED_1_V3, address(native), FEE_SCALAR / 2, ADMIN); + + //approve the native to be transferred from the user + approveNative(address(feeManager), DEFAULT_REPORT_NATIVE_FEE / 2, USER); + + //get the default payload + bytes memory payload = getPayload(getV3Report(DEFAULT_FEED_1_V3)); + + Common.Asset memory fee = getFee(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + Common.Asset memory reward = getReward(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + uint256 appliedDiscount = getAppliedDiscount(getV3Report(DEFAULT_FEED_1_V3), getNativeQuote(), USER); + + vm.expectEmit(); + + emit DiscountApplied(DEFAULT_CONFIG_DIGEST, USER, fee, reward, appliedDiscount); + + //call processFee should not revert as the fee is 0 + processFee(DEFAULT_CONFIG_DIGEST, payload, USER, address(native), 0); + } + + function test_processFeeWithNoDiscountDoesNotEmitEvent() public { + //simulate a deposit of link for the conversion pool + mintLink(address(feeManager), DEFAULT_REPORT_LINK_FEE); + + //approve the native to be transferred from the user + approveNative(address(feeManager), DEFAULT_REPORT_NATIVE_FEE, USER); + + //get the default payload + bytes memory payload = getPayload(getV3Report(DEFAULT_FEED_1_V3)); + + //call processFee should not revert as the fee is 0 + processFee(DEFAULT_CONFIG_DIGEST, payload, USER, address(native), 0); + + //no logs should have been emitted + assertEq(vm.getRecordedLogs().length, 0); + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/test/fee-manager/DestinationFeeManager.processFeeBulk.t.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/test/fee-manager/DestinationFeeManager.processFeeBulk.t.sol new file mode 100644 index 0000000000..a50441bed6 --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/test/fee-manager/DestinationFeeManager.processFeeBulk.t.sol @@ -0,0 +1,310 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import "./BaseDestinationFeeManager.t.sol"; +import {IDestinationRewardManager} from "../../interfaces/IDestinationRewardManager.sol"; + +/** + * @title BaseFeeManagerTest + * @author Michael Fletcher + * @notice This contract will test the functionality of the feeManager processFee + */ +contract DestinationFeeManagerProcessFeeTest is BaseDestinationFeeManagerTest { + uint256 internal constant NUMBER_OF_REPORTS = 5; + + function setUp() public override { + super.setUp(); + } + + function test_processMultipleLinkReports() public { + bytes memory payload = getPayload(getV3Report(DEFAULT_FEED_1_V3)); + + bytes[] memory payloads = new bytes[](NUMBER_OF_REPORTS); + for (uint256 i = 0; i < NUMBER_OF_REPORTS; ++i) { + payloads[i] = payload; + } + + bytes32[] memory poolIds = new bytes32[](NUMBER_OF_REPORTS); + for (uint256 i = 0; i < NUMBER_OF_REPORTS; ++i) { + poolIds[i] = DEFAULT_CONFIG_DIGEST; + } + + approveLink(address(rewardManager), DEFAULT_REPORT_LINK_FEE * NUMBER_OF_REPORTS, USER); + + processFee(poolIds, payloads, USER, address(link), DEFAULT_NATIVE_MINT_QUANTITY); + + assertEq(getLinkBalance(address(rewardManager)), DEFAULT_REPORT_LINK_FEE * NUMBER_OF_REPORTS); + assertEq(getLinkBalance(address(feeManager)), 0); + assertEq(getLinkBalance(USER), DEFAULT_LINK_MINT_QUANTITY - DEFAULT_REPORT_LINK_FEE * NUMBER_OF_REPORTS); + + //the subscriber (user) should receive funds back and not the proxy, although when live the proxy will forward the funds sent and not cover it seen here + assertEq(USER.balance, DEFAULT_NATIVE_MINT_QUANTITY); + assertEq(PROXY.balance, DEFAULT_NATIVE_MINT_QUANTITY); + } + + function test_processMultipleWrappedNativeReports() public { + mintLink(address(feeManager), DEFAULT_REPORT_LINK_FEE * NUMBER_OF_REPORTS + 1); + + bytes memory payload = getPayload(getV3Report(DEFAULT_FEED_1_V3)); + + bytes[] memory payloads = new bytes[](NUMBER_OF_REPORTS); + for (uint256 i; i < NUMBER_OF_REPORTS; ++i) { + payloads[i] = payload; + } + + bytes32[] memory poolIds = new bytes32[](NUMBER_OF_REPORTS); + for (uint256 i = 0; i < NUMBER_OF_REPORTS; ++i) { + poolIds[i] = DEFAULT_CONFIG_DIGEST; + } + + approveNative(address(feeManager), DEFAULT_REPORT_NATIVE_FEE * NUMBER_OF_REPORTS, USER); + + processFee(poolIds, payloads, USER, address(native), 0); + + assertEq(getNativeBalance(address(feeManager)), DEFAULT_REPORT_NATIVE_FEE * NUMBER_OF_REPORTS); + assertEq(getLinkBalance(address(rewardManager)), DEFAULT_REPORT_LINK_FEE * NUMBER_OF_REPORTS); + assertEq(getLinkBalance(address(feeManager)), 1); + assertEq(getNativeBalance(USER), DEFAULT_NATIVE_MINT_QUANTITY - DEFAULT_REPORT_NATIVE_FEE * NUMBER_OF_REPORTS); + } + + function test_processMultipleUnwrappedNativeReports() public { + mintLink(address(feeManager), DEFAULT_REPORT_LINK_FEE * NUMBER_OF_REPORTS + 1); + + bytes memory payload = getPayload(getV3Report(DEFAULT_FEED_1_V3)); + + bytes[] memory payloads = new bytes[](NUMBER_OF_REPORTS); + for (uint256 i; i < NUMBER_OF_REPORTS; ++i) { + payloads[i] = payload; + } + + bytes32[] memory poolIds = new bytes32[](NUMBER_OF_REPORTS); + for (uint256 i = 0; i < NUMBER_OF_REPORTS; ++i) { + poolIds[i] = DEFAULT_CONFIG_DIGEST; + } + + processFee(poolIds, payloads, USER, address(native), DEFAULT_REPORT_NATIVE_FEE * NUMBER_OF_REPORTS * 2); + + assertEq(getNativeBalance(address(feeManager)), DEFAULT_REPORT_NATIVE_FEE * NUMBER_OF_REPORTS); + assertEq(getLinkBalance(address(rewardManager)), DEFAULT_REPORT_LINK_FEE * NUMBER_OF_REPORTS); + assertEq(getLinkBalance(address(feeManager)), 1); + + assertEq(PROXY.balance, DEFAULT_NATIVE_MINT_QUANTITY); + assertEq(USER.balance, DEFAULT_NATIVE_MINT_QUANTITY - DEFAULT_REPORT_NATIVE_FEE * NUMBER_OF_REPORTS); + } + + function test_processV1V2V3Reports() public { + mintLink(address(feeManager), 1); + + bytes memory payloadV1 = abi.encode( + [DEFAULT_CONFIG_DIGEST, 0, 0], + getV1Report(DEFAULT_FEED_1_V1), + new bytes32[](1), + new bytes32[](1), + bytes32("") + ); + + bytes memory linkPayloadV2 = getPayload(getV2Report(DEFAULT_FEED_1_V2)); + bytes memory linkPayloadV3 = getPayload(getV3Report(DEFAULT_FEED_1_V3)); + + bytes[] memory payloads = new bytes[](5); + payloads[0] = payloadV1; + payloads[1] = linkPayloadV2; + payloads[2] = linkPayloadV2; + payloads[3] = linkPayloadV3; + payloads[4] = linkPayloadV3; + + approveLink(address(rewardManager), DEFAULT_REPORT_LINK_FEE * 4, USER); + + bytes32[] memory poolIds = new bytes32[](5); + for (uint256 i = 0; i < 5; ++i) { + poolIds[i] = DEFAULT_CONFIG_DIGEST; + } + + processFee(poolIds, payloads, USER, address(link), 0); + + assertEq(getNativeBalance(address(feeManager)), 0); + assertEq(getLinkBalance(address(rewardManager)), DEFAULT_REPORT_LINK_FEE * 4); + assertEq(getLinkBalance(address(feeManager)), 1); + + assertEq(getLinkBalance(USER), DEFAULT_LINK_MINT_QUANTITY - DEFAULT_REPORT_LINK_FEE * 4); + assertEq(getNativeBalance(USER), DEFAULT_NATIVE_MINT_QUANTITY - 0); + } + + function test_processV1V2V3ReportsWithUnwrapped() public { + mintLink(address(feeManager), DEFAULT_REPORT_LINK_FEE * 4 + 1); + + bytes memory payloadV1 = abi.encode( + [DEFAULT_CONFIG_DIGEST, 0, 0], + getV1Report(DEFAULT_FEED_1_V1), + new bytes32[](1), + new bytes32[](1), + bytes32("") + ); + + bytes memory nativePayloadV2 = getPayload(getV2Report(DEFAULT_FEED_1_V2)); + bytes memory nativePayloadV3 = getPayload(getV3Report(DEFAULT_FEED_1_V3)); + + bytes[] memory payloads = new bytes[](5); + payloads[0] = payloadV1; + payloads[1] = nativePayloadV2; + payloads[2] = nativePayloadV2; + payloads[3] = nativePayloadV3; + payloads[4] = nativePayloadV3; + + bytes32[] memory poolIds = new bytes32[](5); + for (uint256 i = 0; i < 5; ++i) { + poolIds[i] = DEFAULT_CONFIG_DIGEST; + } + + processFee(poolIds, payloads, USER, address(native), DEFAULT_REPORT_NATIVE_FEE * 4); + + assertEq(getNativeBalance(address(feeManager)), DEFAULT_REPORT_NATIVE_FEE * 4); + assertEq(getLinkBalance(address(rewardManager)), DEFAULT_REPORT_LINK_FEE * 4); + assertEq(getLinkBalance(address(feeManager)), 1); + + assertEq(USER.balance, DEFAULT_NATIVE_MINT_QUANTITY - DEFAULT_REPORT_NATIVE_FEE * 4); + assertEq(PROXY.balance, DEFAULT_NATIVE_MINT_QUANTITY); + } + + function test_processMultipleV1Reports() public { + bytes memory payload = abi.encode( + [DEFAULT_CONFIG_DIGEST, 0, 0], + getV1Report(DEFAULT_FEED_1_V1), + new bytes32[](1), + new bytes32[](1), + bytes32("") + ); + + bytes[] memory payloads = new bytes[](NUMBER_OF_REPORTS); + for (uint256 i = 0; i < NUMBER_OF_REPORTS; ++i) { + payloads[i] = payload; + } + + bytes32[] memory poolIds = new bytes32[](NUMBER_OF_REPORTS); + for (uint256 i = 0; i < NUMBER_OF_REPORTS; ++i) { + poolIds[i] = DEFAULT_CONFIG_DIGEST; + } + + processFee(poolIds, payloads, USER, address(native), DEFAULT_REPORT_NATIVE_FEE * 5); + + assertEq(USER.balance, DEFAULT_NATIVE_MINT_QUANTITY); + assertEq(PROXY.balance, DEFAULT_NATIVE_MINT_QUANTITY); + } + + function test_eventIsEmittedIfNotEnoughLink() public { + bytes memory nativePayload = getPayload(getV3Report(DEFAULT_FEED_1_V3)); + + bytes[] memory payloads = new bytes[](5); + payloads[0] = nativePayload; + payloads[1] = nativePayload; + payloads[2] = nativePayload; + payloads[3] = nativePayload; + payloads[4] = nativePayload; + + approveNative(address(feeManager), DEFAULT_REPORT_NATIVE_FEE * 5, USER); + + IDestinationRewardManager.FeePayment[] memory payments = new IDestinationRewardManager.FeePayment[](5); + payments[0] = IDestinationRewardManager.FeePayment(DEFAULT_CONFIG_DIGEST, uint192(DEFAULT_REPORT_LINK_FEE)); + payments[1] = IDestinationRewardManager.FeePayment(DEFAULT_CONFIG_DIGEST, uint192(DEFAULT_REPORT_LINK_FEE)); + payments[2] = IDestinationRewardManager.FeePayment(DEFAULT_CONFIG_DIGEST, uint192(DEFAULT_REPORT_LINK_FEE)); + payments[3] = IDestinationRewardManager.FeePayment(DEFAULT_CONFIG_DIGEST, uint192(DEFAULT_REPORT_LINK_FEE)); + payments[4] = IDestinationRewardManager.FeePayment(DEFAULT_CONFIG_DIGEST, uint192(DEFAULT_REPORT_LINK_FEE)); + + vm.expectEmit(); + + bytes32[] memory poolIds = new bytes32[](5); + for (uint256 i = 0; i < 5; ++i) { + poolIds[i] = payments[i].poolId; + } + + emit InsufficientLink(payments); + + processFee(poolIds, payloads, USER, address(native), 0); + + assertEq(getNativeBalance(address(feeManager)), DEFAULT_REPORT_NATIVE_FEE * 5); + assertEq(getNativeBalance(USER), DEFAULT_NATIVE_MINT_QUANTITY - DEFAULT_REPORT_NATIVE_FEE * 5); + assertEq(getLinkBalance(USER), DEFAULT_LINK_MINT_QUANTITY); + } + + function test_processPoolIdsPassedMismatched() public { + mintLink(address(feeManager), DEFAULT_REPORT_LINK_FEE * NUMBER_OF_REPORTS + 1); + + bytes memory payload = getPayload(getV3Report(DEFAULT_FEED_1_V3)); + + bytes[] memory payloads = new bytes[](NUMBER_OF_REPORTS); + for (uint256 i; i < NUMBER_OF_REPORTS; ++i) { + payloads[i] = payload; + } + + // poolIds passed are different that number of reports in payload + bytes32[] memory poolIds = new bytes32[](NUMBER_OF_REPORTS - 1); + for (uint256 i = 0; i < NUMBER_OF_REPORTS - 1; ++i) { + poolIds[i] = DEFAULT_CONFIG_DIGEST; + } + + vm.expectRevert(POOLID_MISMATCH_ERROR); + processFee(poolIds, payloads, USER, address(native), DEFAULT_REPORT_NATIVE_FEE * NUMBER_OF_REPORTS * 2); + } + + function test_poolIdsCannotBeZeroAddress() public { + mintLink(address(feeManager), DEFAULT_REPORT_LINK_FEE * NUMBER_OF_REPORTS + 1); + + bytes memory payload = getPayload(getV3Report(DEFAULT_FEED_1_V3)); + + bytes[] memory payloads = new bytes[](NUMBER_OF_REPORTS); + for (uint256 i; i < NUMBER_OF_REPORTS; ++i) { + payloads[i] = payload; + } + + bytes32[] memory poolIds = new bytes32[](NUMBER_OF_REPORTS); + for (uint256 i = 0; i < NUMBER_OF_REPORTS; ++i) { + poolIds[i] = DEFAULT_CONFIG_DIGEST; + } + + poolIds[2] = 0x000; + vm.expectRevert(INVALID_ADDRESS_ERROR); + processFee(poolIds, payloads, USER, address(native), DEFAULT_REPORT_NATIVE_FEE * NUMBER_OF_REPORTS * 2); + } + + function test_rewardsAreCorrectlySentToEachAssociatedPoolWhenVerifyingInBulk() public { + bytes memory payload = getPayload(getV3Report(DEFAULT_FEED_1_V3)); + + bytes[] memory payloads = new bytes[](NUMBER_OF_REPORTS); + for (uint256 i = 0; i < NUMBER_OF_REPORTS; ++i) { + payloads[i] = payload; + } + + bytes32[] memory poolIds = new bytes32[](NUMBER_OF_REPORTS); + for (uint256 i = 0; i < NUMBER_OF_REPORTS - 1; ++i) { + poolIds[i] = DEFAULT_CONFIG_DIGEST; + } + poolIds[NUMBER_OF_REPORTS - 1] = DEFAULT_CONFIG_DIGEST2; + + approveLink(address(rewardManager), DEFAULT_REPORT_LINK_FEE * NUMBER_OF_REPORTS, USER); + + // Checking no rewards yet for each pool + for (uint256 i = 0; i < NUMBER_OF_REPORTS; ++i) { + bytes32 p_id = poolIds[i]; + uint256 poolDeficit = rewardManager.s_totalRewardRecipientFees(p_id); + assertEq(poolDeficit, 0); + } + + processFee(poolIds, payloads, USER, address(link), DEFAULT_NATIVE_MINT_QUANTITY); + + assertEq(getLinkBalance(address(rewardManager)), DEFAULT_REPORT_LINK_FEE * NUMBER_OF_REPORTS); + assertEq(getLinkBalance(address(feeManager)), 0); + assertEq(getLinkBalance(USER), DEFAULT_LINK_MINT_QUANTITY - DEFAULT_REPORT_LINK_FEE * NUMBER_OF_REPORTS); + + assertEq(USER.balance, DEFAULT_NATIVE_MINT_QUANTITY); + assertEq(PROXY.balance, DEFAULT_NATIVE_MINT_QUANTITY); + + // Checking each pool got the correct rewards + uint256 expectedRewards = DEFAULT_REPORT_LINK_FEE * (NUMBER_OF_REPORTS - 1); + uint256 poolRewards = rewardManager.s_totalRewardRecipientFees(DEFAULT_CONFIG_DIGEST); + assertEq(poolRewards, expectedRewards); + + expectedRewards = DEFAULT_REPORT_LINK_FEE; + poolRewards = rewardManager.s_totalRewardRecipientFees(DEFAULT_CONFIG_DIGEST2); + assertEq(poolRewards, expectedRewards); + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/test/mocks/DestinationFeeManagerProxy.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/test/mocks/DestinationFeeManagerProxy.sol new file mode 100644 index 0000000000..46ec7fff3b --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/test/mocks/DestinationFeeManagerProxy.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {IDestinationFeeManager} from "../../interfaces/IDestinationFeeManager.sol"; + +contract DestinationFeeManagerProxy { + IDestinationFeeManager internal s_feeManager; + + function processFee(bytes32 poolId, bytes calldata payload, bytes calldata parameterPayload) public payable { + s_feeManager.processFee{value: msg.value}(poolId, payload, parameterPayload, msg.sender); + } + + function processFeeBulk( + bytes32[] memory poolIds, + bytes[] calldata payloads, + bytes calldata parameterPayload + ) public payable { + s_feeManager.processFeeBulk{value: msg.value}(poolIds, payloads, parameterPayload, msg.sender); + } + + function setDestinationFeeManager(IDestinationFeeManager feeManager) public { + s_feeManager = feeManager; + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/test/reward-manager/BaseDestinationRewardManager.t.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/test/reward-manager/BaseDestinationRewardManager.t.sol new file mode 100644 index 0000000000..7cafb1629d --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/test/reward-manager/BaseDestinationRewardManager.t.sol @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {Test} from "forge-std/Test.sol"; +import {ERC20Mock} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/mocks/ERC20Mock.sol"; +import {DestinationRewardManager} from "../../../v0.4.0/DestinationRewardManager.sol"; +import {Common} from "../../../libraries/Common.sol"; +import {IDestinationRewardManager} from "../../interfaces/IDestinationRewardManager.sol"; + +/** + * @title DestinationRewardManagerTest + * @author Michael Fletcher + * @notice Base class for all reward manager tests + * @dev This contract is intended to be inherited from and not used directly. It contains functionality to setup a primary and secondary pool + */ +contract BaseDestinationRewardManagerTest is Test { + //contracts + ERC20Mock internal asset; + ERC20Mock internal unsupported; + DestinationRewardManager internal rewardManager; + + //default address for unregistered recipient + address internal constant INVALID_ADDRESS = address(0); + //contract owner + address internal constant ADMIN = address(uint160(uint256(keccak256("ADMIN")))); + //address to represent feeManager contract + address internal constant FEE_MANAGER = address(uint160(uint256(keccak256("FEE_MANAGER")))); + //address to represent another feeManager + address internal constant FEE_MANAGER_2 = address(uint160(uint256(keccak256("FEE_MANAGER_2")))); + //a general user + address internal constant USER = address(uint160(uint256(keccak256("USER")))); + + //default recipients configured in reward manager + address internal constant DEFAULT_RECIPIENT_1 = address(uint160(uint256(keccak256("DEFAULT_RECIPIENT_1")))); + address internal constant DEFAULT_RECIPIENT_2 = address(uint160(uint256(keccak256("DEFAULT_RECIPIENT_2")))); + address internal constant DEFAULT_RECIPIENT_3 = address(uint160(uint256(keccak256("DEFAULT_RECIPIENT_3")))); + address internal constant DEFAULT_RECIPIENT_4 = address(uint160(uint256(keccak256("DEFAULT_RECIPIENT_4")))); + address internal constant DEFAULT_RECIPIENT_5 = address(uint160(uint256(keccak256("DEFAULT_RECIPIENT_5")))); + address internal constant DEFAULT_RECIPIENT_6 = address(uint160(uint256(keccak256("DEFAULT_RECIPIENT_6")))); + address internal constant DEFAULT_RECIPIENT_7 = address(uint160(uint256(keccak256("DEFAULT_RECIPIENT_7")))); + + //additional recipients not in the reward manager + address internal constant DEFAULT_RECIPIENT_8 = address(uint160(uint256(keccak256("DEFAULT_RECIPIENT_8")))); + address internal constant DEFAULT_RECIPIENT_9 = address(uint160(uint256(keccak256("DEFAULT_RECIPIENT_9")))); + + //two pools should be enough to test all edge cases + bytes32 internal constant PRIMARY_POOL_ID = keccak256("primary_pool"); + bytes32 internal constant SECONDARY_POOL_ID = keccak256("secondary_pool"); + bytes32 internal constant INVALID_POOL_ID = keccak256("invalid_pool"); + bytes32 internal constant ZERO_POOL_ID = bytes32(0); + + //convenience arrays of all pool combinations used for testing + bytes32[] internal PRIMARY_POOL_ARRAY = [PRIMARY_POOL_ID]; + bytes32[] internal SECONDARY_POOL_ARRAY = [SECONDARY_POOL_ID]; + bytes32[] internal ALL_POOLS = [PRIMARY_POOL_ID, SECONDARY_POOL_ID]; + + //erc20 config + uint256 internal constant DEFAULT_MINT_QUANTITY = 100 ether; + + //reward scalar (this should match the const in the contract) + uint64 internal constant POOL_SCALAR = 1e18; + uint64 internal constant ONE_PERCENT = POOL_SCALAR / 100; + uint64 internal constant FIFTY_PERCENT = POOL_SCALAR / 2; + uint64 internal constant TEN_PERCENT = POOL_SCALAR / 10; + + //the selector for each error + bytes4 internal immutable UNAUTHORIZED_ERROR_SELECTOR = DestinationRewardManager.Unauthorized.selector; + bytes4 internal immutable INVALID_ADDRESS_ERROR_SELECTOR = DestinationRewardManager.InvalidAddress.selector; + bytes4 internal immutable INVALID_WEIGHT_ERROR_SELECTOR = DestinationRewardManager.InvalidWeights.selector; + bytes4 internal immutable INVALID_POOL_ID_ERROR_SELECTOR = DestinationRewardManager.InvalidPoolId.selector; + bytes internal constant ONLY_CALLABLE_BY_OWNER_ERROR = "Only callable by owner"; + bytes4 internal immutable INVALID_POOL_LENGTH_SELECTOR = DestinationRewardManager.InvalidPoolLength.selector; + + // Events emitted within the reward manager + event RewardRecipientsUpdated(bytes32 indexed poolId, Common.AddressAndWeight[] newRewardRecipients); + event RewardsClaimed(bytes32 indexed poolId, address indexed recipient, uint192 quantity); + event FeeManagerUpdated(address newProxyAddress); + event FeePaid(IDestinationRewardManager.FeePayment[] payments, address payee); + + function setUp() public virtual { + //change to admin user + vm.startPrank(ADMIN); + + //init required contracts + _initializeERC20Contracts(); + _initializeRewardManager(); + } + + function _initializeERC20Contracts() internal { + //create the contracts + asset = new ERC20Mock("ASSET", "AST", ADMIN, 0); + unsupported = new ERC20Mock("UNSUPPORTED", "UNS", ADMIN, 0); + + //mint some tokens to the admin + asset.mint(ADMIN, DEFAULT_MINT_QUANTITY); + unsupported.mint(ADMIN, DEFAULT_MINT_QUANTITY); + + //mint some tokens to the user + asset.mint(FEE_MANAGER, DEFAULT_MINT_QUANTITY); + unsupported.mint(FEE_MANAGER, DEFAULT_MINT_QUANTITY); + } + + function _initializeRewardManager() internal { + //create the contract + rewardManager = new DestinationRewardManager(address(asset)); + + rewardManager.addFeeManager(FEE_MANAGER); + } + + function createPrimaryPool() public { + rewardManager.setRewardRecipients(PRIMARY_POOL_ID, getPrimaryRecipients()); + } + + function createSecondaryPool() public { + rewardManager.setRewardRecipients(SECONDARY_POOL_ID, getSecondaryRecipients()); + } + + //override this to test variations of different recipients. changing this function will require existing tests to be updated as constants are hardcoded to be explicit + function getPrimaryRecipients() public virtual returns (Common.AddressAndWeight[] memory) { + //array of recipients + Common.AddressAndWeight[] memory recipients = new Common.AddressAndWeight[](4); + + //init each recipient with even weights. 2500 = 25% of pool + recipients[0] = Common.AddressAndWeight(DEFAULT_RECIPIENT_1, POOL_SCALAR / 4); + recipients[1] = Common.AddressAndWeight(DEFAULT_RECIPIENT_2, POOL_SCALAR / 4); + recipients[2] = Common.AddressAndWeight(DEFAULT_RECIPIENT_3, POOL_SCALAR / 4); + recipients[3] = Common.AddressAndWeight(DEFAULT_RECIPIENT_4, POOL_SCALAR / 4); + + return recipients; + } + + function getPrimaryRecipientAddresses() public pure returns (address[] memory) { + //array of recipients + address[] memory recipients = new address[](4); + + recipients[0] = DEFAULT_RECIPIENT_1; + recipients[1] = DEFAULT_RECIPIENT_2; + recipients[2] = DEFAULT_RECIPIENT_3; + recipients[3] = DEFAULT_RECIPIENT_4; + + return recipients; + } + + //override this to test variations of different recipients. + function getSecondaryRecipients() public virtual returns (Common.AddressAndWeight[] memory) { + //array of recipients + Common.AddressAndWeight[] memory recipients = new Common.AddressAndWeight[](4); + + //init each recipient with even weights. 2500 = 25% of pool + recipients[0] = Common.AddressAndWeight(DEFAULT_RECIPIENT_1, POOL_SCALAR / 4); + recipients[1] = Common.AddressAndWeight(DEFAULT_RECIPIENT_5, POOL_SCALAR / 4); + recipients[2] = Common.AddressAndWeight(DEFAULT_RECIPIENT_6, POOL_SCALAR / 4); + recipients[3] = Common.AddressAndWeight(DEFAULT_RECIPIENT_7, POOL_SCALAR / 4); + + return recipients; + } + + function getSecondaryRecipientAddresses() public pure returns (address[] memory) { + //array of recipients + address[] memory recipients = new address[](4); + + recipients[0] = DEFAULT_RECIPIENT_1; + recipients[1] = DEFAULT_RECIPIENT_5; + recipients[2] = DEFAULT_RECIPIENT_6; + recipients[3] = DEFAULT_RECIPIENT_7; + + return recipients; + } + + function addFundsToPool(bytes32 poolId, Common.Asset memory amount, address sender) public { + IDestinationRewardManager.FeePayment[] memory payments = new IDestinationRewardManager.FeePayment[](1); + payments[0] = IDestinationRewardManager.FeePayment(poolId, uint192(amount.amount)); + + addFundsToPool(payments, sender); + } + + function addFundsToPool(IDestinationRewardManager.FeePayment[] memory payments, address sender) public { + //record the current address and switch to the sender + address originalAddr = msg.sender; + changePrank(sender); + + uint256 totalPayment; + for (uint256 i; i < payments.length; ++i) { + totalPayment += payments[i].amount; + } + + //approve the amount being paid into the pool + ERC20Mock(address(asset)).approve(address(rewardManager), totalPayment); + + //this represents the verifier adding some funds to the pool + rewardManager.onFeePaid(payments, sender); + + //change back to the original address + changePrank(originalAddr); + } + + function getAsset(uint256 quantity) public view returns (Common.Asset memory) { + return Common.Asset(address(asset), quantity); + } + + function getAssetBalance(address addr) public view returns (uint256) { + return asset.balanceOf(addr); + } + + function claimRewards(bytes32[] memory poolIds, address sender) public { + //record the current address and switch to the recipient + address originalAddr = msg.sender; + changePrank(sender); + + //claim the rewards + rewardManager.claimRewards(poolIds); + + //change back to the original address + changePrank(originalAddr); + } + + function payRecipients(bytes32 poolId, address[] memory recipients, address sender) public { + //record the current address and switch to the recipient + address originalAddr = msg.sender; + changePrank(sender); + + //pay the recipients + rewardManager.payRecipients(poolId, recipients); + + //change back to the original address + changePrank(originalAddr); + } + + function setRewardRecipients(bytes32 poolId, Common.AddressAndWeight[] memory recipients, address sender) public { + //record the current address and switch to the recipient + address originalAddr = msg.sender; + changePrank(sender); + + //pay the recipients + rewardManager.setRewardRecipients(poolId, recipients); + + //change back to the original address + changePrank(originalAddr); + } + + function setFeeManager(address feeManager, address sender) public { + //record the current address and switch to the recipient + address originalAddr = msg.sender; + changePrank(sender); + + //update the proxy + rewardManager.addFeeManager(feeManager); + + //change back to the original address + changePrank(originalAddr); + } + + function updateRewardRecipients(bytes32 poolId, Common.AddressAndWeight[] memory recipients, address sender) public { + //record the current address and switch to the recipient + address originalAddr = msg.sender; + changePrank(sender); + + //pay the recipients + rewardManager.updateRewardRecipients(poolId, recipients); + + //change back to the original address + changePrank(originalAddr); + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/test/reward-manager/DestinationRewardManager.claim.t.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/test/reward-manager/DestinationRewardManager.claim.t.sol new file mode 100644 index 0000000000..c0a67d0875 --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/test/reward-manager/DestinationRewardManager.claim.t.sol @@ -0,0 +1,790 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {BaseDestinationRewardManagerTest} from "./BaseDestinationRewardManager.t.sol"; +import {Common} from "../../../libraries/Common.sol"; + +/** + * @title DestinationRewardManagerClaimTest + * @author Michael Fletcher + * @notice This contract will test the claim functionality of the RewardManager contract. + */ +contract DestinationRewardManagerClaimTest is BaseDestinationRewardManagerTest { + uint256 internal constant POOL_DEPOSIT_AMOUNT = 10e18; + + function setUp() public override { + //setup contracts + super.setUp(); + + //create a single pool for these tests + createPrimaryPool(); + + //add funds to the pool to be split among the recipients + addFundsToPool(PRIMARY_POOL_ID, getAsset(POOL_DEPOSIT_AMOUNT), FEE_MANAGER); + } + + function test_claimAllRecipients() public { + //expected recipient amount is 1/4 of the pool deposit + uint256 expectedRecipientAmount = POOL_DEPOSIT_AMOUNT / 4; + + //claim funds for each recipient within the pool + for (uint256 i; i < getPrimaryRecipients().length; i++) { + //get the recipient that is claiming + Common.AddressAndWeight memory recipient = getPrimaryRecipients()[i]; + + //claim the individual rewards for each recipient + claimRewards(PRIMARY_POOL_ARRAY, recipient.addr); + + //check the balance matches the ratio the recipient should have received + assertEq(getAssetBalance(recipient.addr), expectedRecipientAmount); + } + } + + function test_claimRewardsWithDuplicatePoolIdsDoesNotPayoutTwice() public { + //add funds to a different pool to ensure they're not claimed + addFundsToPool(SECONDARY_POOL_ID, getAsset(POOL_DEPOSIT_AMOUNT), FEE_MANAGER); + + //expected recipient amount is 1/4 of the pool deposit + uint256 expectedRecipientAmount = POOL_DEPOSIT_AMOUNT / 4; + + //create an array containing duplicate poolIds + bytes32[] memory poolIds = new bytes32[](2); + poolIds[0] = PRIMARY_POOL_ID; + poolIds[1] = PRIMARY_POOL_ID; + + //claim funds for each recipient within the pool + for (uint256 i; i < getPrimaryRecipients().length; i++) { + //get the recipient that is claiming + Common.AddressAndWeight memory recipient = getPrimaryRecipients()[i]; + + //claim the individual rewards for each recipient + claimRewards(poolIds, recipient.addr); + + //check the balance matches the ratio the recipient should have received + assertEq(getAssetBalance(recipient.addr), expectedRecipientAmount); + } + + //the pool should still have the remaining + assertEq(getAssetBalance(address(rewardManager)), POOL_DEPOSIT_AMOUNT); + } + + function test_claimSingleRecipient() public { + //get the recipient that is claiming + Common.AddressAndWeight memory recipient = getPrimaryRecipients()[0]; + + //claim the individual rewards for this recipient + claimRewards(PRIMARY_POOL_ARRAY, recipient.addr); + + //expected recipient amount is 1/4 of the pool deposit + uint256 expectedRecipientAmount = POOL_DEPOSIT_AMOUNT / 4; + + //check the recipients balance matches the ratio the recipient should have received + assertEq(getAssetBalance(recipient.addr), expectedRecipientAmount); + + //check the rewardManager has the remaining quantity + assertEq(getAssetBalance(address(rewardManager)), POOL_DEPOSIT_AMOUNT - expectedRecipientAmount); + } + + function test_claimMultipleRecipients() public { + //claim the individual rewards for each recipient + claimRewards(PRIMARY_POOL_ARRAY, getPrimaryRecipients()[0].addr); + claimRewards(PRIMARY_POOL_ARRAY, getPrimaryRecipients()[1].addr); + + //expected recipient amount is 1/4 of the pool deposit + uint256 expectedRecipientAmount = POOL_DEPOSIT_AMOUNT / 4; + + //check the recipients balance matches the ratio the recipient should have received + assertEq(getAssetBalance(getPrimaryRecipients()[0].addr), expectedRecipientAmount); + assertEq(getAssetBalance(getPrimaryRecipients()[1].addr), expectedRecipientAmount); + + //check the rewardManager has the remaining quantity + assertEq(getAssetBalance(address(rewardManager)), POOL_DEPOSIT_AMOUNT - (expectedRecipientAmount * 2)); + } + + function test_claimUnregisteredRecipient() public { + //claim the rewards for a recipient who isn't in this pool + claimRewards(PRIMARY_POOL_ARRAY, getSecondaryRecipients()[1].addr); + + //check the recipients didn't receive any fees from this pool + assertEq(getAssetBalance(getSecondaryRecipients()[1].addr), 0); + + //check the rewardManager has the remaining quantity + assertEq(getAssetBalance(address(rewardManager)), POOL_DEPOSIT_AMOUNT); + } + + function test_claimUnevenAmountRoundsDown() public { + //adding 1 to the pool should leave 1 wei worth of dust, which the contract doesn't handle due to it being economically infeasible + addFundsToPool(PRIMARY_POOL_ID, getAsset(1), FEE_MANAGER); + + //expected recipient amount is 1/4 of the pool deposit + uint256 expectedRecipientAmount = POOL_DEPOSIT_AMOUNT / 4; + + //claim funds for each recipient within the pool + for (uint256 i; i < getPrimaryRecipients().length; i++) { + //get the recipient that is claiming + Common.AddressAndWeight memory recipient = getPrimaryRecipients()[i]; + + //claim the individual rewards for each recipient + claimRewards(PRIMARY_POOL_ARRAY, recipient.addr); + + //check the balance matches the ratio the recipient should have received + assertEq(getAssetBalance(recipient.addr), expectedRecipientAmount); + } + + //check the rewardManager has the remaining quantity equals 1 wei + assertEq(getAssetBalance(address(rewardManager)), 1); + } + + function test_claimUnregisteredPoolId() public { + //get the recipient that is claiming + Common.AddressAndWeight memory recipient = getPrimaryRecipients()[0]; + + //claim the individual rewards for this recipient + claimRewards(SECONDARY_POOL_ARRAY, recipient.addr); + + //check the recipients balance is still 0 as there's no pool to receive fees from + assertEq(getAssetBalance(recipient.addr), 0); + + //check the rewardManager has the full amount + assertEq(getAssetBalance(address(rewardManager)), POOL_DEPOSIT_AMOUNT); + } + + function test_singleRecipientClaimMultipleDeposits() public { + //get the recipient that is claiming + Common.AddressAndWeight memory recipient = getPrimaryRecipients()[0]; + + //claim the individual rewards for this recipient + claimRewards(PRIMARY_POOL_ARRAY, recipient.addr); + + //expected recipient amount is 1/4 of the pool deposit + uint256 expectedRecipientAmount = POOL_DEPOSIT_AMOUNT / 4; + + //check the recipients balance matches the ratio the recipient should have received + assertEq(getAssetBalance(recipient.addr), expectedRecipientAmount); + + //check the rewardManager has the remaining quantity, which is 3/4 of the initial deposit + assertEq(getAssetBalance(address(rewardManager)), POOL_DEPOSIT_AMOUNT - expectedRecipientAmount); + + //add funds to the pool to be split among the recipients + addFundsToPool(PRIMARY_POOL_ID, getAsset(POOL_DEPOSIT_AMOUNT), FEE_MANAGER); + + //claim the individual rewards for this recipient + claimRewards(PRIMARY_POOL_ARRAY, recipient.addr); + + //check the recipients balance matches the ratio the recipient should have received, which is 1/4 of each deposit + assertEq(getAssetBalance(recipient.addr), expectedRecipientAmount * 2); + + //check the rewardManager has the remaining quantity, which is now 3/4 of both deposits + assertEq(getAssetBalance(address(rewardManager)), POOL_DEPOSIT_AMOUNT * 2 - (expectedRecipientAmount * 2)); + } + + function test_recipientsClaimMultipleDeposits() public { + //expected recipient amount is 1/4 of the pool deposit + uint256 expectedRecipientAmount = POOL_DEPOSIT_AMOUNT / 4; + + //claim funds for each recipient within the pool + for (uint256 i; i < getPrimaryRecipients().length; i++) { + //get the recipient that is claiming + Common.AddressAndWeight memory recipient = getPrimaryRecipients()[i]; + + //claim the individual rewards for each recipient + claimRewards(PRIMARY_POOL_ARRAY, recipient.addr); + + //check the balance matches the ratio the recipient should have received + assertEq(getAssetBalance(recipient.addr), expectedRecipientAmount); + } + + //the reward manager balance should be 0 as all of the funds have been claimed + assertEq(getAssetBalance(address(rewardManager)), 0); + + //add funds to the pool to be split among the recipients + addFundsToPool(PRIMARY_POOL_ID, getAsset(POOL_DEPOSIT_AMOUNT), FEE_MANAGER); + + //claim funds for each recipient within the pool + for (uint256 i; i < getPrimaryRecipients().length; i++) { + //get the recipient that is claiming + Common.AddressAndWeight memory recipient = getPrimaryRecipients()[i]; + + //claim the individual rewards for each recipient + claimRewards(PRIMARY_POOL_ARRAY, recipient.addr); + + //expected recipient amount is 1/4 of the pool deposit + expectedRecipientAmount = (POOL_DEPOSIT_AMOUNT / 4) * 2; + + //check the balance matches the ratio the recipient should have received + assertEq(getAssetBalance(recipient.addr), expectedRecipientAmount); + } + + //the reward manager balance should again be 0 as all of the funds have been claimed + assertEq(getAssetBalance(address(rewardManager)), 0); + } + + function test_eventIsEmittedUponClaim() public { + //get the recipient that is claiming + Common.AddressAndWeight memory recipient = getPrimaryRecipients()[0]; + + //expect an emit + vm.expectEmit(); + + //emit the event that is expected to be emitted + emit RewardsClaimed(PRIMARY_POOL_ID, recipient.addr, uint192(POOL_DEPOSIT_AMOUNT / 4)); + + //claim the individual rewards for each recipient + claimRewards(PRIMARY_POOL_ARRAY, recipient.addr); + } + + function test_eventIsNotEmittedUponUnsuccessfulClaim() public { + //record logs to check no events were emitted + vm.recordLogs(); + + //get the recipient that is claiming + Common.AddressAndWeight memory recipient = getPrimaryRecipients()[0]; + + //claim the individual rewards for each recipient + claimRewards(SECONDARY_POOL_ARRAY, recipient.addr); + + //no logs should have been emitted + assertEq(vm.getRecordedLogs().length, 0); + } +} + +contract DestinationRewardManagerRecipientClaimMultiplePoolsTest is BaseDestinationRewardManagerTest { + uint256 internal constant POOL_DEPOSIT_AMOUNT = 10e18; + + function setUp() public override { + //setup contracts + super.setUp(); + + //create a two pools + createPrimaryPool(); + createSecondaryPool(); + + //add funds to each of the pools to be split among the recipients + addFundsToPool(PRIMARY_POOL_ID, getAsset(POOL_DEPOSIT_AMOUNT), FEE_MANAGER); + addFundsToPool(SECONDARY_POOL_ID, getAsset(POOL_DEPOSIT_AMOUNT), FEE_MANAGER); + } + + function test_claimAllRecipientsSinglePool() public { + //expected recipient amount is 1/4 of the pool deposit + uint256 expectedRecipientAmount = POOL_DEPOSIT_AMOUNT / 4; + + //claim funds for each recipient within the pool + for (uint256 i; i < getPrimaryRecipients().length; i++) { + //get the recipient that is claiming + Common.AddressAndWeight memory recipient = getPrimaryRecipients()[i]; + + //claim the individual rewards for each recipient + claimRewards(PRIMARY_POOL_ARRAY, recipient.addr); + + //check the balance matches the ratio the recipient should have received + assertEq(getAssetBalance(recipient.addr), expectedRecipientAmount); + } + + //check the pool balance is still equal to DEPOSIT_AMOUNT as the test only claims for one of the pools + assertEq(getAssetBalance(address(rewardManager)), POOL_DEPOSIT_AMOUNT); + } + + function test_claimMultipleRecipientsSinglePool() public { + //claim the individual rewards for each recipient + claimRewards(SECONDARY_POOL_ARRAY, getSecondaryRecipients()[0].addr); + claimRewards(SECONDARY_POOL_ARRAY, getSecondaryRecipients()[1].addr); + + //expected recipient amount is 1/4 of the pool deposit + uint256 expectedRecipientAmount = POOL_DEPOSIT_AMOUNT / 4; + + //check the recipients balance matches the ratio the recipient should have received + assertEq(getAssetBalance(getSecondaryRecipients()[0].addr), expectedRecipientAmount); + assertEq(getAssetBalance(getSecondaryRecipients()[1].addr), expectedRecipientAmount); + + //check the rewardManager has the remaining quantity + assertEq(getAssetBalance(address(rewardManager)), POOL_DEPOSIT_AMOUNT * 2 - (expectedRecipientAmount * 2)); + } + + function test_claimMultipleRecipientsMultiplePools() public { + //claim the individual rewards for each recipient + claimRewards(PRIMARY_POOL_ARRAY, getPrimaryRecipients()[0].addr); + claimRewards(PRIMARY_POOL_ARRAY, getPrimaryRecipients()[1].addr); + claimRewards(SECONDARY_POOL_ARRAY, getSecondaryRecipients()[0].addr); + claimRewards(SECONDARY_POOL_ARRAY, getSecondaryRecipients()[1].addr); + + //expected recipient amount is 1/4 of the pool deposit + uint256 expectedRecipientAmount = POOL_DEPOSIT_AMOUNT / 4; + + //check the recipients balance matches the ratio the recipient should have received. The first recipient is shared across both pools so should receive 1/4 of each pool + assertEq(getAssetBalance(getPrimaryRecipients()[0].addr), expectedRecipientAmount * 2); + assertEq(getAssetBalance(getPrimaryRecipients()[1].addr), expectedRecipientAmount); + assertEq(getAssetBalance(getSecondaryRecipients()[1].addr), expectedRecipientAmount); + + //check the rewardManager has the remaining quantity + assertEq(getAssetBalance(address(rewardManager)), POOL_DEPOSIT_AMOUNT); + } + + function test_claimAllRecipientsMultiplePools() public { + //expected recipient amount is 1/4 of the pool deposit + uint256 expectedRecipientAmount = POOL_DEPOSIT_AMOUNT / 4; + + //claim funds for each recipient within the pool + for (uint256 i = 1; i < getPrimaryRecipients().length; i++) { + //get the recipient that is claiming + Common.AddressAndWeight memory recipient = getPrimaryRecipients()[i]; + + //claim the individual rewards for each recipient + claimRewards(PRIMARY_POOL_ARRAY, recipient.addr); + + //check the balance matches the ratio the recipient should have received + assertEq(getAssetBalance(recipient.addr), expectedRecipientAmount); + } + + //claim funds for each recipient within the pool + for (uint256 i = 1; i < getSecondaryRecipients().length; i++) { + //get the recipient that is claiming + Common.AddressAndWeight memory secondaryRecipient = getSecondaryRecipients()[i]; + + //claim the individual rewards for each recipient + claimRewards(SECONDARY_POOL_ARRAY, secondaryRecipient.addr); + + //check the balance matches the ratio the recipient should have received + assertEq(getAssetBalance(secondaryRecipient.addr), expectedRecipientAmount); + } + + //special case to handle the first recipient of each pool as they're the same address + Common.AddressAndWeight memory commonRecipient = getPrimaryRecipients()[0]; + + //claim the individual rewards for each pool + claimRewards(PRIMARY_POOL_ARRAY, commonRecipient.addr); + claimRewards(SECONDARY_POOL_ARRAY, commonRecipient.addr); + + //check the balance matches the ratio the recipient should have received, which is 1/4 of each deposit for each pool + assertEq(getAssetBalance(commonRecipient.addr), expectedRecipientAmount * 2); + } + + function test_claimSingleUniqueRecipient() public { + //the first recipient of the secondary pool is in both pools, so take the second recipient which is unique + Common.AddressAndWeight memory recipient = getSecondaryRecipients()[1]; + + //claim the individual rewards for this recipient + claimRewards(PRIMARY_POOL_ARRAY, recipient.addr); + claimRewards(SECONDARY_POOL_ARRAY, recipient.addr); + + //the recipient should have received 1/4 of the deposit amount + uint256 recipientExpectedAmount = POOL_DEPOSIT_AMOUNT / 4; + + //the recipient should have received 1/4 of the deposit amount + assertEq(getAssetBalance(recipient.addr), recipientExpectedAmount); + + //check the rewardManager has the remaining quantity + assertEq(getAssetBalance(address(rewardManager)), POOL_DEPOSIT_AMOUNT * 2 - recipientExpectedAmount); + } + + function test_claimSingleRecipientMultiplePools() public { + //the first recipient of the secondary pool is in both pools + Common.AddressAndWeight memory recipient = getSecondaryRecipients()[0]; + + //claim the individual rewards for this recipient + claimRewards(PRIMARY_POOL_ARRAY, recipient.addr); + claimRewards(SECONDARY_POOL_ARRAY, recipient.addr); + + //the recipient should have received 1/4 of the deposit amount for each pool + uint256 recipientExpectedAmount = (POOL_DEPOSIT_AMOUNT / 4) * 2; + + //this recipient belongs in both pools so should have received 1/4 of each + assertEq(getAssetBalance(recipient.addr), recipientExpectedAmount); + + //check the rewardManager has the remaining quantity + assertEq(getAssetBalance(address(rewardManager)), POOL_DEPOSIT_AMOUNT * 2 - recipientExpectedAmount); + } + + function test_claimUnregisteredRecipient() public { + //claim the individual rewards for this recipient + claimRewards(PRIMARY_POOL_ARRAY, getSecondaryRecipients()[1].addr); + claimRewards(SECONDARY_POOL_ARRAY, getPrimaryRecipients()[1].addr); + + //check the recipients didn't receive any fees from this pool + assertEq(getAssetBalance(getSecondaryRecipients()[1].addr), 0); + assertEq(getAssetBalance(getPrimaryRecipients()[1].addr), 0); + + //check the rewardManager has the remaining quantity + assertEq(getAssetBalance(address(rewardManager)), POOL_DEPOSIT_AMOUNT * 2); + } + + function test_claimUnevenAmountRoundsDown() public { + //adding an uneven amount of dust to each pool, this should round down to the nearest whole number with 4 remaining in the contract + addFundsToPool(PRIMARY_POOL_ID, getAsset(3), FEE_MANAGER); + addFundsToPool(SECONDARY_POOL_ID, getAsset(1), FEE_MANAGER); + + //the recipient should have received 1/4 of the deposit amount for each pool + uint256 recipientExpectedAmount = POOL_DEPOSIT_AMOUNT / 4; + + //claim funds for each recipient within the pool + for (uint256 i; i < getPrimaryRecipients().length; i++) { + //get the recipient that is claiming + Common.AddressAndWeight memory recipient = getPrimaryRecipients()[i]; + + //claim the individual rewards for each recipient + claimRewards(PRIMARY_POOL_ARRAY, recipient.addr); + + //check the balance matches the ratio the recipient should have received + assertEq(getAssetBalance(recipient.addr), recipientExpectedAmount); + } + + //special case to handle the first recipient of each pool as they're the same address + claimRewards(SECONDARY_POOL_ARRAY, getSecondaryRecipients()[0].addr); + + //check the balance matches the ratio the recipient should have received + assertEq(getAssetBalance(getSecondaryRecipients()[0].addr), recipientExpectedAmount * 2); + + //claim funds for each recipient of the secondary pool except the first + for (uint256 i = 1; i < getSecondaryRecipients().length; i++) { + //get the recipient that is claiming + Common.AddressAndWeight memory recipient = getSecondaryRecipients()[i]; + + //claim the individual rewards for each recipient + claimRewards(SECONDARY_POOL_ARRAY, recipient.addr); + + //check the balance matches the ratio the recipient should have received + assertEq(getAssetBalance(recipient.addr), recipientExpectedAmount); + } + + //contract should have 4 remaining + assertEq(getAssetBalance(address(rewardManager)), 4); + } + + function test_singleRecipientClaimMultipleDeposits() public { + //get the recipient that is claiming + Common.AddressAndWeight memory recipient = getSecondaryRecipients()[0]; + + //claim the individual rewards for this recipient + claimRewards(SECONDARY_POOL_ARRAY, recipient.addr); + + //the recipient should have received 1/4 of the deposit amount + uint256 expectedRecipientAmount = POOL_DEPOSIT_AMOUNT / 4; + + //check the recipients balance matches the ratio the recipient should have received + assertEq(getAssetBalance(recipient.addr), expectedRecipientAmount); + + //check the rewardManager has the remaining quantity, which is 3/4 of the initial deposit plus the deposit from the second pool + assertEq(getAssetBalance(address(rewardManager)), POOL_DEPOSIT_AMOUNT * 2 - expectedRecipientAmount); + + //add funds to the pool to be split among the recipients + addFundsToPool(SECONDARY_POOL_ID, getAsset(POOL_DEPOSIT_AMOUNT), FEE_MANAGER); + + //claim the individual rewards for this recipient + claimRewards(SECONDARY_POOL_ARRAY, recipient.addr); + + //the recipient should have received 1/4 of the next deposit amount + expectedRecipientAmount += POOL_DEPOSIT_AMOUNT / 4; + + //check the recipients balance matches the ratio the recipient should have received, which is 1/4 of each deposit + assertEq(getAssetBalance(recipient.addr), expectedRecipientAmount); + + //check the rewardManager has the remaining quantity, which is now 3/4 of both deposits + assertEq(getAssetBalance(address(rewardManager)), POOL_DEPOSIT_AMOUNT * 3 - expectedRecipientAmount); + } + + function test_recipientsClaimMultipleDeposits() public { + //the recipient should have received 1/4 of the deposit amount + uint256 expectedRecipientAmount = POOL_DEPOSIT_AMOUNT / 4; + + //claim funds for each recipient within the pool + for (uint256 i; i < getSecondaryRecipients().length; i++) { + //get the recipient that is claiming + Common.AddressAndWeight memory recipient = getSecondaryRecipients()[i]; + + //claim the individual rewards for each recipient + claimRewards(SECONDARY_POOL_ARRAY, recipient.addr); + + //check the balance matches the ratio the recipient should have received + assertEq(getAssetBalance(recipient.addr), expectedRecipientAmount); + } + + //the reward manager balance should contain only the funds of the secondary pool + assertEq(getAssetBalance(address(rewardManager)), POOL_DEPOSIT_AMOUNT); + + //add funds to the pool to be split among the recipients + addFundsToPool(SECONDARY_POOL_ID, getAsset(POOL_DEPOSIT_AMOUNT), FEE_MANAGER); + + //special case to handle the first recipient of each pool as they're the same address + claimRewards(SECONDARY_POOL_ARRAY, getSecondaryRecipients()[0].addr); + + //check the balance matches the ratio the recipient should have received + assertEq(getAssetBalance(getSecondaryRecipients()[0].addr), expectedRecipientAmount * 2); + + //claim funds for each recipient within the pool except the first + for (uint256 i = 1; i < getSecondaryRecipients().length; i++) { + //get the recipient that is claiming + Common.AddressAndWeight memory recipient = getSecondaryRecipients()[i]; + + //claim the individual rewards for each recipient + claimRewards(SECONDARY_POOL_ARRAY, recipient.addr); + + //check the balance matches the ratio the recipient should have received + assertEq(getAssetBalance(recipient.addr), expectedRecipientAmount * 2); + } + + //the reward manager balance should again be the balance of the secondary pool as the primary pool has been emptied twice + assertEq(getAssetBalance(address(rewardManager)), POOL_DEPOSIT_AMOUNT); + } + + function test_claimEmptyPoolWhenSecondPoolContainsFunds() public { + //the recipient should have received 1/4 of the deposit amount + uint256 expectedRecipientAmount = POOL_DEPOSIT_AMOUNT / 4; + + //claim all rewards for each recipient in the primary pool + for (uint256 i; i < getPrimaryRecipients().length; i++) { + //get the recipient that is claiming + Common.AddressAndWeight memory recipient = getPrimaryRecipients()[i]; + + //claim the individual rewards for each recipient + claimRewards(PRIMARY_POOL_ARRAY, recipient.addr); + + //check the balance matches the ratio the recipient should have received + assertEq(getAssetBalance(recipient.addr), expectedRecipientAmount); + } + + //claim all the rewards again for the first recipient as that address is a member of both pools + claimRewards(PRIMARY_POOL_ARRAY, getSecondaryRecipients()[0].addr); + + //check the balance + assertEq(getAssetBalance(getSecondaryRecipients()[0].addr), expectedRecipientAmount); + } + + function test_getRewardsAvailableToRecipientInBothPools() public { + //get index 0 as this recipient is in both default pools + bytes32[] memory poolIds = rewardManager.getAvailableRewardPoolIds( + getPrimaryRecipients()[0].addr, + 0, + type(uint256).max + ); + + //check the recipient is in both pools + assertEq(poolIds[0], PRIMARY_POOL_ID); + assertEq(poolIds[1], SECONDARY_POOL_ID); + } + + function test_getRewardsAvailableToRecipientInSinglePool() public { + //get index 0 as this recipient is in both default pools + bytes32[] memory poolIds = rewardManager.getAvailableRewardPoolIds( + getPrimaryRecipients()[1].addr, + 0, + type(uint256).max + ); + + //check the recipient is in both pools + assertEq(poolIds[0], PRIMARY_POOL_ID); + assertEq(poolIds[1], ZERO_POOL_ID); + } + + function test_getRewardsAvailableToRecipientInNoPools() public view { + //get index 0 as this recipient is in both default pools + bytes32[] memory poolIds = rewardManager.getAvailableRewardPoolIds(FEE_MANAGER, 0, type(uint256).max); + + //check the recipient is in neither pool + assertEq(poolIds[0], ZERO_POOL_ID); + assertEq(poolIds[1], ZERO_POOL_ID); + } + + function test_getRewardsAvailableToRecipientInBothPoolsWhereAlreadyClaimed() public { + //get index 0 as this recipient is in both default pools + bytes32[] memory poolIds = rewardManager.getAvailableRewardPoolIds( + getPrimaryRecipients()[0].addr, + 0, + type(uint256).max + ); + + //check the recipient is in both pools + assertEq(poolIds[0], PRIMARY_POOL_ID); + assertEq(poolIds[1], SECONDARY_POOL_ID); + + //claim the rewards for each pool + claimRewards(PRIMARY_POOL_ARRAY, getPrimaryRecipients()[0].addr); + claimRewards(SECONDARY_POOL_ARRAY, getSecondaryRecipients()[0].addr); + + //get the available pools again + poolIds = rewardManager.getAvailableRewardPoolIds(getPrimaryRecipients()[0].addr, 0, type(uint256).max); + + //user should not be in any pool + assertEq(poolIds[0], ZERO_POOL_ID); + assertEq(poolIds[1], ZERO_POOL_ID); + } + + function test_getAvailableRewardsCursorCannotBeGreaterThanTotalPools() public { + vm.expectRevert(INVALID_POOL_LENGTH_SELECTOR); + + rewardManager.getAvailableRewardPoolIds(FEE_MANAGER, type(uint256).max, 0); + } + + function test_getAvailableRewardsCursorAndTotalPoolsEqual() public { + bytes32[] memory poolIds = rewardManager.getAvailableRewardPoolIds(getPrimaryRecipients()[0].addr, 2, 2); + + assertEq(poolIds.length, 0); + } + + function test_getAvailableRewardsCursorSingleResult() public { + bytes32[] memory poolIds = rewardManager.getAvailableRewardPoolIds(getPrimaryRecipients()[0].addr, 0, 1); + + assertEq(poolIds[0], PRIMARY_POOL_ID); + } +} + +contract DestinationRewardManagerRecipientClaimDifferentWeightsTest is BaseDestinationRewardManagerTest { + uint256 internal constant POOL_DEPOSIT_AMOUNT = 10e18; + + function setUp() public override { + //setup contracts + super.setUp(); + + //create a single pool for these tests + createPrimaryPool(); + + //add funds to the pool to be split among the recipients + addFundsToPool(PRIMARY_POOL_ID, getAsset(POOL_DEPOSIT_AMOUNT), FEE_MANAGER); + } + + function getPrimaryRecipients() public virtual override returns (Common.AddressAndWeight[] memory) { + //array of recipients + Common.AddressAndWeight[] memory recipients = new Common.AddressAndWeight[](4); + + //init each recipient with uneven weights + recipients[0] = Common.AddressAndWeight(DEFAULT_RECIPIENT_1, TEN_PERCENT); + recipients[1] = Common.AddressAndWeight(DEFAULT_RECIPIENT_2, TEN_PERCENT * 8); + recipients[2] = Common.AddressAndWeight(DEFAULT_RECIPIENT_3, ONE_PERCENT * 6); + recipients[3] = Common.AddressAndWeight(DEFAULT_RECIPIENT_4, ONE_PERCENT * 4); + + return recipients; + } + + function test_allRecipientsClaimingReceiveExpectedAmount() public { + //loop all the recipients and claim their expected amount + for (uint256 i; i < getPrimaryRecipients().length; i++) { + //get the recipient that is claiming + Common.AddressAndWeight memory recipient = getPrimaryRecipients()[i]; + + //claim the individual rewards for each recipient + claimRewards(PRIMARY_POOL_ARRAY, recipient.addr); + + //the recipient should have received a share proportional to their weight + uint256 expectedRecipientAmount = (POOL_DEPOSIT_AMOUNT * recipient.weight) / POOL_SCALAR; + + //check the balance matches the ratio the recipient should have received + assertEq(getAssetBalance(recipient.addr), expectedRecipientAmount); + } + } +} + +contract DestinationRewardManagerRecipientClaimUnevenWeightTest is BaseDestinationRewardManagerTest { + uint256 internal constant POOL_DEPOSIT_AMOUNT = 10e18; + + function setUp() public override { + //setup contracts + super.setUp(); + + //create a single pool for these tests + createPrimaryPool(); + } + + function getPrimaryRecipients() public virtual override returns (Common.AddressAndWeight[] memory) { + //array of recipients + Common.AddressAndWeight[] memory recipients = new Common.AddressAndWeight[](2); + + uint64 oneThird = POOL_SCALAR / 3; + + //init each recipient with even weights. + recipients[0] = Common.AddressAndWeight(DEFAULT_RECIPIENT_1, oneThird); + recipients[1] = Common.AddressAndWeight(DEFAULT_RECIPIENT_2, 2 * oneThird + 1); + + return recipients; + } + + function test_allRecipientsClaimingReceiveExpectedAmountWithSmallDeposit() public { + //add a smaller amount of funds to the pool + uint256 smallDeposit = 1e8; + + //add a smaller amount of funds to the pool + addFundsToPool(PRIMARY_POOL_ID, getAsset(smallDeposit), FEE_MANAGER); + + //loop all the recipients and claim their expected amount + for (uint256 i; i < getPrimaryRecipients().length; i++) { + //get the recipient that is claiming + Common.AddressAndWeight memory recipient = getPrimaryRecipients()[i]; + + //claim the individual rewards for each recipient + claimRewards(PRIMARY_POOL_ARRAY, recipient.addr); + + //the recipient should have received a share proportional to their weight + uint256 expectedRecipientAmount = (smallDeposit * recipient.weight) / POOL_SCALAR; + + //check the balance matches the ratio the recipient should have received + assertEq(getAssetBalance(recipient.addr), expectedRecipientAmount); + } + + //smaller deposits will consequently have less precision and will not be able to be split as evenly, the remaining 1 will be lost due to 333...|... being paid out instead of 333...4| + assertEq(getAssetBalance(address(rewardManager)), 1); + } + + function test_allRecipientsClaimingReceiveExpectedAmount() public { + //add funds to the pool to be split among the recipients + addFundsToPool(PRIMARY_POOL_ID, getAsset(POOL_DEPOSIT_AMOUNT), FEE_MANAGER); + + //loop all the recipients and claim their expected amount + for (uint256 i; i < getPrimaryRecipients().length; i++) { + //get the recipient that is claiming + Common.AddressAndWeight memory recipient = getPrimaryRecipients()[i]; + + //claim the individual rewards for each recipient + claimRewards(PRIMARY_POOL_ARRAY, recipient.addr); + + //the recipient should have received a share proportional to their weight + uint256 expectedRecipientAmount = (POOL_DEPOSIT_AMOUNT * recipient.weight) / POOL_SCALAR; + + //check the balance matches the ratio the recipient should have received + assertEq(getAssetBalance(recipient.addr), expectedRecipientAmount); + } + + //their should be 0 wei left over indicating a successful split + assertEq(getAssetBalance(address(rewardManager)), 0); + } +} + +contract DestinationRewardManagerNoRecipientSet is BaseDestinationRewardManagerTest { + uint256 internal constant POOL_DEPOSIT_AMOUNT = 10e18; + + function setUp() public override { + //setup contracts + super.setUp(); + + //add funds to the pool to be split among the recipients once registered + addFundsToPool(PRIMARY_POOL_ID, getAsset(POOL_DEPOSIT_AMOUNT), FEE_MANAGER); + } + + function test_claimAllRecipientsAfterRecipientsSet() public { + //expected recipient amount is 1/4 of the pool deposit + uint256 expectedRecipientAmount = POOL_DEPOSIT_AMOUNT / 4; + + //try and claim funds for each recipient within the pool + for (uint256 i; i < getPrimaryRecipients().length; i++) { + //get the recipient that is claiming + Common.AddressAndWeight memory recipient = getPrimaryRecipients()[i]; + + //there should be no rewards claimed as the recipient is not registered + claimRewards(PRIMARY_POOL_ARRAY, recipient.addr); + + //check the recipient received nothing + assertEq(getAssetBalance(recipient.addr), 0); + } + + //Set the recipients after the rewards have been paid into the pool + setRewardRecipients(PRIMARY_POOL_ID, getPrimaryRecipients(), ADMIN); + + //claim funds for each recipient within the pool + for (uint256 i; i < getPrimaryRecipients().length; i++) { + //get the recipient that is claiming + Common.AddressAndWeight memory recipient = getPrimaryRecipients()[i]; + + //there should be no rewards claimed as the recipient is registered + claimRewards(PRIMARY_POOL_ARRAY, recipient.addr); + + //check the balance matches the ratio the recipient should have received + assertEq(getAssetBalance(recipient.addr), expectedRecipientAmount); + } + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/test/reward-manager/DestinationRewardManager.general.t.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/test/reward-manager/DestinationRewardManager.general.t.sol new file mode 100644 index 0000000000..4c79d2cba5 --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/test/reward-manager/DestinationRewardManager.general.t.sol @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {BaseDestinationRewardManagerTest} from "./BaseDestinationRewardManager.t.sol"; +import {DestinationRewardManager} from "../../../v0.4.0/DestinationRewardManager.sol"; +import {ERC20Mock} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/mocks/ERC20Mock.sol"; +import {IDestinationRewardManager} from "../../interfaces/IDestinationRewardManager.sol"; + +/** + * @title DestinationRewardManagerSetupTest + * @author Michael Fletcher + * @notice This contract will test the core functionality of the DestinationRewardManager contract + */ +contract DestinationRewardManagerSetupTest is BaseDestinationRewardManagerTest { + uint256 internal constant POOL_DEPOSIT_AMOUNT = 10e18; + + function setUp() public override { + //setup contracts + super.setUp(); + } + + function test_rejectsZeroLinkAddressOnConstruction() public { + //should revert if the contract is a zero address + vm.expectRevert(INVALID_ADDRESS_ERROR_SELECTOR); + + //create a rewardManager with a zero link address + new DestinationRewardManager(address(0)); + } + + function test_eventEmittedUponFeeManagerUpdate() public { + //expect the event to be emitted + vm.expectEmit(); + + //emit the event that is expected to be emitted + emit FeeManagerUpdated(FEE_MANAGER_2); + + //set the verifier proxy + setFeeManager(FEE_MANAGER_2, ADMIN); + } + + function test_eventEmittedUponFeePaid() public { + //create pool and add funds + createPrimaryPool(); + + //change to the feeManager who is the one who will be paying the fees + changePrank(FEE_MANAGER); + + //approve the amount being paid into the pool + ERC20Mock(getAsset(POOL_DEPOSIT_AMOUNT).assetAddress).approve(address(rewardManager), POOL_DEPOSIT_AMOUNT); + + IDestinationRewardManager.FeePayment[] memory payments = new IDestinationRewardManager.FeePayment[](1); + payments[0] = IDestinationRewardManager.FeePayment(PRIMARY_POOL_ID, uint192(POOL_DEPOSIT_AMOUNT)); + + //event is emitted when funds are added + vm.expectEmit(); + emit FeePaid(payments, FEE_MANAGER); + + //this represents the verifier adding some funds to the pool + rewardManager.onFeePaid(payments, FEE_MANAGER); + } + + function test_setFeeManagerZeroAddress() public { + //should revert if the contract is a zero address + vm.expectRevert(INVALID_ADDRESS_ERROR_SELECTOR); + + //set the verifier proxy + setFeeManager(address(0), ADMIN); + } + + function test_addFeeManagerZeroAddress() public { + vm.expectRevert(INVALID_ADDRESS_ERROR_SELECTOR); + rewardManager.addFeeManager(address(0)); + } + + function test_addFeeManagerExistingAddress() public { + address dummyAddress = address(998); + rewardManager.addFeeManager(dummyAddress); + vm.expectRevert(INVALID_ADDRESS_ERROR_SELECTOR); + rewardManager.addFeeManager(dummyAddress); + } + + function test_removeFeeManagerNonExistentAddress() public { + address dummyAddress = address(991); + vm.expectRevert(INVALID_ADDRESS_ERROR_SELECTOR); + rewardManager.removeFeeManager(dummyAddress); + } + + function test_addRemoveFeeManager() public { + address dummyAddress1 = address(1); + address dummyAddress2 = address(2); + rewardManager.addFeeManager(dummyAddress1); + rewardManager.addFeeManager(dummyAddress2); + assertEq(rewardManager.s_feeManagerAddressList(dummyAddress1), dummyAddress1); + assertEq(rewardManager.s_feeManagerAddressList(dummyAddress2), dummyAddress2); + rewardManager.removeFeeManager(dummyAddress1); + assertEq(rewardManager.s_feeManagerAddressList(dummyAddress1), address(0)); + assertEq(rewardManager.s_feeManagerAddressList(dummyAddress2), dummyAddress2); + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/test/reward-manager/DestinationRewardManager.payRecipients.t.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/test/reward-manager/DestinationRewardManager.payRecipients.t.sol new file mode 100644 index 0000000000..4aa3c868b3 --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/test/reward-manager/DestinationRewardManager.payRecipients.t.sol @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {BaseDestinationRewardManagerTest} from "./BaseDestinationRewardManager.t.sol"; +import {IDestinationRewardManager} from "../../interfaces/IDestinationRewardManager.sol"; + +/** + * @title DestinationRewardManagerPayRecipientsTest + * @author Michael Fletcher + * @notice This contract will test the payRecipients functionality of the RewardManager contract + */ +contract DestinationRewardManagerPayRecipientsTest is BaseDestinationRewardManagerTest { + uint256 internal constant POOL_DEPOSIT_AMOUNT = 10e18; + + function setUp() public override { + //setup contracts + super.setUp(); + + //create a single pool for these tests + createPrimaryPool(); + + //add funds to the pool to be split among the recipients + addFundsToPool(PRIMARY_POOL_ID, getAsset(POOL_DEPOSIT_AMOUNT), FEE_MANAGER); + } + + function test_payAllRecipients() public { + //pay all the recipients in the pool + payRecipients(PRIMARY_POOL_ID, getPrimaryRecipientAddresses(), ADMIN); + + //each recipient should receive 1/4 of the pool + uint256 expectedRecipientAmount = POOL_DEPOSIT_AMOUNT / 4; + + //check each recipient received the correct amount + for (uint256 i = 0; i < getPrimaryRecipientAddresses().length; i++) { + assertEq(getAssetBalance(getPrimaryRecipientAddresses()[i]), expectedRecipientAmount); + } + } + + function test_paySingleRecipient() public { + //get the first individual recipient + address recipient = getPrimaryRecipientAddresses()[0]; + + //get a single recipient as an array + address[] memory recipients = new address[](1); + recipients[0] = recipient; + + //pay a single recipient + payRecipients(PRIMARY_POOL_ID, recipients, ADMIN); + + //the recipient should have received 1/4 of the deposit amount + uint256 expectedRecipientAmount = POOL_DEPOSIT_AMOUNT / 4; + + assertEq(getAssetBalance(recipient), expectedRecipientAmount); + } + + function test_payRecipientWithInvalidPool() public { + //get the first individual recipient + address recipient = getPrimaryRecipientAddresses()[0]; + + //get a single recipient as an array + address[] memory recipients = new address[](1); + recipients[0] = recipient; + + //pay a single recipient + payRecipients(SECONDARY_POOL_ID, recipients, ADMIN); + + //the recipient should have received nothing + assertEq(getAssetBalance(recipient), 0); + } + + function test_payRecipientsEmptyRecipientList() public { + //get a single recipient + address[] memory recipients = new address[](0); + + //pay a single recipient + payRecipients(PRIMARY_POOL_ID, recipients, ADMIN); + + //rewardManager should have the full balance + assertEq(getAssetBalance(address(rewardManager)), POOL_DEPOSIT_AMOUNT); + } + + function test_payAllRecipientsWithAdditionalUnregisteredRecipient() public { + //load all the recipients and add an additional one who is not in the pool + address[] memory recipients = new address[](getPrimaryRecipientAddresses().length + 1); + for (uint256 i = 0; i < getPrimaryRecipientAddresses().length; i++) { + recipients[i] = getPrimaryRecipientAddresses()[i]; + } + recipients[recipients.length - 1] = DEFAULT_RECIPIENT_5; + + //pay the recipients + payRecipients(PRIMARY_POOL_ID, recipients, ADMIN); + + //each recipient should receive 1/4 of the pool except the last + uint256 expectedRecipientAmount = POOL_DEPOSIT_AMOUNT / 4; + + //check each recipient received the correct amount + for (uint256 i = 0; i < getPrimaryRecipientAddresses().length; i++) { + assertEq(getAssetBalance(getPrimaryRecipientAddresses()[i]), expectedRecipientAmount); + } + + //the unregistered recipient should receive nothing + assertEq(getAssetBalance(DEFAULT_RECIPIENT_5), 0); + } + + function test_payAllRecipientsWithAdditionalInvalidRecipient() public { + //load all the recipients and add an additional one which is invalid, that should receive nothing + address[] memory recipients = new address[](getPrimaryRecipientAddresses().length + 1); + for (uint256 i = 0; i < getPrimaryRecipientAddresses().length; i++) { + recipients[i] = getPrimaryRecipientAddresses()[i]; + } + recipients[recipients.length - 1] = INVALID_ADDRESS; + + //pay the recipients + payRecipients(PRIMARY_POOL_ID, recipients, ADMIN); + + //each recipient should receive 1/4 of the pool except the last + uint256 expectedRecipientAmount = POOL_DEPOSIT_AMOUNT / 4; + + //check each recipient received the correct amount + for (uint256 i = 0; i < getPrimaryRecipientAddresses().length; i++) { + assertEq(getAssetBalance(getPrimaryRecipientAddresses()[i]), expectedRecipientAmount); + } + } + + function test_paySubsetOfRecipientsInPool() public { + //load a subset of the recipients into an array + address[] memory recipients = new address[](getPrimaryRecipientAddresses().length - 1); + for (uint256 i = 0; i < recipients.length; i++) { + recipients[i] = getPrimaryRecipientAddresses()[i]; + } + + //pay the subset of recipients + payRecipients(PRIMARY_POOL_ID, recipients, ADMIN); + + //each recipient should receive 1/4 of the pool except the last + uint256 expectedRecipientAmount = POOL_DEPOSIT_AMOUNT / 4; + + //check each subset of recipients received the correct amount + for (uint256 i = 0; i < recipients.length - 1; i++) { + assertEq(getAssetBalance(recipients[i]), expectedRecipientAmount); + } + + //check the pool has the remaining balance + assertEq( + getAssetBalance(address(rewardManager)), + POOL_DEPOSIT_AMOUNT - expectedRecipientAmount * recipients.length + ); + } + + function test_payAllRecipientsFromNonAdminUser() public { + //should revert if the caller isn't an admin or recipient within the pool + vm.expectRevert(UNAUTHORIZED_ERROR_SELECTOR); + + //pay all the recipients in the pool + payRecipients(PRIMARY_POOL_ID, getPrimaryRecipientAddresses(), FEE_MANAGER); + } + + function test_payAllRecipientsFromRecipientInPool() public { + //pay all the recipients in the pool + payRecipients(PRIMARY_POOL_ID, getPrimaryRecipientAddresses(), DEFAULT_RECIPIENT_1); + + //each recipient should receive 1/4 of the pool + uint256 expectedRecipientAmount = POOL_DEPOSIT_AMOUNT / 4; + + //check each recipient received the correct amount + for (uint256 i = 0; i < getPrimaryRecipientAddresses().length; i++) { + assertEq(getAssetBalance(getPrimaryRecipientAddresses()[i]), expectedRecipientAmount); + } + } + + function test_payRecipientsWithInvalidPoolId() public { + //pay all the recipients in the pool + payRecipients(INVALID_POOL_ID, getPrimaryRecipientAddresses(), ADMIN); + + //pool should still contain the full balance + assertEq(getAssetBalance(address(rewardManager)), POOL_DEPOSIT_AMOUNT); + } + + function test_addFundsToPoolAsOwner() public { + //add funds to the pool + addFundsToPool(PRIMARY_POOL_ID, getAsset(POOL_DEPOSIT_AMOUNT), FEE_MANAGER); + } + + function test_addFundsToPoolAsNonOwnerOrFeeManager() public { + //should revert if the caller isn't an admin or recipient within the pool + vm.expectRevert(UNAUTHORIZED_ERROR_SELECTOR); + + IDestinationRewardManager.FeePayment[] memory payments = new IDestinationRewardManager.FeePayment[](1); + payments[0] = IDestinationRewardManager.FeePayment(PRIMARY_POOL_ID, uint192(POOL_DEPOSIT_AMOUNT)); + + //add funds to the pool + rewardManager.onFeePaid(payments, USER); + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/test/reward-manager/DestinationRewardManager.setRecipients.t.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/test/reward-manager/DestinationRewardManager.setRecipients.t.sol new file mode 100644 index 0000000000..facbaa1ab7 --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/test/reward-manager/DestinationRewardManager.setRecipients.t.sol @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {BaseDestinationRewardManagerTest} from "./BaseDestinationRewardManager.t.sol"; +import {Common} from "../../../libraries/Common.sol"; + +/** + * @title DestinationRewardManagerSetRecipientsTest + * @author Michael Fletcher + * @notice This contract will test the setRecipient functionality of the RewardManager contract + */ +contract DestinationRewardManagerSetRecipientsTest is BaseDestinationRewardManagerTest { + function setUp() public override { + //setup contracts + super.setUp(); + } + + function test_setRewardRecipients() public { + //set the recipients + setRewardRecipients(PRIMARY_POOL_ID, getPrimaryRecipients(), ADMIN); + } + + function test_setRewardRecipientsIsEmpty() public { + //array of recipients + Common.AddressAndWeight[] memory recipients = new Common.AddressAndWeight[](4); + + //should revert if the recipients array is empty + vm.expectRevert(INVALID_ADDRESS_ERROR_SELECTOR); + + //set the recipients + setRewardRecipients(PRIMARY_POOL_ID, recipients, ADMIN); + } + + function test_setRewardRecipientWithZeroWeight() public { + //array of recipients + Common.AddressAndWeight[] memory recipients = new Common.AddressAndWeight[](5); + + //init each recipient with even weights + recipients[0] = Common.AddressAndWeight(DEFAULT_RECIPIENT_1, ONE_PERCENT * 25); + recipients[1] = Common.AddressAndWeight(DEFAULT_RECIPIENT_2, ONE_PERCENT * 25); + recipients[2] = Common.AddressAndWeight(DEFAULT_RECIPIENT_3, ONE_PERCENT * 25); + recipients[3] = Common.AddressAndWeight(DEFAULT_RECIPIENT_4, ONE_PERCENT * 25); + recipients[4] = Common.AddressAndWeight(DEFAULT_RECIPIENT_5, 0); + + //set the recipients + setRewardRecipients(PRIMARY_POOL_ID, recipients, ADMIN); + } + + function test_setRewardRecipientWithZeroAddress() public { + //array of recipients + Common.AddressAndWeight[] memory recipients = getPrimaryRecipients(); + + //override the first recipient with a zero address + recipients[0].addr = address(0); + + //should revert if the recipients array is empty + vm.expectRevert(INVALID_ADDRESS_ERROR_SELECTOR); + + //set the recipients + setRewardRecipients(PRIMARY_POOL_ID, recipients, ADMIN); + } + + function test_setRewardRecipientWeights() public { + //array of recipients + Common.AddressAndWeight[] memory recipients = new Common.AddressAndWeight[](4); + + //init each recipient with even weights + recipients[0] = Common.AddressAndWeight(DEFAULT_RECIPIENT_1, 25); + recipients[1] = Common.AddressAndWeight(DEFAULT_RECIPIENT_2, 25); + recipients[2] = Common.AddressAndWeight(DEFAULT_RECIPIENT_3, 25); + recipients[3] = Common.AddressAndWeight(DEFAULT_RECIPIENT_4, 25); + + //should revert if the recipients array is empty + vm.expectRevert(INVALID_WEIGHT_ERROR_SELECTOR); + + //set the recipients with a recipient with a weight of 100% + setRewardRecipients(PRIMARY_POOL_ID, recipients, ADMIN); + } + + function test_setSingleRewardRecipient() public { + //array of recipients + Common.AddressAndWeight[] memory recipients = new Common.AddressAndWeight[](1); + + //init each recipient with even weights + recipients[0] = Common.AddressAndWeight(DEFAULT_RECIPIENT_1, POOL_SCALAR); + + //set the recipients with a recipient with a weight of 100% + setRewardRecipients(PRIMARY_POOL_ID, recipients, ADMIN); + } + + function test_setRewardRecipientTwice() public { + //set the recipients + setRewardRecipients(PRIMARY_POOL_ID, getPrimaryRecipients(), ADMIN); + + //should revert if recipients for this pool have already been set + vm.expectRevert(INVALID_POOL_ID_ERROR_SELECTOR); + + //set the recipients again + setRewardRecipients(PRIMARY_POOL_ID, getPrimaryRecipients(), ADMIN); + } + + function test_setRewardRecipientFromNonOwnerOrFeeManagerAddress() public { + //should revert if the sender is not the owner or proxy + vm.expectRevert(UNAUTHORIZED_ERROR_SELECTOR); + + //set the recipients + setRewardRecipients(PRIMARY_POOL_ID, getPrimaryRecipients(), USER); + } + + function test_setRewardRecipientFromManagerAddress() public { + //update the proxy address + setFeeManager(FEE_MANAGER_2, ADMIN); + + //set the recipients + setRewardRecipients(PRIMARY_POOL_ID, getPrimaryRecipients(), FEE_MANAGER_2); + } + + function test_eventIsEmittedUponSetRecipients() public { + //expect an emit + vm.expectEmit(); + + //emit the event that is expected to be emitted + emit RewardRecipientsUpdated(PRIMARY_POOL_ID, getPrimaryRecipients()); + + //set the recipients + setRewardRecipients(PRIMARY_POOL_ID, getPrimaryRecipients(), ADMIN); + } + + function test_setRecipientContainsDuplicateRecipients() public { + //create a new array to hold the existing recipients + Common.AddressAndWeight[] memory recipients = new Common.AddressAndWeight[](getPrimaryRecipients().length * 2); + + //add all the existing recipients + for (uint256 i; i < getPrimaryRecipients().length; i++) { + recipients[i] = getPrimaryRecipients()[i]; + } + //add all the existing recipients again + for (uint256 i; i < getPrimaryRecipients().length; i++) { + recipients[i + getPrimaryRecipients().length] = getPrimaryRecipients()[i]; + } + + //should revert as the list contains a duplicate + vm.expectRevert(INVALID_ADDRESS_ERROR_SELECTOR); + + //set the recipients + setRewardRecipients(PRIMARY_POOL_ID, recipients, ADMIN); + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/test/reward-manager/DestinationRewardManager.updateRewardRecipients.t.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/test/reward-manager/DestinationRewardManager.updateRewardRecipients.t.sol new file mode 100644 index 0000000000..226be8ed32 --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/test/reward-manager/DestinationRewardManager.updateRewardRecipients.t.sol @@ -0,0 +1,450 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {BaseDestinationRewardManagerTest} from "./BaseDestinationRewardManager.t.sol"; +import {Common} from "../../../libraries/Common.sol"; + +/** + * @title DestinationRewardManagerUpdateRewardRecipientsTest + * @author Michael Fletcher + * @notice This contract will test the updateRecipient functionality of the RewardManager contract + */ +contract DestinationRewardManagerUpdateRewardRecipientsTest is BaseDestinationRewardManagerTest { + uint256 internal constant POOL_DEPOSIT_AMOUNT = 10e18; + + function setUp() public override { + //setup contracts + super.setUp(); + + //create a single pool for these tests + createPrimaryPool(); + + //add funds to the pool to be split among the recipients + addFundsToPool(PRIMARY_POOL_ID, getAsset(POOL_DEPOSIT_AMOUNT), FEE_MANAGER); + } + + function test_onlyAdminCanUpdateRecipients() public { + //should revert if the caller is not the admin + vm.expectRevert(ONLY_CALLABLE_BY_OWNER_ERROR); + + //updating a recipient should force the funds to be paid out + updateRewardRecipients(PRIMARY_POOL_ID, getPrimaryRecipients(), FEE_MANAGER); + } + + function test_updateAllRecipientsWithSameAddressAndWeight() public { + //expected recipient amount is 1/4 of the pool deposit + uint256 expectedRecipientAmount = POOL_DEPOSIT_AMOUNT / 4; + + //updating a recipient should force the funds to be paid out + updateRewardRecipients(PRIMARY_POOL_ID, getPrimaryRecipients(), ADMIN); + + //check each recipient received the correct amount + for (uint256 i; i < getPrimaryRecipients().length; i++) { + //check the balance matches the ratio the recipient should have received + assertEq(getAssetBalance(getPrimaryRecipients()[i].addr), expectedRecipientAmount); + } + } + + function test_updatePartialRecipientsWithSameAddressAndWeight() public { + //expected recipient amount is 1/4 of the pool deposit + uint256 expectedRecipientAmount = POOL_DEPOSIT_AMOUNT / 4; + + //get a subset of the recipients + Common.AddressAndWeight[] memory recipients = new Common.AddressAndWeight[](2); + recipients[0] = Common.AddressAndWeight(DEFAULT_RECIPIENT_1, ONE_PERCENT * 25); + recipients[1] = Common.AddressAndWeight(DEFAULT_RECIPIENT_2, ONE_PERCENT * 25); + + //updating a recipient should force the funds to be paid out + updateRewardRecipients(PRIMARY_POOL_ID, recipients, ADMIN); + + //check each recipient received the correct amount + for (uint256 i; i < recipients.length; i++) { + //check the balance matches the ratio the recipient should have received + assertEq(getAssetBalance(recipients[i].addr), expectedRecipientAmount); + } + + //the reward manager should still have half remaining funds + assertEq(getAssetBalance(address(rewardManager)), POOL_DEPOSIT_AMOUNT / 2); + } + + function test_updateRecipientWithNewZeroAddress() public { + //create a new array to hold the existing recipients plus a new zero address + Common.AddressAndWeight[] memory recipients = new Common.AddressAndWeight[](getPrimaryRecipients().length + 1); + + //add all the existing recipients + for (uint256 i; i < getPrimaryRecipients().length; i++) { + recipients[i] = getPrimaryRecipients()[i]; + } + //add a new address to the primary recipients + recipients[recipients.length - 1] = Common.AddressAndWeight(address(0), 0); + + //should revert if the recipient is a zero address + vm.expectRevert(INVALID_ADDRESS_ERROR_SELECTOR); + + //update the recipients with invalid address + updateRewardRecipients(PRIMARY_POOL_ID, recipients, ADMIN); + } + + function test_updateRecipientsContainsDuplicateRecipients() public { + //create a new array to hold the existing recipients + Common.AddressAndWeight[] memory recipients = new Common.AddressAndWeight[](getPrimaryRecipients().length * 2); + + //add all the existing recipients + for (uint256 i; i < getPrimaryRecipients().length; i++) { + recipients[i] = getPrimaryRecipients()[i]; + } + //add all the existing recipients again + for (uint256 i; i < getPrimaryRecipients().length; i++) { + recipients[i + getPrimaryRecipients().length] = getPrimaryRecipients()[i]; + } + + //should revert as the list contains a duplicate + vm.expectRevert(INVALID_ADDRESS_ERROR_SELECTOR); + + //update the recipients with the duplicate addresses + updateRewardRecipients(PRIMARY_POOL_ID, recipients, ADMIN); + } + + function test_updateRecipientsToDifferentSet() public { + //create a list of containing recipients from the primary configured set, and new recipients + Common.AddressAndWeight[] memory recipients = new Common.AddressAndWeight[](getPrimaryRecipients().length + 4); + for (uint256 i; i < getPrimaryRecipients().length; i++) { + //copy the recipient and set the weight to 0 which implies the recipient is being replaced + recipients[i] = Common.AddressAndWeight(getPrimaryRecipients()[i].addr, 0); + } + + //add the new recipients individually + recipients[4] = Common.AddressAndWeight(DEFAULT_RECIPIENT_5, ONE_PERCENT * 25); + recipients[5] = Common.AddressAndWeight(DEFAULT_RECIPIENT_6, ONE_PERCENT * 25); + recipients[6] = Common.AddressAndWeight(DEFAULT_RECIPIENT_7, ONE_PERCENT * 25); + recipients[7] = Common.AddressAndWeight(DEFAULT_RECIPIENT_8, ONE_PERCENT * 25); + + //updating a recipient should force the funds to be paid out for the primary recipients + updateRewardRecipients(PRIMARY_POOL_ID, recipients, ADMIN); + } + + function test_updateRecipientsToDifferentPartialSet() public { + //create a list of containing recipients from the primary configured set, and new recipients + Common.AddressAndWeight[] memory recipients = new Common.AddressAndWeight[](getPrimaryRecipients().length + 2); + for (uint256 i; i < getPrimaryRecipients().length; i++) { + //copy the recipient and set the weight to 0 which implies the recipient is being replaced + recipients[i] = Common.AddressAndWeight(getPrimaryRecipients()[i].addr, 0); + } + + //add the new recipients individually + recipients[4] = Common.AddressAndWeight(DEFAULT_RECIPIENT_5, FIFTY_PERCENT); + recipients[5] = Common.AddressAndWeight(DEFAULT_RECIPIENT_6, FIFTY_PERCENT); + + //updating a recipient should force the funds to be paid out for the primary recipients + updateRewardRecipients(PRIMARY_POOL_ID, recipients, ADMIN); + } + + function test_updateRecipientsToDifferentLargerSet() public { + //create a list of containing recipients from the primary configured set, and new recipients + Common.AddressAndWeight[] memory recipients = new Common.AddressAndWeight[](getPrimaryRecipients().length + 5); + for (uint256 i; i < getPrimaryRecipients().length; i++) { + //copy the recipient and set the weight to 0 which implies the recipient is being replaced + recipients[i] = Common.AddressAndWeight(getPrimaryRecipients()[i].addr, 0); + } + + //add the new recipients individually + recipients[4] = Common.AddressAndWeight(DEFAULT_RECIPIENT_5, TEN_PERCENT * 2); + recipients[5] = Common.AddressAndWeight(DEFAULT_RECIPIENT_6, TEN_PERCENT * 2); + recipients[6] = Common.AddressAndWeight(DEFAULT_RECIPIENT_7, TEN_PERCENT * 2); + recipients[7] = Common.AddressAndWeight(DEFAULT_RECIPIENT_8, TEN_PERCENT * 2); + recipients[8] = Common.AddressAndWeight(DEFAULT_RECIPIENT_9, TEN_PERCENT * 2); + + //updating a recipient should force the funds to be paid out for the primary recipients + updateRewardRecipients(PRIMARY_POOL_ID, recipients, ADMIN); + } + + function test_updateRecipientsUpdateAndRemoveExistingForLargerSet() public { + //create a list of containing recipients from the primary configured set, and new recipients + Common.AddressAndWeight[] memory recipients = new Common.AddressAndWeight[](9); + + //update the existing recipients + recipients[0] = Common.AddressAndWeight(getPrimaryRecipients()[0].addr, 0); + recipients[1] = Common.AddressAndWeight(getPrimaryRecipients()[1].addr, 0); + recipients[2] = Common.AddressAndWeight(getPrimaryRecipients()[2].addr, TEN_PERCENT * 3); + recipients[3] = Common.AddressAndWeight(getPrimaryRecipients()[3].addr, TEN_PERCENT * 3); + + //add the new recipients individually + recipients[4] = Common.AddressAndWeight(DEFAULT_RECIPIENT_5, TEN_PERCENT); + recipients[5] = Common.AddressAndWeight(DEFAULT_RECIPIENT_6, TEN_PERCENT); + recipients[6] = Common.AddressAndWeight(DEFAULT_RECIPIENT_7, TEN_PERCENT); + recipients[7] = Common.AddressAndWeight(DEFAULT_RECIPIENT_8, TEN_PERCENT); + recipients[8] = Common.AddressAndWeight(DEFAULT_RECIPIENT_9, TEN_PERCENT); + + //should revert as the weight does not equal 100% + vm.expectRevert(INVALID_WEIGHT_ERROR_SELECTOR); + + //updating a recipient should force the funds to be paid out for the primary recipients + updateRewardRecipients(PRIMARY_POOL_ID, recipients, ADMIN); + } + + function test_updateRecipientsUpdateAndRemoveExistingForSmallerSet() public { + //create a list of containing recipients from the primary configured set, and new recipients + Common.AddressAndWeight[] memory recipients = new Common.AddressAndWeight[](5); + + //update the existing recipients + recipients[0] = Common.AddressAndWeight(getPrimaryRecipients()[0].addr, 0); + recipients[1] = Common.AddressAndWeight(getPrimaryRecipients()[1].addr, 0); + recipients[2] = Common.AddressAndWeight(getPrimaryRecipients()[2].addr, TEN_PERCENT * 3); + recipients[3] = Common.AddressAndWeight(getPrimaryRecipients()[3].addr, TEN_PERCENT * 2); + + //add the new recipients individually + recipients[4] = Common.AddressAndWeight(DEFAULT_RECIPIENT_5, TEN_PERCENT * 5); + + //updating a recipient should force the funds to be paid out for the primary recipients + updateRewardRecipients(PRIMARY_POOL_ID, recipients, ADMIN); + } + + function test_updateRecipientsToDifferentSetWithInvalidWeights() public { + //create a list of containing recipients from the primary configured set, and new recipients + Common.AddressAndWeight[] memory recipients = new Common.AddressAndWeight[](getPrimaryRecipients().length + 2); + for (uint256 i; i < getPrimaryRecipients().length; i++) { + //copy the recipient and set the weight to 0 which implies the recipient is being replaced + recipients[i] = Common.AddressAndWeight(getPrimaryRecipients()[i].addr, 0); + } + + //add the new recipients individually + recipients[4] = Common.AddressAndWeight(DEFAULT_RECIPIENT_5, TEN_PERCENT * 5); + recipients[5] = Common.AddressAndWeight(DEFAULT_RECIPIENT_6, TEN_PERCENT); + + //should revert as the weight will not equal 100% + vm.expectRevert(INVALID_WEIGHT_ERROR_SELECTOR); + + //updating a recipient should force the funds to be paid out for the primary recipients + updateRewardRecipients(PRIMARY_POOL_ID, recipients, ADMIN); + } + + function test_updatePartialRecipientsToSubset() public { + //create a list of containing recipients from the primary configured set, and new recipients + Common.AddressAndWeight[] memory recipients = new Common.AddressAndWeight[](4); + recipients[0] = Common.AddressAndWeight(DEFAULT_RECIPIENT_1, 0); + recipients[1] = Common.AddressAndWeight(DEFAULT_RECIPIENT_2, 0); + recipients[2] = Common.AddressAndWeight(DEFAULT_RECIPIENT_3, TEN_PERCENT * 5); + recipients[3] = Common.AddressAndWeight(DEFAULT_RECIPIENT_4, TEN_PERCENT * 5); + + //updating a recipient should force the funds to be paid out for the primary recipients + updateRewardRecipients(PRIMARY_POOL_ID, recipients, ADMIN); + } + + function test_updatePartialRecipientsWithUnderWeightSet() public { + //create a list of containing recipients from the primary configured set, and new recipients + Common.AddressAndWeight[] memory recipients = new Common.AddressAndWeight[](4); + recipients[0] = Common.AddressAndWeight(DEFAULT_RECIPIENT_1, TEN_PERCENT); + recipients[1] = Common.AddressAndWeight(DEFAULT_RECIPIENT_2, TEN_PERCENT); + recipients[2] = Common.AddressAndWeight(DEFAULT_RECIPIENT_3, TEN_PERCENT); + recipients[3] = Common.AddressAndWeight(DEFAULT_RECIPIENT_4, TEN_PERCENT); + + //should revert as the new weights exceed the previous weights being replaced + vm.expectRevert(INVALID_WEIGHT_ERROR_SELECTOR); + + //updating a recipient should force the funds to be paid out for the primary recipients + updateRewardRecipients(PRIMARY_POOL_ID, recipients, ADMIN); + } + + function test_updatePartialRecipientsWithExcessiveWeight() public { + //create a list of containing recipients from the primary configured set, and new recipients + Common.AddressAndWeight[] memory recipients = new Common.AddressAndWeight[](4); + recipients[0] = Common.AddressAndWeight(DEFAULT_RECIPIENT_1, TEN_PERCENT); + recipients[1] = Common.AddressAndWeight(DEFAULT_RECIPIENT_2, TEN_PERCENT); + recipients[2] = Common.AddressAndWeight(DEFAULT_RECIPIENT_3, TEN_PERCENT); + recipients[3] = Common.AddressAndWeight(DEFAULT_RECIPIENT_4, POOL_SCALAR); + + //should revert as the new weights exceed the previous weights being replaced + vm.expectRevert(INVALID_WEIGHT_ERROR_SELECTOR); + + //updating a recipient should force the funds to be paid out for the primary recipients + updateRewardRecipients(PRIMARY_POOL_ID, recipients, ADMIN); + } + + function test_updateRecipientWeights() public { + //expected recipient amount is 1/4 of the pool deposit for original recipients + uint256 expectedRecipientAmount = POOL_DEPOSIT_AMOUNT / 4; + + //create a list of containing recipients from the primary configured set with their new weights + Common.AddressAndWeight[] memory recipients = new Common.AddressAndWeight[](4); + recipients[0] = Common.AddressAndWeight(DEFAULT_RECIPIENT_1, TEN_PERCENT); + recipients[1] = Common.AddressAndWeight(DEFAULT_RECIPIENT_2, TEN_PERCENT); + recipients[2] = Common.AddressAndWeight(DEFAULT_RECIPIENT_3, TEN_PERCENT * 3); + recipients[3] = Common.AddressAndWeight(DEFAULT_RECIPIENT_4, TEN_PERCENT * 5); + + //updating a recipient should force the funds to be paid out for the primary recipients + updateRewardRecipients(PRIMARY_POOL_ID, recipients, ADMIN); + + //check each recipient received the correct amount + for (uint256 i; i < recipients.length; i++) { + //check the balance matches the ratio the recipient should have received + assertEq(getAssetBalance(recipients[i].addr), expectedRecipientAmount); + } + + //the reward manager should have no funds remaining + assertEq(getAssetBalance(address(rewardManager)), 0); + + //add more funds to the pool to check new distribution + addFundsToPool(PRIMARY_POOL_ID, getAsset(POOL_DEPOSIT_AMOUNT), FEE_MANAGER); + + //loop each user and claim the rewards + for (uint256 i; i < recipients.length; i++) { + //claim the rewards for this recipient + claimRewards(PRIMARY_POOL_ARRAY, recipients[i].addr); + } + + //manually check the balance of each recipient + assertEq( + getAssetBalance(DEFAULT_RECIPIENT_1), + (POOL_DEPOSIT_AMOUNT * TEN_PERCENT) / POOL_SCALAR + expectedRecipientAmount + ); + assertEq( + getAssetBalance(DEFAULT_RECIPIENT_2), + (POOL_DEPOSIT_AMOUNT * TEN_PERCENT) / POOL_SCALAR + expectedRecipientAmount + ); + assertEq( + getAssetBalance(DEFAULT_RECIPIENT_3), + (POOL_DEPOSIT_AMOUNT * TEN_PERCENT * 3) / POOL_SCALAR + expectedRecipientAmount + ); + assertEq( + getAssetBalance(DEFAULT_RECIPIENT_4), + (POOL_DEPOSIT_AMOUNT * TEN_PERCENT * 5) / POOL_SCALAR + expectedRecipientAmount + ); + } + + function test_partialUpdateRecipientWeights() public { + //expected recipient amount is 1/4 of the pool deposit for original recipients + uint256 expectedRecipientAmount = POOL_DEPOSIT_AMOUNT / 4; + + //create a list of containing recipients from the primary configured set with their new weights + Common.AddressAndWeight[] memory recipients = new Common.AddressAndWeight[](2); + recipients[0] = Common.AddressAndWeight(DEFAULT_RECIPIENT_1, TEN_PERCENT); + recipients[1] = Common.AddressAndWeight(DEFAULT_RECIPIENT_2, TEN_PERCENT * 4); + + //updating a recipient should force the funds to be paid out for the primary recipients + updateRewardRecipients(PRIMARY_POOL_ID, recipients, ADMIN); + + //check each recipient received the correct amount + for (uint256 i; i < recipients.length; i++) { + //check the balance matches the ratio the recipient should have received + assertEq(getAssetBalance(recipients[i].addr), expectedRecipientAmount); + } + + //the reward manager should have half the funds remaining + assertEq(getAssetBalance(address(rewardManager)), POOL_DEPOSIT_AMOUNT / 2); + + //add more funds to the pool to check new distribution + addFundsToPool(PRIMARY_POOL_ID, getAsset(POOL_DEPOSIT_AMOUNT), FEE_MANAGER); + + //loop each user and claim the rewards + for (uint256 i; i < recipients.length; i++) { + //claim the rewards for this recipient + claimRewards(PRIMARY_POOL_ARRAY, recipients[i].addr); + } + + //manually check the balance of each recipient + assertEq( + getAssetBalance(DEFAULT_RECIPIENT_1), + (POOL_DEPOSIT_AMOUNT * TEN_PERCENT) / POOL_SCALAR + expectedRecipientAmount + ); + assertEq( + getAssetBalance(DEFAULT_RECIPIENT_2), + (POOL_DEPOSIT_AMOUNT * TEN_PERCENT * 4) / POOL_SCALAR + expectedRecipientAmount + ); + + //the reward manager should have half the funds remaining + assertEq(getAssetBalance(address(rewardManager)), POOL_DEPOSIT_AMOUNT); + } + + function test_eventIsEmittedUponUpdateRecipients() public { + //expect an emit + vm.expectEmit(); + + //emit the event that is expected to be emitted + emit RewardRecipientsUpdated(PRIMARY_POOL_ID, getPrimaryRecipients()); + + //expected recipient amount is 1/4 of the pool deposit + uint256 expectedRecipientAmount = POOL_DEPOSIT_AMOUNT / 4; + + //updating a recipient should force the funds to be paid out + updateRewardRecipients(PRIMARY_POOL_ID, getPrimaryRecipients(), ADMIN); + + //check each recipient received the correct amount + for (uint256 i; i < getPrimaryRecipients().length; i++) { + //check the balance matches the ratio the recipient should have received + assertEq(getAssetBalance(getPrimaryRecipients()[i].addr), expectedRecipientAmount); + } + } +} + +contract DestinationRewardManagerUpdateRewardRecipientsMultiplePoolsTest is BaseDestinationRewardManagerTest { + uint256 internal constant POOL_DEPOSIT_AMOUNT = 10e18; + + function setUp() public override { + //setup contracts + super.setUp(); + + //create a single pool for these tests + createPrimaryPool(); + createSecondaryPool(); + + //add funds to the pool to be split among the recipients + addFundsToPool(PRIMARY_POOL_ID, getAsset(POOL_DEPOSIT_AMOUNT), FEE_MANAGER); + addFundsToPool(SECONDARY_POOL_ID, getAsset(POOL_DEPOSIT_AMOUNT), FEE_MANAGER); + } + + function getSecondaryRecipients() public override returns (Common.AddressAndWeight[] memory) { + //for testing purposes, the primary and secondary pool to contain the same recipients + return getPrimaryRecipients(); + } + + function test_updatePrimaryRecipientWeights() public { + //expected recipient amount is 1/4 of the pool deposit for original recipients + uint256 expectedRecipientAmount = POOL_DEPOSIT_AMOUNT / 4; + + //create a list of containing recipients from the primary configured set, and new recipients + Common.AddressAndWeight[] memory recipients = new Common.AddressAndWeight[](4); + recipients[0] = Common.AddressAndWeight(DEFAULT_RECIPIENT_1, TEN_PERCENT * 4); + recipients[1] = Common.AddressAndWeight(DEFAULT_RECIPIENT_2, TEN_PERCENT * 4); + recipients[2] = Common.AddressAndWeight(DEFAULT_RECIPIENT_3, TEN_PERCENT); + recipients[3] = Common.AddressAndWeight(DEFAULT_RECIPIENT_4, TEN_PERCENT); + + //updating a recipient should force the funds to be paid out for the primary recipients + updateRewardRecipients(PRIMARY_POOL_ID, recipients, ADMIN); + + //check each recipient received the correct amount + for (uint256 i; i < recipients.length; i++) { + //check the balance matches the ratio the recipient should have received + assertEq(getAssetBalance(recipients[i].addr), expectedRecipientAmount); + } + + //the reward manager should still have the funds for the secondary pool + assertEq(getAssetBalance(address(rewardManager)), POOL_DEPOSIT_AMOUNT); + + //add more funds to the pool to check new distribution + addFundsToPool(PRIMARY_POOL_ID, getAsset(POOL_DEPOSIT_AMOUNT), FEE_MANAGER); + + //claim the rewards for the updated recipients manually + claimRewards(PRIMARY_POOL_ARRAY, recipients[0].addr); + claimRewards(PRIMARY_POOL_ARRAY, recipients[1].addr); + claimRewards(PRIMARY_POOL_ARRAY, recipients[2].addr); + claimRewards(PRIMARY_POOL_ARRAY, recipients[3].addr); + + //check the balance matches the ratio the recipient who were updated should have received + assertEq( + getAssetBalance(recipients[0].addr), + (POOL_DEPOSIT_AMOUNT * TEN_PERCENT * 4) / POOL_SCALAR + expectedRecipientAmount + ); + assertEq( + getAssetBalance(recipients[1].addr), + (POOL_DEPOSIT_AMOUNT * TEN_PERCENT * 4) / POOL_SCALAR + expectedRecipientAmount + ); + assertEq( + getAssetBalance(recipients[2].addr), + (POOL_DEPOSIT_AMOUNT * TEN_PERCENT) / POOL_SCALAR + expectedRecipientAmount + ); + assertEq( + getAssetBalance(recipients[3].addr), + (POOL_DEPOSIT_AMOUNT * TEN_PERCENT) / POOL_SCALAR + expectedRecipientAmount + ); + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/BaseDestinationVerifierTest.t.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/BaseDestinationVerifierTest.t.sol new file mode 100644 index 0000000000..ec3b3a0eed --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/BaseDestinationVerifierTest.t.sol @@ -0,0 +1,347 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {Test} from "forge-std/Test.sol"; +import {DestinationVerifierProxy} from "../../DestinationVerifierProxy.sol"; +import {IERC165} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; +import {IDestinationVerifier} from "../../interfaces/IDestinationVerifier.sol"; +import {IDestinationVerifierProxy} from "../../interfaces/IDestinationVerifierProxy.sol"; +import {DestinationVerifier} from "../../DestinationVerifier.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {AccessControllerInterface} from "../../../../shared/interfaces/AccessControllerInterface.sol"; +import {DestinationFeeManager} from "../../DestinationFeeManager.sol"; +import {Common} from "../../../libraries/Common.sol"; +import {ERC20Mock} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/mocks/ERC20Mock.sol"; +import {WERC20Mock} from "../../../../shared/mocks/WERC20Mock.sol"; +import {DestinationRewardManager} from "../../DestinationRewardManager.sol"; +import {IDestinationRewardManager} from "../../interfaces/IDestinationRewardManager.sol"; + +contract BaseTest is Test { + uint64 internal constant POOL_SCALAR = 1e18; + uint64 internal constant ONE_PERCENT = POOL_SCALAR / 100; + uint256 internal constant MAX_ORACLES = 31; + address internal constant ADMIN = address(1); + address internal constant USER = address(2); + + address internal constant MOCK_VERIFIER_ADDRESS = address(100); + address internal constant ACCESS_CONTROLLER_ADDRESS = address(300); + + uint256 internal constant DEFAULT_REPORT_LINK_FEE = 1e10; + uint256 internal constant DEFAULT_REPORT_NATIVE_FEE = 1e12; + + uint64 internal constant VERIFIER_VERSION = 1; + + uint8 internal constant FAULT_TOLERANCE = 10; + + DestinationVerifierProxy internal s_verifierProxy; + DestinationVerifier internal s_verifier; + DestinationFeeManager internal feeManager; + DestinationRewardManager internal rewardManager; + ERC20Mock internal link; + WERC20Mock internal native; + + struct Signer { + uint256 mockPrivateKey; + address signerAddress; + } + + Signer[MAX_ORACLES] internal s_signers; + bytes32[] internal s_offchaintransmitters; + bool private s_baseTestInitialized; + + struct V3Report { + // The feed ID the report has data for + bytes32 feedId; + // The time the median value was observed on + uint32 observationsTimestamp; + // The timestamp the report is valid from + uint32 validFromTimestamp; + // The link fee + uint192 linkFee; + // The native fee + uint192 nativeFee; + // The expiry of the report + uint32 expiresAt; + // The median value agreed in an OCR round + int192 benchmarkPrice; + // The best bid value agreed in an OCR round + int192 bid; + // The best ask value agreed in an OCR round + int192 ask; + } + + bytes32 internal constant V_MASK = 0x0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; + bytes32 internal constant V1_BITMASK = 0x0001000000000000000000000000000000000000000000000000000000000000; + bytes32 internal constant V2_BITMASK = 0x0002000000000000000000000000000000000000000000000000000000000000; + bytes32 internal constant V3_BITMASK = 0x0003000000000000000000000000000000000000000000000000000000000000; + + bytes32 internal constant INVALID_FEED = keccak256("INVALID"); + uint32 internal constant OBSERVATIONS_TIMESTAMP = 1000; + uint64 internal constant BLOCKNUMBER_LOWER_BOUND = 1000; + uint64 internal constant BLOCKNUMBER_UPPER_BOUND = BLOCKNUMBER_LOWER_BOUND + 5; + int192 internal constant MEDIAN = 1 ether; + int192 internal constant BID = 500000000 gwei; + int192 internal constant ASK = 2 ether; + + //version 0 feeds + bytes32 internal constant FEED_ID = (keccak256("ETH-USD") & V_MASK) | V1_BITMASK; + bytes32 internal constant FEED_ID_2 = (keccak256("LINK-USD") & V_MASK) | V1_BITMASK; + bytes32 internal constant FEED_ID_3 = (keccak256("BTC-USD") & V_MASK) | V1_BITMASK; + + //version 3 feeds + bytes32 internal constant FEED_ID_V3 = (keccak256("ETH-USD") & V_MASK) | V3_BITMASK; + + function _encodeReport(V3Report memory report) internal pure returns (bytes memory) { + return + abi.encode( + report.feedId, + report.observationsTimestamp, + report.validFromTimestamp, + report.nativeFee, + report.linkFee, + report.expiresAt, + report.benchmarkPrice, + report.bid, + report.ask + ); + } + + function _generateSignerSignatures( + bytes memory report, + bytes32[3] memory reportContext, + Signer[] memory signers + ) internal pure returns (bytes32[] memory rawRs, bytes32[] memory rawSs, bytes32 rawVs) { + bytes32[] memory rs = new bytes32[](signers.length); + bytes32[] memory ss = new bytes32[](signers.length); + bytes memory vs = new bytes(signers.length); + + bytes32 hash = keccak256(abi.encodePacked(keccak256(report), reportContext)); + + for (uint256 i = 0; i < signers.length; i++) { + (uint8 v, bytes32 r, bytes32 s) = vm.sign(signers[i].mockPrivateKey, hash); + rs[i] = r; + ss[i] = s; + vs[i] = bytes1(v - 27); + } + return (rs, ss, bytes32(vs)); + } + + function _generateV3EncodedBlob( + V3Report memory report, + bytes32[3] memory reportContext, + Signer[] memory signers + ) internal pure returns (bytes memory) { + bytes memory reportBytes = _encodeReport(report); + (bytes32[] memory rs, bytes32[] memory ss, bytes32 rawVs) = _generateSignerSignatures( + reportBytes, + reportContext, + signers + ); + return abi.encode(reportContext, reportBytes, rs, ss, rawVs); + } + + function _verify(bytes memory payload, address feeAddress, uint256 wrappedNativeValue, address sender) internal { + address originalAddr = msg.sender; + changePrank(sender); + + s_verifierProxy.verify{value: wrappedNativeValue}(payload, abi.encode(feeAddress)); + + changePrank(originalAddr); + } + + function _generateV3Report() internal view returns (V3Report memory) { + return + V3Report({ + feedId: FEED_ID_V3, + observationsTimestamp: OBSERVATIONS_TIMESTAMP, + validFromTimestamp: uint32(block.timestamp), + nativeFee: uint192(DEFAULT_REPORT_NATIVE_FEE), + linkFee: uint192(DEFAULT_REPORT_LINK_FEE), + expiresAt: uint32(block.timestamp), + benchmarkPrice: MEDIAN, + bid: BID, + ask: ASK + }); + } + + function _verifyBulk( + bytes[] memory payload, + address feeAddress, + uint256 wrappedNativeValue, + address sender + ) internal { + address originalAddr = msg.sender; + changePrank(sender); + + s_verifierProxy.verifyBulk{value: wrappedNativeValue}(payload, abi.encode(feeAddress)); + + changePrank(originalAddr); + } + + function _approveLink(address spender, uint256 quantity, address sender) internal { + address originalAddr = msg.sender; + changePrank(sender); + + link.approve(spender, quantity); + changePrank(originalAddr); + } + + function setUp() public virtual { + // BaseTest.setUp is often called multiple times from tests' setUp due to inheritance. + if (s_baseTestInitialized) return; + s_baseTestInitialized = true; + vm.startPrank(ADMIN); + + s_verifierProxy = new DestinationVerifierProxy(); + s_verifier = new DestinationVerifier(address(s_verifierProxy)); + s_verifierProxy.setVerifier(address(s_verifier)); + + // setting up FeeManager and RewardManager + native = new WERC20Mock(); + link = new ERC20Mock("LINK", "LINK", ADMIN, 0); + rewardManager = new DestinationRewardManager(address(link)); + feeManager = new DestinationFeeManager(address(link), address(native), address(s_verifier), address(rewardManager)); + + for (uint256 i; i < MAX_ORACLES; i++) { + uint256 mockPK = i + 1; + s_signers[i].mockPrivateKey = mockPK; + s_signers[i].signerAddress = vm.addr(mockPK); + } + } + + function _getSigners(uint256 numSigners) internal view returns (Signer[] memory) { + Signer[] memory signers = new Signer[](numSigners); + for (uint256 i; i < numSigners; i++) { + signers[i] = s_signers[i]; + } + return signers; + } + + function _getSignerAddresses(Signer[] memory signers) internal pure returns (address[] memory) { + address[] memory signerAddrs = new address[](signers.length); + for (uint256 i = 0; i < signerAddrs.length; i++) { + signerAddrs[i] = signers[i].signerAddress; + } + return signerAddrs; + } + + function _signerAddressAndDonConfigKey(address signer, bytes24 donConfigId) internal pure returns (bytes32) { + return keccak256(abi.encodePacked(signer, donConfigId)); + } + + function _donConfigIdFromConfigData(address[] memory signers, uint8 f) internal pure returns (bytes24) { + Common._quickSort(signers, 0, int256(signers.length - 1)); + bytes24 donConfigId = bytes24(keccak256(abi.encodePacked(signers, f))); + return donConfigId; + } + + function assertReportsEqual(bytes memory response, V3Report memory testReport) public pure { + ( + bytes32 feedId, + uint32 observationsTimestamp, + uint32 validFromTimestamp, + uint192 nativeFee, + uint192 linkFee, + uint32 expiresAt, + int192 benchmarkPrice, + int192 bid, + int192 ask + ) = abi.decode(response, (bytes32, uint32, uint32, uint192, uint192, uint32, int192, int192, int192)); + assertEq(feedId, testReport.feedId); + assertEq(observationsTimestamp, testReport.observationsTimestamp); + assertEq(validFromTimestamp, testReport.validFromTimestamp); + assertEq(expiresAt, testReport.expiresAt); + assertEq(benchmarkPrice, testReport.benchmarkPrice); + assertEq(bid, testReport.bid); + assertEq(ask, testReport.ask); + assertEq(linkFee, testReport.linkFee); + assertEq(nativeFee, testReport.nativeFee); + } + + function _approveNative(address spender, uint256 quantity, address sender) internal { + address originalAddr = msg.sender; + changePrank(sender); + + native.approve(spender, quantity); + changePrank(originalAddr); + } +} + +contract VerifierWithFeeManager is BaseTest { + uint256 internal constant DEFAULT_LINK_MINT_QUANTITY = 100 ether; + uint256 internal constant DEFAULT_NATIVE_MINT_QUANTITY = 100 ether; + + function setUp() public virtual override { + BaseTest.setUp(); + + s_verifierProxy.setVerifier(address(s_verifier)); + s_verifier.setFeeManager(address(feeManager)); + rewardManager.addFeeManager(address(feeManager)); + + //mint some tokens to the user + link.mint(USER, DEFAULT_LINK_MINT_QUANTITY); + native.mint(USER, DEFAULT_NATIVE_MINT_QUANTITY); + vm.deal(USER, DEFAULT_NATIVE_MINT_QUANTITY); + + //mint some link tokens to the feeManager pool + link.mint(address(feeManager), DEFAULT_REPORT_LINK_FEE); + } +} + +contract MultipleVerifierWithMultipleFeeManagers is BaseTest { + uint256 internal constant DEFAULT_LINK_MINT_QUANTITY = 100 ether; + uint256 internal constant DEFAULT_NATIVE_MINT_QUANTITY = 100 ether; + + DestinationVerifier internal s_verifier2; + DestinationVerifier internal s_verifier3; + + DestinationVerifierProxy internal s_verifierProxy2; + DestinationVerifierProxy internal s_verifierProxy3; + + DestinationFeeManager internal feeManager2; + + function setUp() public virtual override { + /* + - Sets up 3 verifiers + - Sets up 2 Fee managers, wire the fee managers and verifiers + - Sets up a Reward Manager which can be used by both fee managers + */ + BaseTest.setUp(); + + s_verifierProxy2 = new DestinationVerifierProxy(); + s_verifierProxy3 = new DestinationVerifierProxy(); + + s_verifier2 = new DestinationVerifier(address(s_verifierProxy2)); + s_verifier3 = new DestinationVerifier(address(s_verifierProxy3)); + + s_verifierProxy2.setVerifier(address(s_verifier2)); + s_verifierProxy3.setVerifier(address(s_verifier3)); + + feeManager2 = new DestinationFeeManager( + address(link), + address(native), + address(s_verifier), + address(rewardManager) + ); + + s_verifier.setFeeManager(address(feeManager)); + s_verifier2.setFeeManager(address(feeManager)); + s_verifier3.setFeeManager(address(feeManager2)); + + // this is already set in the base contract + // feeManager.addVerifier(address(s_verifier)); + feeManager.addVerifier(address(s_verifier2)); + feeManager2.addVerifier(address(s_verifier3)); + + rewardManager.addFeeManager(address(feeManager)); + rewardManager.addFeeManager(address(feeManager2)); + + //mint some tokens to the user + link.mint(USER, DEFAULT_LINK_MINT_QUANTITY); + native.mint(USER, DEFAULT_NATIVE_MINT_QUANTITY); + vm.deal(USER, DEFAULT_NATIVE_MINT_QUANTITY); + + //mint some link tokens to the feeManager pool + link.mint(address(feeManager), DEFAULT_REPORT_LINK_FEE); + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierInterfacesTest.t.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierInterfacesTest.t.sol new file mode 100644 index 0000000000..d4772ba185 --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierInterfacesTest.t.sol @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {Test} from "forge-std/Test.sol"; +import {VerifierWithFeeManager} from "./BaseDestinationVerifierTest.t.sol"; +import {DestinationVerifier} from "../../../v0.4.0/DestinationVerifier.sol"; +import {DestinationVerifierProxy} from "../../../v0.4.0/DestinationVerifierProxy.sol"; +import {AccessControllerInterface} from "../../../../shared/interfaces/AccessControllerInterface.sol"; +import {IDestinationFeeManager} from "../../../v0.4.0/interfaces/IDestinationFeeManager.sol"; +import {IDestinationRewardManager} from "../../../v0.4.0/interfaces/IDestinationRewardManager.sol"; +import {IDestinationVerifierProxy} from "../../../v0.4.0/interfaces/IDestinationVerifierProxy.sol"; +import {Common} from "../../../libraries/Common.sol"; +import {BaseTest} from "./BaseDestinationVerifierTest.t.sol"; +import {IERC20} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC20.sol"; + +/* +This test checks the interfaces of destination verifier matches the expectations. +The code here comes from this example: + +https://docs.chain.link/chainlink-automation/guides/streams-lookup + +*/ + +// Custom interfaces for IVerifierProxy and IFeeManager +interface IVerifierProxy { + /** + * @notice Verifies that the data encoded has been signed. + * correctly by routing to the correct verifier, and bills the user if applicable. + * @param payload The encoded data to be verified, including the signed + * report. + * @param parameterPayload Fee metadata for billing. For the current implementation this is just the abi-encoded fee token ERC-20 address. + * @return verifierResponse The encoded report from the verifier. + */ + function verify( + bytes calldata payload, + bytes calldata parameterPayload + ) external payable returns (bytes memory verifierResponse); + + function s_feeManager() external view returns (IDestinationFeeManager); +} + +interface IFeeManager { + /** + * @notice Calculates the fee and reward associated with verifying a report, including discounts for subscribers. + * This function assesses the fee and reward for report verification, applying a discount for recognized subscriber addresses. + * @param subscriber The address attempting to verify the report. A discount is applied if this address + * is recognized as a subscriber. + * @param unverifiedReport The report data awaiting verification. The content of this report is used to + * determine the base fee and reward, before considering subscriber discounts. + * @param quoteAddress The payment token address used for quoting fees and rewards. + * @return fee The fee assessed for verifying the report, with subscriber discounts applied where applicable. + * @return reward The reward allocated to the caller for successfully verifying the report. + * @return totalDiscount The total discount amount deducted from the fee for subscribers. + */ + function getFeeAndReward( + address subscriber, + bytes memory unverifiedReport, + address quoteAddress + ) external returns (Common.Asset memory, Common.Asset memory, uint256); + + function i_linkAddress() external view returns (address); + + function i_nativeAddress() external view returns (address); + + function i_rewardManager() external view returns (address); +} + +//Tests +// https://docs.chain.link/chainlink-automation/guides/streams-lookup +contract VerifierInterfacesTest is VerifierWithFeeManager { + address internal constant DEFAULT_RECIPIENT_1 = address(uint160(uint256(keccak256("DEFAULT_RECIPIENT_1")))); + + IVerifierProxy public verifier; + V3Report internal s_testReport; + + address public FEE_ADDRESS; + string public constant DATASTREAMS_FEEDLABEL = "feedIDs"; + string public constant DATASTREAMS_QUERYLABEL = "timestamp"; + int192 public last_retrieved_price; + bytes internal signedReport; + bytes32[3] internal s_reportContext; + uint8 MINIMAL_FAULT_TOLERANCE = 2; + + function setUp() public virtual override { + VerifierWithFeeManager.setUp(); + s_reportContext[0] = bytes32(abi.encode(uint32(5), uint8(1))); + Signer[] memory signers = _getSigners(MAX_ORACLES); + + s_testReport = V3Report({ + feedId: FEED_ID_V3, + observationsTimestamp: OBSERVATIONS_TIMESTAMP, + validFromTimestamp: uint32(block.timestamp), + nativeFee: uint192(DEFAULT_REPORT_NATIVE_FEE), + linkFee: uint192(DEFAULT_REPORT_LINK_FEE), + expiresAt: uint32(block.timestamp), + benchmarkPrice: MEDIAN, + bid: BID, + ask: ASK + }); + address[] memory signerAddrs = _getSignerAddresses(signers); + Common.AddressAndWeight[] memory weights = new Common.AddressAndWeight[](1); + weights[0] = Common.AddressAndWeight(DEFAULT_RECIPIENT_1, ONE_PERCENT * 100); + s_verifier.setConfig(signerAddrs, MINIMAL_FAULT_TOLERANCE, weights); + signedReport = _generateV3EncodedBlob(s_testReport, s_reportContext, signers); + + verifier = IVerifierProxy(address(s_verifierProxy)); + } + + function test_DestinationContractInterfaces() public { + bytes memory unverifiedReport = signedReport; + + (, bytes memory reportData) = abi.decode(unverifiedReport, (bytes32[3], bytes)); + + // Report verification fees + IFeeManager feeManager = IFeeManager(address(verifier.s_feeManager())); + IDestinationRewardManager rewardManager = IDestinationRewardManager(address(feeManager.i_rewardManager())); + + address feeTokenAddress = feeManager.i_linkAddress(); + (Common.Asset memory fee, , ) = feeManager.getFeeAndReward(address(this), reportData, feeTokenAddress); + + // Approve rewardManager to spend this contract's balance in fees + _approveLink(address(rewardManager), fee.amount, USER); + _verify(unverifiedReport, address(feeTokenAddress), 0, USER); + + assertEq(link.balanceOf(USER), DEFAULT_LINK_MINT_QUANTITY - fee.amount); + assertEq(link.balanceOf(address(rewardManager)), fee.amount); + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierProxyTest.t.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierProxyTest.t.sol new file mode 100644 index 0000000000..c93c9dc6d9 --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierProxyTest.t.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {BaseTest} from "./BaseDestinationVerifierTest.t.sol"; +import {DestinationVerifierProxy} from "../../../v0.4.0/DestinationVerifierProxy.sol"; +import {DestinationVerifier} from "../../../v0.4.0/DestinationVerifier.sol"; +import {DestinationFeeManager} from "../../../v0.4.0/DestinationFeeManager.sol"; +import {IERC165} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; + +contract DestinationVerifierProxyInitializeVerifierTest is BaseTest { + function test_setVerifierCalledByNoOwner() public { + address STRANGER = address(999); + changePrank(STRANGER); + vm.expectRevert(bytes("Only callable by owner")); + s_verifierProxy.setVerifier(address(s_verifier)); + } + + function test_setVerifierWhichDoesntHonourInterface() public { + vm.expectRevert(abi.encodeWithSelector(DestinationVerifierProxy.VerifierInvalid.selector, address(rewardManager))); + s_verifierProxy.setVerifier(address(rewardManager)); + } + + function test_setVerifierOk() public { + s_verifierProxy.setVerifier(address(s_verifier)); + assertEq(s_verifierProxy.s_feeManager(), s_verifier.s_feeManager()); + assertEq(s_verifierProxy.s_accessController(), s_verifier.s_accessController()); + } + + function test_correctlySetsTheOwner() public { + DestinationVerifierProxy proxy = new DestinationVerifierProxy(); + assertEq(proxy.owner(), ADMIN); + } + + function test_correctlySetsVersion() public view { + string memory version = s_verifierProxy.typeAndVersion(); + assertEq(version, "DestinationVerifierProxy 1.0.0"); + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierRemoveLatestConfigTest.t.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierRemoveLatestConfigTest.t.sol new file mode 100644 index 0000000000..6309efc995 --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierRemoveLatestConfigTest.t.sol @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {BaseTest} from "./BaseDestinationVerifierTest.t.sol"; +import {DestinationVerifier} from "../../../v0.4.0/DestinationVerifier.sol"; +import {DestinationRewardManager} from "../../../v0.4.0/DestinationRewardManager.sol"; +import {Common} from "../../../libraries/Common.sol"; + +contract DestinationVerifierSetConfigTest is BaseTest { + bytes32[3] internal s_reportContext; + V3Report internal s_testReport; + + function setUp() public virtual override { + BaseTest.setUp(); + s_reportContext[0] = bytes32(abi.encode(uint32(5), uint8(1))); + } + + function test_removeLatestConfigWhenNoConfigShouldFail() public { + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.DonConfigDoesNotExist.selector)); + s_verifier.removeLatestConfig(); + } + + function test_removeLatestConfig() public { + /* + This test sets two Configs: Config A and Config B. + - it removes and readds config B multiple times while trying Config A verifications + */ + Signer[] memory signers = _getSigners(MAX_ORACLES); + + uint8 MINIMAL_FAULT_TOLERANCE = 2; + BaseTest.Signer[] memory signersA = new BaseTest.Signer[](7); + signersA[0] = signers[0]; + signersA[1] = signers[1]; + signersA[2] = signers[2]; + signersA[3] = signers[3]; + signersA[4] = signers[4]; + signersA[5] = signers[5]; + signersA[6] = signers[6]; + + // ConfigA + address[] memory signersAddrA = _getSignerAddresses(signersA); + s_verifier.setConfig(signersAddrA, MINIMAL_FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + vm.warp(block.timestamp + 10); + V3Report memory s_testReportA = V3Report({ + feedId: FEED_ID_V3, + observationsTimestamp: OBSERVATIONS_TIMESTAMP, + validFromTimestamp: uint32(block.timestamp), + nativeFee: uint192(DEFAULT_REPORT_NATIVE_FEE), + linkFee: uint192(DEFAULT_REPORT_LINK_FEE), + expiresAt: uint32(block.timestamp), + benchmarkPrice: MEDIAN, + bid: BID, + ask: ASK + }); + + vm.warp(block.timestamp + 100); + // Config B + BaseTest.Signer[] memory signersB = new BaseTest.Signer[](7); + // signers in ConfigA + signersB[0] = signers[8]; + signersB[1] = signers[9]; + signersB[2] = signers[10]; + signersB[3] = signers[11]; + signersB[4] = signers[12]; + signersB[5] = signers[13]; + signersB[6] = signers[14]; + address[] memory signersAddrsB = _getSignerAddresses(signersB); + s_verifier.setConfig(signersAddrsB, MINIMAL_FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + + V3Report memory s_testReportB = V3Report({ + feedId: FEED_ID_V3, + observationsTimestamp: OBSERVATIONS_TIMESTAMP, + validFromTimestamp: uint32(block.timestamp), + nativeFee: uint192(DEFAULT_REPORT_NATIVE_FEE), + linkFee: uint192(DEFAULT_REPORT_LINK_FEE), + expiresAt: uint32(block.timestamp), + benchmarkPrice: MEDIAN, + bid: BID, + ask: ASK + }); + + BaseTest.Signer[] memory reportSignersA = new BaseTest.Signer[](3); + reportSignersA[0] = signers[0]; + reportSignersA[1] = signers[1]; + reportSignersA[2] = signers[2]; + + BaseTest.Signer[] memory reportSignersB = new BaseTest.Signer[](3); + reportSignersB[0] = signers[8]; + reportSignersB[1] = signers[9]; + reportSignersB[2] = signers[10]; + + bytes memory signedReportA = _generateV3EncodedBlob(s_testReportA, s_reportContext, reportSignersA); + bytes memory signedReportB = _generateV3EncodedBlob(s_testReportB, s_reportContext, reportSignersB); + + // verifying should work + s_verifierProxy.verify(signedReportA, abi.encode(native)); + s_verifierProxy.verify(signedReportB, abi.encode(native)); + + s_verifier.removeLatestConfig(); + + // this should remove the latest config, so ConfigA should be able to verify reports still + s_verifierProxy.verify(signedReportA, abi.encode(native)); + // this report cannot be verified any longer because ConfigB is not there + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.BadVerification.selector)); + s_verifierProxy.verify(signedReportB, abi.encode(native)); + + // since ConfigB is removed we should be able to set it again with no errors + s_verifier.setConfig(signersAddrsB, MINIMAL_FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + + // we should be able to remove ConfigB + s_verifier.removeLatestConfig(); + // removing configA + s_verifier.removeLatestConfig(); + + // verifigny should fail + // verifying should work + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.BadVerification.selector)); + s_verifierProxy.verify(signedReportA, abi.encode(native)); + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.BadVerification.selector)); + s_verifierProxy.verify(signedReportB, abi.encode(native)); + + // removing again should fail. no other configs exist + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.DonConfigDoesNotExist.selector)); + s_verifier.removeLatestConfig(); + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierSetAccessControllerTest.t.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierSetAccessControllerTest.t.sol new file mode 100644 index 0000000000..d40b674f23 --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierSetAccessControllerTest.t.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {BaseTest} from "./BaseDestinationVerifierTest.t.sol"; + +contract DestinationVerifierSetAccessControllerTest is BaseTest { + event AccessControllerSet(address oldAccessController, address newAccessController); + + function test_revertsIfCalledByNonOwner() public { + vm.expectRevert("Only callable by owner"); + + changePrank(USER); + s_verifier.setAccessController(ACCESS_CONTROLLER_ADDRESS); + } + + function test_successfullySetsNewAccessController() public { + s_verifier.setAccessController(ACCESS_CONTROLLER_ADDRESS); + address ac = s_verifier.s_accessController(); + assertEq(ac, ACCESS_CONTROLLER_ADDRESS); + } + + function test_successfullySetsNewAccessControllerIsEmpty() public { + s_verifier.setAccessController(address(0)); + address ac = s_verifier.s_accessController(); + assertEq(ac, address(0)); + } + + function test_emitsTheCorrectEvent() public { + vm.expectEmit(true, false, false, false); + emit AccessControllerSet(address(0), ACCESS_CONTROLLER_ADDRESS); + s_verifier.setAccessController(ACCESS_CONTROLLER_ADDRESS); + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierSetConfigTest.t.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierSetConfigTest.t.sol new file mode 100644 index 0000000000..f6e5fd1f21 --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierSetConfigTest.t.sol @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {BaseTest} from "./BaseDestinationVerifierTest.t.sol"; +import {DestinationVerifier} from "../../../v0.4.0/DestinationVerifier.sol"; +import {DestinationRewardManager} from "../../../v0.4.0/DestinationRewardManager.sol"; +import {Common} from "../../../libraries/Common.sol"; + +contract DestinationVerifierSetConfigTest is BaseTest { + function setUp() public virtual override { + BaseTest.setUp(); + } + + function test_revertsIfCalledByNonOwner() public { + vm.expectRevert("Only callable by owner"); + Signer[] memory signers = _getSigners(MAX_ORACLES); + changePrank(USER); + s_verifier.setConfig(_getSignerAddresses(signers), FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + } + + function test_revertsIfSetWithTooManySigners() public { + address[] memory signers = new address[](MAX_ORACLES + 1); + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.ExcessSigners.selector, signers.length, MAX_ORACLES)); + s_verifier.setConfig(signers, FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + } + + function test_revertsIfFaultToleranceIsZero() public { + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.FaultToleranceMustBePositive.selector)); + Signer[] memory signers = _getSigners(MAX_ORACLES); + s_verifier.setConfig(_getSignerAddresses(signers), 0, new Common.AddressAndWeight[](0)); + } + + function test_revertsIfNotEnoughSigners() public { + address[] memory signers = new address[](2); + signers[0] = address(1000); + signers[1] = address(1001); + + vm.expectRevert( + abi.encodeWithSelector(DestinationVerifier.InsufficientSigners.selector, signers.length, FAULT_TOLERANCE * 3 + 1) + ); + s_verifier.setConfig(signers, FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + } + + function test_revertsIfDuplicateSigners() public { + Signer[] memory signers = _getSigners(MAX_ORACLES); + address[] memory signerAddrs = _getSignerAddresses(signers); + signerAddrs[0] = signerAddrs[1]; + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.NonUniqueSignatures.selector)); + s_verifier.setConfig(signerAddrs, FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + } + + function test_revertsIfSignerContainsZeroAddress() public { + Signer[] memory signers = _getSigners(MAX_ORACLES); + address[] memory signerAddrs = _getSignerAddresses(signers); + signerAddrs[0] = address(0); + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.ZeroAddress.selector)); + s_verifier.setConfig(signerAddrs, FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + } + + function test_donConfigIdIsSameForSignersInDifferentOrder() public { + Signer[] memory signers = _getSigners(MAX_ORACLES); + address[] memory signerAddrs = _getSignerAddresses(signers); + + bytes24 expectedDonConfigId = _donConfigIdFromConfigData(signerAddrs, FAULT_TOLERANCE); + + s_verifier.setConfig(signerAddrs, FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + + address temp = signerAddrs[0]; + signerAddrs[0] = signerAddrs[1]; + signerAddrs[1] = temp; + + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.DonConfigAlreadyExists.selector, expectedDonConfigId)); + + s_verifier.setConfig(signerAddrs, FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + } + + function test_NoDonConfigAlreadyExists() public { + Signer[] memory signers = _getSigners(MAX_ORACLES); + address[] memory signerAddrs = _getSignerAddresses(signers); + + s_verifier.setConfig(signerAddrs, FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + + // testing adding same set of Signers but different FAULT_TOLERENCE does not result in DonConfigAlreadyExists revert + s_verifier.setConfig(signerAddrs, FAULT_TOLERANCE - 1, new Common.AddressAndWeight[](0)); + + // testing adding a different set of Signers with same FAULT_TOLERENCE does not result in DonConfigAlreadyExists revert + address[] memory signerAddrsMinusOne = new address[](signerAddrs.length - 1); + for (uint256 i = 0; i < signerAddrs.length - 1; i++) { + signerAddrsMinusOne[i] = signerAddrs[i]; + } + s_verifier.setConfig(signerAddrsMinusOne, FAULT_TOLERANCE - 1, new Common.AddressAndWeight[](0)); + } + + function test_addressesAndWeightsDoNotProduceSideEffectsInDonConfigIds() public { + Signer[] memory signers = _getSigners(MAX_ORACLES); + address[] memory signerAddrs = _getSignerAddresses(signers); + + s_verifier.setConfig(signerAddrs, FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + + bytes24 expectedDonConfigId = _donConfigIdFromConfigData(signerAddrs, FAULT_TOLERANCE); + + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.DonConfigAlreadyExists.selector, expectedDonConfigId)); + + // Same call to setConfig with different addressAndWeights do not entail a new DonConfigID + // Resulting in a DonConfigAlreadyExists error + Common.AddressAndWeight[] memory weights = new Common.AddressAndWeight[](1); + weights[0] = Common.AddressAndWeight(signers[0].signerAddress, 1); + s_verifier.setConfig(signerAddrs, FAULT_TOLERANCE, weights); + } + + function test_setConfigActiveUnknownDonConfigId() public { + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.DonConfigDoesNotExist.selector)); + s_verifier.setConfigActive(3, true); + } + + function test_setConfigWithActivationTime() public { + // simple case setting a config with specific activation time + Signer[] memory signers = _getSigners(MAX_ORACLES); + address[] memory signerAddrs = _getSignerAddresses(signers); + uint32 activationTime = 10; + s_verifier.setConfigWithActivationTime( + signerAddrs, + FAULT_TOLERANCE, + new Common.AddressAndWeight[](0), + activationTime + ); + } + + function test_setConfigWithActivationTimeNoFutureTimeShouldFail() public { + // calling setConfigWithActivationTime with a future timestamp should fail + Signer[] memory signers = _getSigners(MAX_ORACLES); + address[] memory signerAddrs = _getSignerAddresses(signers); + uint32 activationTime = uint32(block.timestamp) + 100; + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.BadActivationTime.selector)); + s_verifier.setConfigWithActivationTime( + signerAddrs, + FAULT_TOLERANCE, + new Common.AddressAndWeight[](0), + activationTime + ); + } + + function test_setConfigWithActivationTimeEarlierThanLatestConfigShouldFail() public { + // setting a config older than the latest current config should fail + Signer[] memory signers = _getSigners(MAX_ORACLES); + address[] memory signerAddrs = _getSignerAddresses(signers); + uint32 oldActivationTime = uint32(block.timestamp) - 1; + // sets a config with timestamp = block.timestamp + s_verifier.setConfig(signerAddrs, FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + // setting a config with ealier timestamp retuls in failure + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.BadActivationTime.selector)); + s_verifier.setConfigWithActivationTime( + signerAddrs, + FAULT_TOLERANCE - 1, + new Common.AddressAndWeight[](0), + oldActivationTime + ); + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierSetFeeManagerTest.t.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierSetFeeManagerTest.t.sol new file mode 100644 index 0000000000..fdf75d6845 --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierSetFeeManagerTest.t.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {BaseTest} from "./BaseDestinationVerifierTest.t.sol"; +import {DestinationVerifier} from "../../../v0.4.0/DestinationVerifier.sol"; + +contract VerifierSetAccessControllerTest is BaseTest { + event FeeManagerSet(address oldFeeManager, address newFeeManager); + + function test_revertsIfCalledByNonOwner() public { + vm.expectRevert("Only callable by owner"); + changePrank(USER); + s_verifier.setFeeManager(address(feeManager)); + } + + function test_successfullySetsNewFeeManager() public { + vm.expectEmit(true, false, false, false); + emit FeeManagerSet(address(0), ACCESS_CONTROLLER_ADDRESS); + s_verifier.setFeeManager(address(feeManager)); + address ac = s_verifier.s_feeManager(); + assertEq(ac, address(feeManager)); + } + + function test_setFeeManagerWhichDoesntHonourInterface() public { + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.FeeManagerInvalid.selector)); + s_verifier.setFeeManager(address(rewardManager)); + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierTest.t.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierTest.t.sol new file mode 100644 index 0000000000..dd157d2a47 --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierTest.t.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {BaseTest} from "./BaseDestinationVerifierTest.t.sol"; +import {DestinationVerifier} from "../../../v0.4.0/DestinationVerifier.sol"; + +contract DestinationVerifierConstructorTest is BaseTest { + bytes32[3] internal s_reportContext; + + function test_revertsIfInitializedWithEmptyVerifierProxy() public { + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.ZeroAddress.selector)); + new DestinationVerifier(address(0)); + } + + function test_typeAndVersion() public { + DestinationVerifier v = new DestinationVerifier(address(s_verifierProxy)); + assertEq(v.owner(), ADMIN); + string memory typeAndVersion = s_verifier.typeAndVersion(); + assertEq(typeAndVersion, "DestinationVerifier 1.0.0"); + } + + function test_falseIfIsNotCorrectInterface() public view { + bool isInterface = s_verifier.supportsInterface(bytes4("abcd")); + assertEq(isInterface, false); + } + + function test_trueIfIsCorrectInterface() public view { + bool isInterface = s_verifier.supportsInterface(DestinationVerifier.verify.selector); + assertEq(isInterface, true); + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierTestBillingReport.t.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierTestBillingReport.t.sol new file mode 100644 index 0000000000..574e169cf2 --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierTestBillingReport.t.sol @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {VerifierWithFeeManager} from "./BaseDestinationVerifierTest.t.sol"; +import {DestinationVerifier} from "../../../v0.4.0/DestinationVerifier.sol"; +import {DestinationVerifierProxy} from "../../../v0.4.0/DestinationVerifierProxy.sol"; +import {AccessControllerInterface} from "../../../../shared/interfaces/AccessControllerInterface.sol"; +import {Common} from "../../../libraries/Common.sol"; + +contract VerifierBillingTests is VerifierWithFeeManager { + bytes32[3] internal s_reportContext; + V3Report internal s_testReportThree; + + function setUp() public virtual override { + VerifierWithFeeManager.setUp(); + s_reportContext[0] = bytes32(abi.encode(uint32(5), uint8(1))); + s_testReportThree = V3Report({ + feedId: FEED_ID_V3, + observationsTimestamp: OBSERVATIONS_TIMESTAMP, + validFromTimestamp: uint32(block.timestamp), + nativeFee: uint192(DEFAULT_REPORT_NATIVE_FEE), + linkFee: uint192(DEFAULT_REPORT_LINK_FEE), + expiresAt: uint32(block.timestamp), + benchmarkPrice: MEDIAN, + bid: BID, + ask: ASK + }); + } + + function test_verifyWithLinkV3Report() public { + Signer[] memory signers = _getSigners(MAX_ORACLES); + address[] memory signerAddrs = _getSignerAddresses(signers); + Common.AddressAndWeight[] memory weights = new Common.AddressAndWeight[](0); + s_verifier.setConfig(signerAddrs, FAULT_TOLERANCE, weights); + bytes memory signedReport = _generateV3EncodedBlob(s_testReportThree, s_reportContext, signers); + bytes32 expectedDonConfigId = _donConfigIdFromConfigData(signerAddrs, FAULT_TOLERANCE); + + _approveLink(address(rewardManager), DEFAULT_REPORT_LINK_FEE, USER); + _verify(signedReport, address(link), 0, USER); + assertEq(link.balanceOf(USER), DEFAULT_LINK_MINT_QUANTITY - DEFAULT_REPORT_LINK_FEE); + + // internal state checks + assertEq(feeManager.s_linkDeficit(expectedDonConfigId), 0); + assertEq(rewardManager.s_totalRewardRecipientFees(expectedDonConfigId), DEFAULT_REPORT_LINK_FEE); + assertEq(link.balanceOf(address(rewardManager)), DEFAULT_REPORT_LINK_FEE); + } + + function test_verifyWithNativeERC20() public { + Signer[] memory signers = _getSigners(MAX_ORACLES); + address[] memory signerAddrs = _getSignerAddresses(signers); + Common.AddressAndWeight[] memory weights = new Common.AddressAndWeight[](1); + weights[0] = Common.AddressAndWeight(signerAddrs[0], ONE_PERCENT * 100); + + s_verifier.setConfig(signerAddrs, FAULT_TOLERANCE, weights); + bytes memory signedReport = _generateV3EncodedBlob( + s_testReportThree, + s_reportContext, + _getSigners(FAULT_TOLERANCE + 1) + ); + _approveNative(address(feeManager), DEFAULT_REPORT_NATIVE_FEE, USER); + _verify(signedReport, address(native), 0, USER); + assertEq(native.balanceOf(USER), DEFAULT_NATIVE_MINT_QUANTITY - DEFAULT_REPORT_NATIVE_FEE); + + assertEq(link.balanceOf(address(rewardManager)), DEFAULT_REPORT_LINK_FEE); + } + + function test_verifyWithNativeUnwrapped() public { + Signer[] memory signers = _getSigners(MAX_ORACLES); + address[] memory signerAddrs = _getSignerAddresses(signers); + Common.AddressAndWeight[] memory weights = new Common.AddressAndWeight[](0); + + s_verifier.setConfig(signerAddrs, FAULT_TOLERANCE, weights); + bytes memory signedReport = _generateV3EncodedBlob( + s_testReportThree, + s_reportContext, + _getSigners(FAULT_TOLERANCE + 1) + ); + _verify(signedReport, address(native), DEFAULT_REPORT_NATIVE_FEE, USER); + + assertEq(USER.balance, DEFAULT_NATIVE_MINT_QUANTITY - DEFAULT_REPORT_NATIVE_FEE); + assertEq(address(feeManager).balance, 0); + } + + function test_verifyWithNativeUnwrappedReturnsChange() public { + Signer[] memory signers = _getSigners(MAX_ORACLES); + address[] memory signerAddrs = _getSignerAddresses(signers); + Common.AddressAndWeight[] memory weights = new Common.AddressAndWeight[](0); + + s_verifier.setConfig(signerAddrs, FAULT_TOLERANCE, weights); + bytes memory signedReport = _generateV3EncodedBlob( + s_testReportThree, + s_reportContext, + _getSigners(FAULT_TOLERANCE + 1) + ); + + _verify(signedReport, address(native), DEFAULT_REPORT_NATIVE_FEE * 2, USER); + assertEq(USER.balance, DEFAULT_NATIVE_MINT_QUANTITY - DEFAULT_REPORT_NATIVE_FEE); + assertEq(address(feeManager).balance, 0); + } +} + +contract DestinationVerifierBulkVerifyBillingReport is VerifierWithFeeManager { + uint256 internal constant NUMBERS_OF_REPORTS = 5; + + bytes32[3] internal s_reportContext; + + function setUp() public virtual override { + VerifierWithFeeManager.setUp(); + // setting a DonConfig we can reuse in the rest of tests + s_reportContext[0] = bytes32(abi.encode(uint32(5), uint8(1))); + Signer[] memory signers = _getSigners(MAX_ORACLES); + address[] memory signerAddrs = _getSignerAddresses(signers); + Common.AddressAndWeight[] memory weights = new Common.AddressAndWeight[](0); + s_verifier.setConfig(signerAddrs, FAULT_TOLERANCE, weights); + } + + function test_verifyWithBulkLink() public { + bytes memory signedReport = _generateV3EncodedBlob( + _generateV3Report(), + s_reportContext, + _getSigners(FAULT_TOLERANCE + 1) + ); + + bytes[] memory signedReports = new bytes[](NUMBERS_OF_REPORTS); + for (uint256 i = 0; i < NUMBERS_OF_REPORTS; i++) { + signedReports[i] = signedReport; + } + + _approveLink(address(rewardManager), DEFAULT_REPORT_LINK_FEE * NUMBERS_OF_REPORTS, USER); + + _verifyBulk(signedReports, address(link), 0, USER); + + assertEq(link.balanceOf(USER), DEFAULT_LINK_MINT_QUANTITY - DEFAULT_REPORT_LINK_FEE * NUMBERS_OF_REPORTS); + assertEq(link.balanceOf(address(rewardManager)), DEFAULT_REPORT_LINK_FEE * NUMBERS_OF_REPORTS); + } + + function test_verifyWithBulkNative() public { + bytes memory signedReport = _generateV3EncodedBlob( + _generateV3Report(), + s_reportContext, + _getSigners(FAULT_TOLERANCE + 1) + ); + + bytes[] memory signedReports = new bytes[](NUMBERS_OF_REPORTS); + for (uint256 i = 0; i < NUMBERS_OF_REPORTS; i++) { + signedReports[i] = signedReport; + } + + _approveNative(address(feeManager), DEFAULT_REPORT_NATIVE_FEE * NUMBERS_OF_REPORTS, USER); + _verifyBulk(signedReports, address(native), 0, USER); + assertEq(native.balanceOf(USER), DEFAULT_NATIVE_MINT_QUANTITY - DEFAULT_REPORT_NATIVE_FEE * NUMBERS_OF_REPORTS); + } + + function test_verifyWithBulkNativeUnwrapped() public { + bytes memory signedReport = _generateV3EncodedBlob( + _generateV3Report(), + s_reportContext, + _getSigners(FAULT_TOLERANCE + 1) + ); + + bytes[] memory signedReports = new bytes[](NUMBERS_OF_REPORTS); + for (uint256 i; i < NUMBERS_OF_REPORTS; i++) { + signedReports[i] = signedReport; + } + + _verifyBulk(signedReports, address(native), 200 * DEFAULT_REPORT_NATIVE_FEE * NUMBERS_OF_REPORTS, USER); + + assertEq(USER.balance, DEFAULT_NATIVE_MINT_QUANTITY - DEFAULT_REPORT_NATIVE_FEE * 5); + assertEq(address(feeManager).balance, 0); + } + + function test_verifyWithBulkNativeUnwrappedReturnsChange() public { + bytes memory signedReport = _generateV3EncodedBlob( + _generateV3Report(), + s_reportContext, + _getSigners(FAULT_TOLERANCE + 1) + ); + + bytes[] memory signedReports = new bytes[](NUMBERS_OF_REPORTS); + for (uint256 i = 0; i < NUMBERS_OF_REPORTS; i++) { + signedReports[i] = signedReport; + } + + _verifyBulk(signedReports, address(native), DEFAULT_REPORT_NATIVE_FEE * (NUMBERS_OF_REPORTS * 2), USER); + + assertEq(USER.balance, DEFAULT_NATIVE_MINT_QUANTITY - DEFAULT_REPORT_NATIVE_FEE * NUMBERS_OF_REPORTS); + assertEq(address(feeManager).balance, 0); + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierTestRewards.t.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierTestRewards.t.sol new file mode 100644 index 0000000000..8ca954b8ca --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierTestRewards.t.sol @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {VerifierWithFeeManager} from "./BaseDestinationVerifierTest.t.sol"; +import {DestinationVerifier} from "../../../v0.4.0/DestinationVerifier.sol"; +import {DestinationVerifierProxy} from "../../../v0.4.0/DestinationVerifierProxy.sol"; +import {AccessControllerInterface} from "../../../../shared/interfaces/AccessControllerInterface.sol"; +import {Common} from "../../../libraries/Common.sol"; + +contract VerifierBillingTests is VerifierWithFeeManager { + uint8 MINIMAL_FAULT_TOLERANCE = 2; + address internal constant DEFAULT_RECIPIENT_1 = address(uint160(uint256(keccak256("DEFAULT_RECIPIENT_1")))); + address internal constant DEFAULT_RECIPIENT_2 = address(uint160(uint256(keccak256("DEFAULT_RECIPIENT_2")))); + address internal constant DEFAULT_RECIPIENT_3 = address(uint160(uint256(keccak256("DEFAULT_RECIPIENT_3")))); + address internal constant DEFAULT_RECIPIENT_4 = address(uint160(uint256(keccak256("DEFAULT_RECIPIENT_4")))); + address internal constant DEFAULT_RECIPIENT_5 = address(uint160(uint256(keccak256("DEFAULT_RECIPIENT_5")))); + address internal constant DEFAULT_RECIPIENT_6 = address(uint160(uint256(keccak256("DEFAULT_RECIPIENT_6")))); + address internal constant DEFAULT_RECIPIENT_7 = address(uint160(uint256(keccak256("DEFAULT_RECIPIENT_7")))); + + function payRecipients(bytes32 poolId, address[] memory recipients, address sender) public { + //record the current address and switch to the recipient + address originalAddr = msg.sender; + changePrank(sender); + + //pay the recipients + rewardManager.payRecipients(poolId, recipients); + + //change back to the original address + changePrank(originalAddr); + } + + bytes32[3] internal s_reportContext; + V3Report internal s_testReport; + + function setUp() public virtual override { + VerifierWithFeeManager.setUp(); + s_reportContext[0] = bytes32(abi.encode(uint32(5), uint8(1))); + s_testReport = generateReportAtTimestamp(block.timestamp); + } + + function generateReportAtTimestamp(uint256 timestamp) public pure returns (V3Report memory) { + return + V3Report({ + feedId: FEED_ID_V3, + observationsTimestamp: OBSERVATIONS_TIMESTAMP, + validFromTimestamp: uint32(timestamp), + nativeFee: uint192(DEFAULT_REPORT_NATIVE_FEE), + linkFee: uint192(DEFAULT_REPORT_LINK_FEE), + // ask michael about this expires at, is it usually set at what blocks + expiresAt: uint32(timestamp) + 500, + benchmarkPrice: MEDIAN, + bid: BID, + ask: ASK + }); + } + + function getRecipientAndWeightsGroup2() public pure returns (Common.AddressAndWeight[] memory, address[] memory) { + address[] memory recipients = new address[](4); + recipients[0] = DEFAULT_RECIPIENT_4; + recipients[1] = DEFAULT_RECIPIENT_5; + recipients[2] = DEFAULT_RECIPIENT_6; + recipients[3] = DEFAULT_RECIPIENT_7; + + Common.AddressAndWeight[] memory weights = new Common.AddressAndWeight[](4); + //init each recipient with even weights. 2500 = 25% of pool + weights[0] = Common.AddressAndWeight(DEFAULT_RECIPIENT_4, POOL_SCALAR / 4); + weights[1] = Common.AddressAndWeight(DEFAULT_RECIPIENT_5, POOL_SCALAR / 4); + weights[2] = Common.AddressAndWeight(DEFAULT_RECIPIENT_6, POOL_SCALAR / 4); + weights[3] = Common.AddressAndWeight(DEFAULT_RECIPIENT_7, POOL_SCALAR / 4); + return (weights, recipients); + } + + function getRecipientAndWeightsGroup1() public pure returns (Common.AddressAndWeight[] memory, address[] memory) { + address[] memory recipients = new address[](4); + recipients[0] = DEFAULT_RECIPIENT_1; + recipients[1] = DEFAULT_RECIPIENT_2; + recipients[2] = DEFAULT_RECIPIENT_3; + recipients[3] = DEFAULT_RECIPIENT_4; + + Common.AddressAndWeight[] memory weights = new Common.AddressAndWeight[](4); + //init each recipient with even weights. 2500 = 25% of pool + weights[0] = Common.AddressAndWeight(DEFAULT_RECIPIENT_1, POOL_SCALAR / 4); + weights[1] = Common.AddressAndWeight(DEFAULT_RECIPIENT_2, POOL_SCALAR / 4); + weights[2] = Common.AddressAndWeight(DEFAULT_RECIPIENT_3, POOL_SCALAR / 4); + weights[3] = Common.AddressAndWeight(DEFAULT_RECIPIENT_4, POOL_SCALAR / 4); + return (weights, recipients); + } + + function test_rewardsAreDistributedAccordingToWeights() public { + /* + Simple test verifying that rewards are distributed according to address and weights + associated to the DonConfig used to verify the report + */ + Signer[] memory signers = _getSigners(MAX_ORACLES); + address[] memory signerAddrs = _getSignerAddresses(signers); + Common.AddressAndWeight[] memory weights = new Common.AddressAndWeight[](1); + weights[0] = Common.AddressAndWeight(DEFAULT_RECIPIENT_1, ONE_PERCENT * 100); + s_verifier.setConfig(signerAddrs, FAULT_TOLERANCE, weights); + bytes memory signedReport = _generateV3EncodedBlob(s_testReport, s_reportContext, signers); + bytes32 expectedDonConfigId = _donConfigIdFromConfigData(signerAddrs, FAULT_TOLERANCE); + + _approveLink(address(rewardManager), DEFAULT_REPORT_LINK_FEE, USER); + _verify(signedReport, address(link), 0, USER); + assertEq(link.balanceOf(USER), DEFAULT_LINK_MINT_QUANTITY - DEFAULT_REPORT_LINK_FEE); + + // internal state checks + assertEq(feeManager.s_linkDeficit(expectedDonConfigId), 0); + assertEq(rewardManager.s_totalRewardRecipientFees(expectedDonConfigId), DEFAULT_REPORT_LINK_FEE); + assertEq(link.balanceOf(address(rewardManager)), DEFAULT_REPORT_LINK_FEE); + + // check the recipients are paid according to weights + address[] memory recipients = new address[](1); + recipients[0] = DEFAULT_RECIPIENT_1; + payRecipients(expectedDonConfigId, recipients, ADMIN); + assertEq(link.balanceOf(recipients[0]), DEFAULT_REPORT_LINK_FEE); + assertEq(link.balanceOf(address(rewardManager)), 0); + } + + function test_rewardsAreDistributedAccordingToWeightsMultipleWeigths() public { + /* + Rewards are distributed according to AddressAndWeight's + associated to the DonConfig used to verify the report: + - multiple recipients + - multiple verifications + */ + Signer[] memory signers = _getSigners(MAX_ORACLES); + address[] memory signerAddrs = _getSignerAddresses(signers); + (Common.AddressAndWeight[] memory weights, address[] memory recipients) = getRecipientAndWeightsGroup1(); + s_verifier.setConfig(signerAddrs, FAULT_TOLERANCE, weights); + + bytes memory signedReport = _generateV3EncodedBlob(s_testReport, s_reportContext, signers); + bytes32 expectedDonConfigId = _donConfigIdFromConfigData(signerAddrs, FAULT_TOLERANCE); + + uint256 number_of_reports_verified = 10; + + for (uint256 i = 0; i < number_of_reports_verified; i++) { + _approveLink(address(rewardManager), DEFAULT_REPORT_LINK_FEE, USER); + _verify(signedReport, address(link), 0, USER); + } + + uint256 expected_pool_amount = DEFAULT_REPORT_LINK_FEE * number_of_reports_verified; + + //each recipient should receive 1/4 of the pool + uint256 expectedRecipientAmount = expected_pool_amount / 4; + + payRecipients(expectedDonConfigId, recipients, ADMIN); + for (uint256 i = 0; i < recipients.length; i++) { + // checking each recipient got rewards as set by the weights + assertEq(link.balanceOf(recipients[i]), expectedRecipientAmount); + } + // checking nothing left in reward manager + assertEq(link.balanceOf(address(rewardManager)), 0); + } + + function test_rewardsAreDistributedAccordingToWeightsUsingHistoricalConfigs() public { + /* + Verifies that reports verified with historical give rewards according to the verifying config AddressAndWeight. + - Sets two Configs: ConfigA and ConfigB, These two Configs have different Recipient and Weights + - Verifies a couple reports with each config + - Pays recipients + - Asserts expected rewards for each recipient + */ + + Signer[] memory signers = _getSigners(10); + address[] memory signerAddrs = _getSignerAddresses(signers); + + (Common.AddressAndWeight[] memory weights, address[] memory recipients) = getRecipientAndWeightsGroup1(); + + // Create ConfigA + s_verifier.setConfig(signerAddrs, MINIMAL_FAULT_TOLERANCE, weights); + vm.warp(block.timestamp + 100); + + V3Report memory testReportAtT1 = generateReportAtTimestamp(block.timestamp); + bytes memory signedReportT1 = _generateV3EncodedBlob(testReportAtT1, s_reportContext, signers); + bytes32 expectedDonConfigIdA = _donConfigIdFromConfigData(signerAddrs, MINIMAL_FAULT_TOLERANCE); + + uint256 number_of_reports_verified = 2; + + // advancing the blocktimestamp so we can test verifying with configs + vm.warp(block.timestamp + 100); + + Signer[] memory signers2 = _getSigners(12); + address[] memory signerAddrs2 = _getSignerAddresses(signers2); + (Common.AddressAndWeight[] memory weights2, address[] memory recipients2) = getRecipientAndWeightsGroup2(); + + // Create ConfigB + s_verifier.setConfig(signerAddrs2, MINIMAL_FAULT_TOLERANCE, weights2); + bytes32 expectedDonConfigIdB = _donConfigIdFromConfigData(signerAddrs2, MINIMAL_FAULT_TOLERANCE); + + V3Report memory testReportAtT2 = generateReportAtTimestamp(block.timestamp); + + // verifiying using ConfigA (report with Old timestamp) + for (uint256 i = 0; i < number_of_reports_verified; i++) { + _approveLink(address(rewardManager), DEFAULT_REPORT_LINK_FEE, USER); + _verify(signedReportT1, address(link), 0, USER); + } + + // verifying using ConfigB (report with new timestamp) + for (uint256 i = 0; i < number_of_reports_verified; i++) { + _approveLink(address(rewardManager), DEFAULT_REPORT_LINK_FEE, USER); + _verify(_generateV3EncodedBlob(testReportAtT2, s_reportContext, signers2), address(link), 0, USER); + } + + uint256 expected_pool_amount = DEFAULT_REPORT_LINK_FEE * number_of_reports_verified; + assertEq(rewardManager.s_totalRewardRecipientFees(expectedDonConfigIdA), expected_pool_amount); + assertEq(rewardManager.s_totalRewardRecipientFees(expectedDonConfigIdB), expected_pool_amount); + + // check the recipients are paid according to weights + payRecipients(expectedDonConfigIdA, recipients, ADMIN); + + for (uint256 i = 0; i < recipients.length; i++) { + // //each recipient should receive 1/4 of the pool + assertEq(link.balanceOf(recipients[i]), expected_pool_amount / 4); + } + + payRecipients(expectedDonConfigIdB, recipients2, ADMIN); + + for (uint256 i = 1; i < recipients2.length; i++) { + // //each recipient should receive 1/4 of the pool + assertEq(link.balanceOf(recipients2[i]), expected_pool_amount / 4); + } + + // this recipient was part of the two config weights + assertEq(link.balanceOf(recipients2[0]), (expected_pool_amount / 4) * 2); + assertEq(link.balanceOf(address(rewardManager)), 0); + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierTestRewardsMultiVefifierFeeManager.t.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierTestRewardsMultiVefifierFeeManager.t.sol new file mode 100644 index 0000000000..6a90cbf373 --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierTestRewardsMultiVefifierFeeManager.t.sol @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {MultipleVerifierWithMultipleFeeManagers} from "./BaseDestinationVerifierTest.t.sol"; +import {DestinationVerifier} from "../../../v0.4.0/DestinationVerifier.sol"; +import {DestinationVerifierProxy} from "../../../v0.4.0/DestinationVerifierProxy.sol"; +import {AccessControllerInterface} from "../../../../shared/interfaces/AccessControllerInterface.sol"; +import {Common} from "../../../libraries/Common.sol"; + +contract MultiVerifierBillingTests is MultipleVerifierWithMultipleFeeManagers { + uint8 MINIMAL_FAULT_TOLERANCE = 2; + address internal constant DEFAULT_RECIPIENT_1 = address(uint160(uint256(keccak256("DEFAULT_RECIPIENT_1")))); + address internal constant DEFAULT_RECIPIENT_2 = address(uint160(uint256(keccak256("DEFAULT_RECIPIENT_2")))); + address internal constant DEFAULT_RECIPIENT_3 = address(uint160(uint256(keccak256("DEFAULT_RECIPIENT_3")))); + address internal constant DEFAULT_RECIPIENT_4 = address(uint160(uint256(keccak256("DEFAULT_RECIPIENT_4")))); + address internal constant DEFAULT_RECIPIENT_5 = address(uint160(uint256(keccak256("DEFAULT_RECIPIENT_5")))); + address internal constant DEFAULT_RECIPIENT_6 = address(uint160(uint256(keccak256("DEFAULT_RECIPIENT_6")))); + address internal constant DEFAULT_RECIPIENT_7 = address(uint160(uint256(keccak256("DEFAULT_RECIPIENT_7")))); + + bytes32[3] internal s_reportContext; + V3Report internal s_testReport; + + function setUp() public virtual override { + MultipleVerifierWithMultipleFeeManagers.setUp(); + s_reportContext[0] = bytes32(abi.encode(uint32(5), uint8(1))); + s_testReport = generateReportAtTimestamp(block.timestamp); + } + + function _verify( + DestinationVerifierProxy proxy, + bytes memory payload, + address feeAddress, + uint256 wrappedNativeValue, + address sender + ) internal { + address originalAddr = msg.sender; + changePrank(sender); + + proxy.verify{value: wrappedNativeValue}(payload, abi.encode(feeAddress)); + + changePrank(originalAddr); + } + + function generateReportAtTimestamp(uint256 timestamp) public pure returns (V3Report memory) { + return + V3Report({ + feedId: FEED_ID_V3, + observationsTimestamp: OBSERVATIONS_TIMESTAMP, + validFromTimestamp: uint32(timestamp), + nativeFee: uint192(DEFAULT_REPORT_NATIVE_FEE), + linkFee: uint192(DEFAULT_REPORT_LINK_FEE), + // ask michael about this expires at, is it usually set at what blocks + expiresAt: uint32(timestamp) + 500, + benchmarkPrice: MEDIAN, + bid: BID, + ask: ASK + }); + } + + function payRecipients(bytes32 poolId, address[] memory recipients, address sender) public { + //record the current address and switch to the recipient + address originalAddr = msg.sender; + changePrank(sender); + + //pay the recipients + rewardManager.payRecipients(poolId, recipients); + + //change back to the original address + changePrank(originalAddr); + } + + function test_multipleFeeManagersAndVerifiers() public { + /* + In this test we got: + - three verifiers (verifier, verifier2, verifier3). + - two fee managers (feeManager, feeManager2) + - one reward manager + + we glue: + - feeManager is used by verifier1 and verifier2 + - feeManager is used by verifier3 + - Rewardmanager is used by feeManager and feeManager2 + + In this test we do verificatons via verifier1, verifier2 and verifier3 and check that rewards are set accordingly + + */ + Signer[] memory signers = _getSigners(MAX_ORACLES); + address[] memory signerAddrs = _getSignerAddresses(signers); + Common.AddressAndWeight[] memory weights = new Common.AddressAndWeight[](1); + weights[0] = Common.AddressAndWeight(DEFAULT_RECIPIENT_1, ONE_PERCENT * 100); + + Common.AddressAndWeight[] memory weights2 = new Common.AddressAndWeight[](1); + weights2[0] = Common.AddressAndWeight(DEFAULT_RECIPIENT_2, ONE_PERCENT * 100); + + Common.AddressAndWeight[] memory weights3 = new Common.AddressAndWeight[](1); + weights3[0] = Common.AddressAndWeight(DEFAULT_RECIPIENT_3, ONE_PERCENT * 100); + + s_verifier.setConfig(signerAddrs, FAULT_TOLERANCE, weights); + s_verifier2.setConfig(signerAddrs, MINIMAL_FAULT_TOLERANCE, weights2); + s_verifier3.setConfig(signerAddrs, MINIMAL_FAULT_TOLERANCE + 1, weights3); + bytes memory signedReport = _generateV3EncodedBlob(s_testReport, s_reportContext, signers); + bytes32 expectedDonConfigID = _donConfigIdFromConfigData(signerAddrs, FAULT_TOLERANCE); + bytes32 expectedDonConfigID2 = _donConfigIdFromConfigData(signerAddrs, MINIMAL_FAULT_TOLERANCE); + bytes32 expectedDonConfigID3 = _donConfigIdFromConfigData(signerAddrs, MINIMAL_FAULT_TOLERANCE + 1); + + _approveLink(address(rewardManager), DEFAULT_REPORT_LINK_FEE, USER); + _verify(s_verifierProxy, signedReport, address(link), 0, USER); + assertEq(link.balanceOf(USER), DEFAULT_LINK_MINT_QUANTITY - DEFAULT_REPORT_LINK_FEE); + + // internal state checks + assertEq(feeManager.s_linkDeficit(expectedDonConfigID), 0); + assertEq(rewardManager.s_totalRewardRecipientFees(expectedDonConfigID), DEFAULT_REPORT_LINK_FEE); + assertEq(link.balanceOf(address(rewardManager)), DEFAULT_REPORT_LINK_FEE); + + // check the recipients are paid according to weights + // These rewards happened through verifier1 and feeManager1 + address[] memory recipients = new address[](1); + recipients[0] = DEFAULT_RECIPIENT_1; + payRecipients(expectedDonConfigID, recipients, ADMIN); + assertEq(link.balanceOf(recipients[0]), DEFAULT_REPORT_LINK_FEE); + assertEq(link.balanceOf(address(rewardManager)), 0); + + // these rewards happaned through verifier2 and feeManager1 + address[] memory recipients2 = new address[](1); + recipients2[0] = DEFAULT_RECIPIENT_2; + _approveLink(address(rewardManager), DEFAULT_REPORT_LINK_FEE, USER); + _verify(s_verifierProxy2, signedReport, address(link), 0, USER); + payRecipients(expectedDonConfigID2, recipients2, ADMIN); + assertEq(link.balanceOf(recipients2[0]), DEFAULT_REPORT_LINK_FEE); + assertEq(link.balanceOf(address(rewardManager)), 0); + + // these rewards happened through verifier3 and feeManager2 + address[] memory recipients3 = new address[](1); + recipients3[0] = DEFAULT_RECIPIENT_3; + _approveLink(address(rewardManager), DEFAULT_REPORT_LINK_FEE, USER); + _verify(s_verifierProxy3, signedReport, address(link), 0, USER); + payRecipients(expectedDonConfigID3, recipients3, ADMIN); + assertEq(link.balanceOf(recipients3[0]), DEFAULT_REPORT_LINK_FEE); + assertEq(link.balanceOf(address(rewardManager)), 0); + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierVerifyBulkTest.t.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierVerifyBulkTest.t.sol new file mode 100644 index 0000000000..1c57295bae --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierVerifyBulkTest.t.sol @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {BaseTest} from "./BaseDestinationVerifierTest.t.sol"; +import {DestinationVerifier} from "../../../v0.4.0/DestinationVerifier.sol"; +import {DestinationVerifierProxy} from "../../../v0.4.0/DestinationVerifierProxy.sol"; +import {AccessControllerInterface} from "../../../../shared/interfaces/AccessControllerInterface.sol"; +import {Common} from "../../../libraries/Common.sol"; + +contract VerifierVerifyBulkTest is BaseTest { + bytes32[3] internal s_reportContext; + V3Report internal s_testReportThree; + + function setUp() public virtual override { + BaseTest.setUp(); + s_reportContext[0] = bytes32(abi.encode(uint32(5), uint8(1))); + + s_testReportThree = V3Report({ + feedId: FEED_ID_V3, + observationsTimestamp: OBSERVATIONS_TIMESTAMP, + validFromTimestamp: uint32(block.timestamp), + nativeFee: uint192(DEFAULT_REPORT_NATIVE_FEE), + linkFee: uint192(DEFAULT_REPORT_LINK_FEE), + expiresAt: uint32(block.timestamp), + benchmarkPrice: MEDIAN, + bid: BID, + ask: ASK + }); + } + + function test_revertsVerifyBulkIfNoAccess() public { + vm.mockCall( + ACCESS_CONTROLLER_ADDRESS, + abi.encodeWithSelector(AccessControllerInterface.hasAccess.selector, USER), + abi.encode(false) + ); + bytes memory signedReport = _generateV3EncodedBlob( + s_testReportThree, + s_reportContext, + _getSigners(FAULT_TOLERANCE + 1) + ); + + bytes[] memory signedReports = new bytes[](2); + signedReports[0] = signedReport; + signedReports[1] = signedReport; + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.AccessForbidden.selector)); + changePrank(USER); + s_verifier.verifyBulk(signedReports, abi.encode(native), msg.sender); + } + + function test_verifyBulkSingleCaseWithSingleConfig() public { + Signer[] memory signers = _getSigners(MAX_ORACLES); + + uint8 MINIMAL_FAULT_TOLERANCE = 2; + BaseTest.Signer[] memory signersSubset1 = new BaseTest.Signer[](7); + signersSubset1[0] = signers[0]; + signersSubset1[1] = signers[1]; + signersSubset1[2] = signers[2]; + signersSubset1[3] = signers[3]; + signersSubset1[4] = signers[4]; + signersSubset1[5] = signers[5]; + signersSubset1[6] = signers[6]; + + address[] memory signersAddrSubset1 = _getSignerAddresses(signersSubset1); + // Config1 + s_verifier.setConfig(signersAddrSubset1, MINIMAL_FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + + V3Report memory report = V3Report({ + feedId: FEED_ID_V3, + observationsTimestamp: OBSERVATIONS_TIMESTAMP, + validFromTimestamp: uint32(block.timestamp), + nativeFee: uint192(DEFAULT_REPORT_NATIVE_FEE), + linkFee: uint192(DEFAULT_REPORT_LINK_FEE), + expiresAt: uint32(block.timestamp), + benchmarkPrice: MEDIAN, + bid: BID, + ask: ASK + }); + + BaseTest.Signer[] memory reportSigners = new BaseTest.Signer[](3); + reportSigners[0] = signers[0]; + reportSigners[1] = signers[1]; + reportSigners[2] = signers[2]; + + bytes[] memory signedReports = new bytes[](10); + + bytes memory signedReport = _generateV3EncodedBlob(report, s_reportContext, reportSigners); + + for (uint256 i = 0; i < signedReports.length; i++) { + signedReports[i] = signedReport; + } + + bytes[] memory verifierResponses = s_verifierProxy.verifyBulk(signedReports, abi.encode(native)); + + for (uint256 i = 0; i < verifierResponses.length; i++) { + bytes memory verifierResponse = verifierResponses[i]; + assertReportsEqual(verifierResponse, report); + } + } + + function test_verifyBulkWithSingleConfigOneVerifyFails() public { + Signer[] memory signers = _getSigners(MAX_ORACLES); + + uint8 MINIMAL_FAULT_TOLERANCE = 2; + BaseTest.Signer[] memory signersSubset1 = new BaseTest.Signer[](7); + signersSubset1[0] = signers[0]; + signersSubset1[1] = signers[1]; + signersSubset1[2] = signers[2]; + signersSubset1[3] = signers[3]; + signersSubset1[4] = signers[4]; + signersSubset1[5] = signers[5]; + signersSubset1[6] = signers[6]; + + address[] memory signersAddrSubset1 = _getSignerAddresses(signersSubset1); + // Config1 + s_verifier.setConfig(signersAddrSubset1, MINIMAL_FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + + BaseTest.Signer[] memory reportSigners = new BaseTest.Signer[](3); + reportSigners[0] = signers[0]; + reportSigners[1] = signers[1]; + reportSigners[2] = signers[2]; + + bytes[] memory signedReports = new bytes[](11); + bytes memory signedReport = _generateV3EncodedBlob(s_testReportThree, s_reportContext, reportSigners); + + for (uint256 i = 0; i < 10; i++) { + signedReports[i] = signedReport; + } + + // Making the last report in this batch not verifiable + BaseTest.Signer[] memory reportSigners2 = new BaseTest.Signer[](3); + reportSigners2[0] = signers[30]; + reportSigners2[1] = signers[29]; + reportSigners2[2] = signers[28]; + signedReports[10] = _generateV3EncodedBlob(s_testReportThree, s_reportContext, reportSigners2); + + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.BadVerification.selector)); + s_verifierProxy.verifyBulk(signedReports, abi.encode(native)); + } +} diff --git a/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierVerifyTest.t.sol b/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierVerifyTest.t.sol new file mode 100644 index 0000000000..658bf4f127 --- /dev/null +++ b/contracts/src/v0.8/llo-feeds/v0.4.0/test/verifier/DestinationVerifierVerifyTest.t.sol @@ -0,0 +1,711 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {BaseTest} from "./BaseDestinationVerifierTest.t.sol"; +import {DestinationVerifier} from "../../../v0.4.0/DestinationVerifier.sol"; +import {DestinationVerifierProxy} from "../../../v0.4.0/DestinationVerifierProxy.sol"; +import {AccessControllerInterface} from "../../../../shared/interfaces/AccessControllerInterface.sol"; +import {Common} from "../../../libraries/Common.sol"; + +contract VerifierVerifyTest is BaseTest { + bytes32[3] internal s_reportContext; + V3Report internal s_testReportThree; + + function setUp() public virtual override { + BaseTest.setUp(); + + s_testReportThree = V3Report({ + feedId: FEED_ID_V3, + observationsTimestamp: OBSERVATIONS_TIMESTAMP, + validFromTimestamp: uint32(block.timestamp), + nativeFee: uint192(DEFAULT_REPORT_NATIVE_FEE), + linkFee: uint192(DEFAULT_REPORT_LINK_FEE), + expiresAt: uint32(block.timestamp), + benchmarkPrice: MEDIAN, + bid: BID, + ask: ASK + }); + } + + function test_verifyReport() public { + // Simple use case just setting a config and verifying a report + Signer[] memory signers = _getSigners(MAX_ORACLES); + address[] memory signerAddrs = _getSignerAddresses(signers); + s_reportContext[0] = bytes32(abi.encode(uint32(5), uint8(1))); + + s_verifier.setConfig(signerAddrs, FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + + bytes memory signedReport = _generateV3EncodedBlob(s_testReportThree, s_reportContext, signers); + + bytes memory verifierResponse = s_verifierProxy.verify(signedReport, abi.encode(native)); + assertReportsEqual(verifierResponse, s_testReportThree); + } + + function test_verifyTooglingActiveFlagsDonConfigs() public { + // sets config + Signer[] memory signers = _getSigners(MAX_ORACLES); + address[] memory signerAddrs = _getSignerAddresses(signers); + s_reportContext[0] = bytes32(abi.encode(uint32(5), uint8(1))); + bytes memory signedReport = _generateV3EncodedBlob(s_testReportThree, s_reportContext, signers); + s_verifier.setConfig(signerAddrs, FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + // verifies report + bytes memory verifierResponse = s_verifierProxy.verify(signedReport, abi.encode(native)); + assertReportsEqual(verifierResponse, s_testReportThree); + + // test verifying via a config that is deactivated + s_verifier.setConfigActive(0, false); + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.BadVerification.selector)); + verifierResponse = s_verifierProxy.verify(signedReport, abi.encode(native)); + + // test verifying via a reactivated config + s_verifier.setConfigActive(0, true); + verifierResponse = s_verifierProxy.verify(signedReport, abi.encode(native)); + assertReportsEqual(verifierResponse, s_testReportThree); + } + + function test_failToVerifyReportIfNotEnoughSigners() public { + Signer[] memory signers = _getSigners(MAX_ORACLES); + + uint8 MINIMAL_FAULT_TOLERANCE = 2; + BaseTest.Signer[] memory signersSubset1 = new BaseTest.Signer[](7); + signersSubset1[0] = signers[0]; + signersSubset1[1] = signers[1]; + signersSubset1[2] = signers[2]; + signersSubset1[3] = signers[3]; + signersSubset1[4] = signers[4]; + signersSubset1[5] = signers[5]; + signersSubset1[6] = signers[6]; + address[] memory signersAddrSubset1 = _getSignerAddresses(signersSubset1); + s_verifier.setConfig(signersAddrSubset1, MINIMAL_FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + + // only one signer, signers < MINIMAL_FAULT_TOLERANCE + BaseTest.Signer[] memory signersSubset2 = new BaseTest.Signer[](1); + signersSubset2[0] = signers[4]; + + bytes memory signedReport = _generateV3EncodedBlob(s_testReportThree, s_reportContext, signersSubset2); + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.BadVerification.selector)); + s_verifierProxy.verify(signedReport, abi.encode(native)); + } + + function test_failToVerifyReportIfNoSigners() public { + Signer[] memory signers = _getSigners(MAX_ORACLES); + + uint8 MINIMAL_FAULT_TOLERANCE = 2; + BaseTest.Signer[] memory signersSubset1 = new BaseTest.Signer[](7); + signersSubset1[0] = signers[0]; + signersSubset1[1] = signers[1]; + signersSubset1[2] = signers[2]; + signersSubset1[3] = signers[3]; + signersSubset1[4] = signers[4]; + signersSubset1[5] = signers[5]; + signersSubset1[6] = signers[6]; + address[] memory signersAddrSubset1 = _getSignerAddresses(signersSubset1); + s_verifier.setConfig(signersAddrSubset1, MINIMAL_FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + + // No signers for this report + BaseTest.Signer[] memory signersSubset2 = new BaseTest.Signer[](0); + bytes memory signedReport = _generateV3EncodedBlob(s_testReportThree, s_reportContext, signersSubset2); + + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.NoSigners.selector)); + s_verifierProxy.verify(signedReport, abi.encode(native)); + } + + function test_failToVerifyReportIfDupSigners() public { + Signer[] memory signers = _getSigners(MAX_ORACLES); + + uint8 MINIMAL_FAULT_TOLERANCE = 2; + BaseTest.Signer[] memory signersSubset1 = new BaseTest.Signer[](7); + signersSubset1[0] = signers[0]; + signersSubset1[1] = signers[1]; + signersSubset1[2] = signers[2]; + signersSubset1[3] = signers[3]; + signersSubset1[4] = signers[4]; + signersSubset1[5] = signers[5]; + signersSubset1[6] = signers[6]; + address[] memory signersAddrSubset1 = _getSignerAddresses(signersSubset1); + s_verifier.setConfig(signersAddrSubset1, MINIMAL_FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + // One signer is repeated + BaseTest.Signer[] memory signersSubset2 = new BaseTest.Signer[](4); + signersSubset2[0] = signers[0]; + signersSubset2[1] = signers[1]; + // repeated signers + signersSubset2[2] = signers[2]; + signersSubset2[3] = signers[2]; + + bytes memory signedReport = _generateV3EncodedBlob(s_testReportThree, s_reportContext, signersSubset2); + + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.BadVerification.selector)); + s_verifierProxy.verify(signedReport, abi.encode(native)); + } + + function test_failToVerifyReportIfSignerNotInConfig() public { + Signer[] memory signers = _getSigners(MAX_ORACLES); + + uint8 MINIMAL_FAULT_TOLERANCE = 2; + BaseTest.Signer[] memory signersSubset1 = new BaseTest.Signer[](7); + signersSubset1[0] = signers[0]; + signersSubset1[1] = signers[1]; + signersSubset1[2] = signers[2]; + signersSubset1[3] = signers[3]; + signersSubset1[4] = signers[4]; + signersSubset1[5] = signers[5]; + signersSubset1[6] = signers[6]; + address[] memory signersAddrSubset1 = _getSignerAddresses(signersSubset1); + s_verifier.setConfig(signersAddrSubset1, MINIMAL_FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + + // one report whose signer is not in the config + BaseTest.Signer[] memory reportSigners = new BaseTest.Signer[](4); + // these signers are part ofm the config + reportSigners[0] = signers[4]; + reportSigners[1] = signers[5]; + reportSigners[2] = signers[6]; + // this single signer is not in the config + reportSigners[3] = signers[7]; + + bytes memory signedReport = _generateV3EncodedBlob(s_testReportThree, s_reportContext, reportSigners); + + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.BadVerification.selector)); + s_verifierProxy.verify(signedReport, abi.encode(native)); + } + + function test_canVerifyOlderV3ReportsWithOlderConfigs() public { + /* + This test is checking we can use historical Configs to verify reports: + - DonConfigA has signers {A, B, C, E} is set at time T1 + - DonConfigB has signers {A, B, C, D} is set at time T2 + - checks we can verify a report with {B, C, D} signers (via DonConfigB) + - checks we can verify a report with {B, C, E} signers and timestamp below T2 (via DonConfigA historical config) + - checks we can't verify a report with {B, C, E} signers and timestamp above T2 (it gets verivied via DonConfigB) + - sets DonConfigA as deactivated + - checks we can't verify a report with {B, C, E} signers and timestamp below T2 (via DonConfigA) + */ + Signer[] memory signers = _getSigners(MAX_ORACLES); + + uint8 MINIMAL_FAULT_TOLERANCE = 2; + BaseTest.Signer[] memory signersSubset1 = new BaseTest.Signer[](7); + signersSubset1[0] = signers[0]; + signersSubset1[1] = signers[1]; + signersSubset1[2] = signers[2]; + signersSubset1[3] = signers[3]; + signersSubset1[4] = signers[4]; + signersSubset1[5] = signers[5]; + signersSubset1[6] = signers[6]; + + address[] memory signersAddrSubset1 = _getSignerAddresses(signersSubset1); + // Config1 + s_verifier.setConfig(signersAddrSubset1, MINIMAL_FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + + BaseTest.Signer[] memory signersSubset2 = new BaseTest.Signer[](7); + signersSubset2[0] = signers[0]; + signersSubset2[1] = signers[1]; + signersSubset2[2] = signers[2]; + signersSubset2[3] = signers[3]; + signersSubset2[4] = signers[4]; + signersSubset2[5] = signers[5]; + signersSubset2[6] = signers[29]; + address[] memory signersAddrSubset2 = _getSignerAddresses(signersSubset2); + + V3Report memory reportAtSetConfig1Timestmap = V3Report({ + feedId: FEED_ID_V3, + observationsTimestamp: OBSERVATIONS_TIMESTAMP, + validFromTimestamp: uint32(block.timestamp), + nativeFee: uint192(DEFAULT_REPORT_NATIVE_FEE), + linkFee: uint192(DEFAULT_REPORT_LINK_FEE), + expiresAt: uint32(block.timestamp), + benchmarkPrice: MEDIAN, + bid: BID, + ask: ASK + }); + + vm.warp(block.timestamp + 100); + + // Config2 + s_verifier.setConfig(signersAddrSubset2, MINIMAL_FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + + V3Report memory reportAtSetConfig2Timestmap = V3Report({ + feedId: FEED_ID_V3, + observationsTimestamp: OBSERVATIONS_TIMESTAMP, + validFromTimestamp: uint32(block.timestamp), + nativeFee: uint192(DEFAULT_REPORT_NATIVE_FEE), + linkFee: uint192(DEFAULT_REPORT_LINK_FEE), + expiresAt: uint32(block.timestamp), + benchmarkPrice: MEDIAN, + bid: BID, + ask: ASK + }); + + BaseTest.Signer[] memory reportSigners = new BaseTest.Signer[](5); + reportSigners[0] = signers[0]; + reportSigners[1] = signers[1]; + reportSigners[2] = signers[2]; + reportSigners[3] = signers[3]; + reportSigners[4] = signers[29]; + + bytes memory signedReport = _generateV3EncodedBlob(reportAtSetConfig2Timestmap, s_reportContext, reportSigners); + + // this report is verified via Config2 + bytes memory verifierResponse = s_verifierProxy.verify(signedReport, abi.encode(native)); + assertReportsEqual(verifierResponse, reportAtSetConfig2Timestmap); + + BaseTest.Signer[] memory reportSigners2 = new BaseTest.Signer[](5); + reportSigners2[0] = signers[0]; + reportSigners2[1] = signers[1]; + reportSigners2[2] = signers[2]; + reportSigners2[3] = signers[3]; + reportSigners2[4] = signers[6]; + + bytes memory signedReport2 = _generateV3EncodedBlob(reportAtSetConfig1Timestmap, s_reportContext, reportSigners2); + + // this report is verified via Config1 (using a historical config) + bytes memory verifierResponse2 = s_verifierProxy.verify(signedReport2, abi.encode(native)); + assertReportsEqual(verifierResponse2, reportAtSetConfig1Timestmap); + + // same report with same signers but with a higher timestamp gets verified via Config2 + // which means verification fails + bytes memory signedReport3 = _generateV3EncodedBlob(reportAtSetConfig2Timestmap, s_reportContext, reportSigners2); + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.BadVerification.selector)); + s_verifierProxy.verify(signedReport3, abi.encode(native)); + + // deactivating Config1 and trying a reverifications ends in failure + s_verifier.setConfigActive(0, false); + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.BadVerification.selector)); + s_verifierProxy.verify(signedReport2, abi.encode(native)); + } + + function test_revertsVerifyIfNoAccess() public { + vm.mockCall( + ACCESS_CONTROLLER_ADDRESS, + abi.encodeWithSelector(AccessControllerInterface.hasAccess.selector, USER), + abi.encode(false) + ); + bytes memory signedReport = _generateV3EncodedBlob( + s_testReportThree, + s_reportContext, + _getSigners(FAULT_TOLERANCE + 1) + ); + + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.AccessForbidden.selector)); + + changePrank(USER); + s_verifier.verify(signedReport, abi.encode(native), msg.sender); + } + + function test_canVerifyNewerReportsWithNewerConfigs() public { + /* + This test is checking that we use prefer verifiying via newer configs instead of old ones. + - DonConfigA has signers {A, B, C, E} is set at time T1 + - DonConfigB has signers {F, G, H, I} is set at time T2 + - DonConfigC has signers {J, K, L, M } is set at time T3 + - checks we can verify a report with {K, L, M} signers (via DonConfigC) + */ + Signer[] memory signers = _getSigners(MAX_ORACLES); + + uint8 MINIMAL_FAULT_TOLERANCE = 2; + BaseTest.Signer[] memory signersSubset1 = new BaseTest.Signer[](7); + signersSubset1[0] = signers[0]; + signersSubset1[1] = signers[1]; + signersSubset1[2] = signers[2]; + signersSubset1[3] = signers[3]; + signersSubset1[4] = signers[4]; + signersSubset1[5] = signers[5]; + signersSubset1[6] = signers[6]; + + address[] memory signersAddrSubset1 = _getSignerAddresses(signersSubset1); + // Config1 + s_verifier.setConfig(signersAddrSubset1, MINIMAL_FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + + BaseTest.Signer[] memory signersSubset2 = new BaseTest.Signer[](7); + signersSubset2[0] = signers[7]; + signersSubset2[1] = signers[8]; + signersSubset2[2] = signers[9]; + signersSubset2[3] = signers[10]; + signersSubset2[4] = signers[11]; + signersSubset2[5] = signers[12]; + signersSubset2[6] = signers[13]; + + address[] memory signersAddrSubset2 = _getSignerAddresses(signersSubset2); + // Config2 + s_verifier.setConfig(signersAddrSubset2, MINIMAL_FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + + BaseTest.Signer[] memory signersSubset3 = new BaseTest.Signer[](7); + signersSubset3[0] = signers[30]; + signersSubset3[1] = signers[29]; + signersSubset3[2] = signers[28]; + signersSubset3[3] = signers[27]; + signersSubset3[4] = signers[26]; + signersSubset3[5] = signers[25]; + signersSubset3[6] = signers[24]; + + address[] memory signersAddrSubset3 = _getSignerAddresses(signersSubset3); + // Config3 + s_verifier.setConfig(signersAddrSubset3, MINIMAL_FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + + V3Report memory report = V3Report({ + feedId: FEED_ID_V3, + observationsTimestamp: OBSERVATIONS_TIMESTAMP, + validFromTimestamp: uint32(block.timestamp), + nativeFee: uint192(DEFAULT_REPORT_NATIVE_FEE), + linkFee: uint192(DEFAULT_REPORT_LINK_FEE), + expiresAt: uint32(block.timestamp), + benchmarkPrice: MEDIAN, + bid: BID, + ask: ASK + }); + + BaseTest.Signer[] memory reportSigners = new BaseTest.Signer[](3); + reportSigners[0] = signers[30]; + reportSigners[1] = signers[29]; + reportSigners[2] = signers[28]; + + bytes memory signedReport = _generateV3EncodedBlob(report, s_reportContext, reportSigners); + + s_verifierProxy.verify(signedReport, abi.encode(native)); + } + + function test_rollingOutConfiguration() public { + /* + This test is checking that we can roll out to a new DON without downtime using a transition configuration + - DonConfigA has signers {A, B, C} is set at time T1 + - DonConfigB (transition config) has signers {A, B, C, D, E, F} is set at time T2 + - DonConfigC has signers {D, E, F} is set at time T3 + + - checks we can verify a report with {A, B, C} signers (via DonConfigA) at time between T1 and T2 + - checks we can verify a report with {A, B, C} signers (via DonConfigB) at time between T2 and T3 + - checks we can verify a report with {D, E, F} signers (via DonConfigB) at time between T2 and T3 + - checks we can verify a report with {D, E, F} signers (via DonConfigC) at time > T3 + - checks we can't verify a report with {A, B, C} signers (via DonConfigC) and timestamp >T3 at time > T3 + - checks we can verify a report with {A, B, C} signers (via DonConfigC) and timestamp between T2 and T3 at time > T3 (historical check) + + */ + + Signer[] memory signers = _getSigners(MAX_ORACLES); + + uint8 MINIMAL_FAULT_TOLERANCE = 2; + BaseTest.Signer[] memory signersSubset1 = new BaseTest.Signer[](7); + signersSubset1[0] = signers[0]; + signersSubset1[1] = signers[1]; + signersSubset1[2] = signers[2]; + signersSubset1[3] = signers[3]; + signersSubset1[4] = signers[4]; + signersSubset1[5] = signers[5]; + signersSubset1[6] = signers[6]; + + // ConfigA + address[] memory signersAddrSubset1 = _getSignerAddresses(signersSubset1); + s_verifier.setConfig(signersAddrSubset1, MINIMAL_FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + + V3Report memory reportT1 = V3Report({ + feedId: FEED_ID_V3, + observationsTimestamp: OBSERVATIONS_TIMESTAMP, + validFromTimestamp: uint32(block.timestamp), + nativeFee: uint192(DEFAULT_REPORT_NATIVE_FEE), + linkFee: uint192(DEFAULT_REPORT_LINK_FEE), + expiresAt: uint32(block.timestamp), + benchmarkPrice: MEDIAN, + bid: BID, + ask: ASK + }); + + BaseTest.Signer[] memory reportSignersConfigA = new BaseTest.Signer[](3); + reportSignersConfigA[0] = signers[0]; + reportSignersConfigA[1] = signers[1]; + reportSignersConfigA[2] = signers[2]; + + // just testing ConfigA + bytes memory signedReport = _generateV3EncodedBlob(reportT1, s_reportContext, reportSignersConfigA); + s_verifierProxy.verify(signedReport, abi.encode(native)); + + vm.warp(block.timestamp + 100); + + BaseTest.Signer[] memory signersSuperset = new BaseTest.Signer[](14); + // signers in ConfigA + signersSuperset[0] = signers[0]; + signersSuperset[1] = signers[1]; + signersSuperset[2] = signers[2]; + signersSuperset[3] = signers[3]; + signersSuperset[4] = signers[4]; + signersSuperset[5] = signers[5]; + signersSuperset[6] = signers[6]; + // new signers + signersSuperset[7] = signers[7]; + signersSuperset[8] = signers[8]; + signersSuperset[9] = signers[9]; + signersSuperset[10] = signers[10]; + signersSuperset[11] = signers[11]; + signersSuperset[12] = signers[12]; + signersSuperset[13] = signers[13]; + + BaseTest.Signer[] memory reportSignersConfigC = new BaseTest.Signer[](3); + reportSignersConfigC[0] = signers[7]; + reportSignersConfigC[1] = signers[8]; + reportSignersConfigC[2] = signers[9]; + + // ConfigB (transition Config) + address[] memory signersAddrsSuperset = _getSignerAddresses(signersSuperset); + s_verifier.setConfig(signersAddrsSuperset, MINIMAL_FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + + V3Report memory reportT2 = V3Report({ + feedId: FEED_ID_V3, + observationsTimestamp: OBSERVATIONS_TIMESTAMP, + validFromTimestamp: uint32(block.timestamp), + nativeFee: uint192(DEFAULT_REPORT_NATIVE_FEE), + linkFee: uint192(DEFAULT_REPORT_LINK_FEE), + expiresAt: uint32(block.timestamp), + benchmarkPrice: MEDIAN, + bid: BID, + ask: ASK + }); + + // testing we can verify a fresh (block timestamp) report with ConfigA signers. This should use ConfigB + signedReport = _generateV3EncodedBlob(reportT2, s_reportContext, reportSignersConfigA); + s_verifierProxy.verify(signedReport, abi.encode(native)); + + // testing we can verify an old ( non fresh block timestamp) report with ConfigA signers. This should use ConfigA + signedReport = _generateV3EncodedBlob(reportT1, s_reportContext, reportSignersConfigA); + s_verifierProxy.verify(signedReport, abi.encode(native)); + // deactivating to make sure we are really verifiying via ConfigA + s_verifier.setConfigActive(0, false); + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.BadVerification.selector)); + s_verifierProxy.verify(signedReport, abi.encode(native)); + s_verifier.setConfigActive(0, true); + + // testing we can verify a fresh (block timestamp) report with the new signers. This should use ConfigB + signedReport = _generateV3EncodedBlob(reportT2, s_reportContext, reportSignersConfigC); + s_verifierProxy.verify(signedReport, abi.encode(native)); + + vm.warp(block.timestamp + 100); + + // Adding ConfigC + BaseTest.Signer[] memory signersSubset2 = new BaseTest.Signer[](7); + signersSubset2[0] = signers[7]; + signersSubset2[1] = signers[8]; + signersSubset2[2] = signers[9]; + signersSubset2[3] = signers[10]; + signersSubset2[4] = signers[11]; + signersSubset2[5] = signers[12]; + signersSubset2[6] = signers[13]; + address[] memory signersAddrsSubset2 = _getSignerAddresses(signersSubset2); + s_verifier.setConfig(signersAddrsSubset2, MINIMAL_FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + + V3Report memory reportT3 = V3Report({ + feedId: FEED_ID_V3, + observationsTimestamp: OBSERVATIONS_TIMESTAMP, + validFromTimestamp: uint32(block.timestamp), + nativeFee: uint192(DEFAULT_REPORT_NATIVE_FEE), + linkFee: uint192(DEFAULT_REPORT_LINK_FEE), + expiresAt: uint32(block.timestamp), + benchmarkPrice: MEDIAN, + bid: BID, + ask: ASK + }); + + // testing we can verify reports with ConfigC signers + signedReport = _generateV3EncodedBlob(reportT3, s_reportContext, reportSignersConfigC); + s_verifierProxy.verify(signedReport, abi.encode(native)); + + // testing an old report (block timestamp) with ConfigC signers should verify via ConfigB + signedReport = _generateV3EncodedBlob(reportT2, s_reportContext, reportSignersConfigC); + s_verifierProxy.verify(signedReport, abi.encode(native)); + // deactivating to make sure we are really verifiying via ConfigB + s_verifier.setConfigActive(1, false); + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.BadVerification.selector)); + s_verifierProxy.verify(signedReport, abi.encode(native)); + s_verifier.setConfigActive(1, true); + + // testing a recent report with ConfigA signers should not verify + signedReport = _generateV3EncodedBlob(reportT3, s_reportContext, reportSignersConfigA); + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.BadVerification.selector)); + s_verifierProxy.verify(signedReport, abi.encode(native)); + + // testing an old report (block timestamp) with ConfigA signers should verify via ConfigB + signedReport = _generateV3EncodedBlob(reportT2, s_reportContext, reportSignersConfigA); + s_verifierProxy.verify(signedReport, abi.encode(native)); + // deactivating to make sure we are really verifiying via ConfigB + s_verifier.setConfigActive(1, false); + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.BadVerification.selector)); + s_verifierProxy.verify(signedReport, abi.encode(native)); + s_verifier.setConfigActive(1, true); + + // testing an old report (block timestamp) with ConfigA signers should verify via ConfigA + signedReport = _generateV3EncodedBlob(reportT1, s_reportContext, reportSignersConfigA); + s_verifierProxy.verify(signedReport, abi.encode(native)); + // deactivating to make sure we are really verifiying via ConfigB + s_verifier.setConfigActive(0, false); + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.BadVerification.selector)); + s_verifierProxy.verify(signedReport, abi.encode(native)); + s_verifier.setConfigActive(0, true); + } + + function test_verifyFailsWhenReportIsOlderThanConfig() public { + /* + - SetConfig A at time T0 + - SetConfig B at time T1 + - tries verifing report issued at blocktimestmap < T0 + + this test is failing: ToDo Ask Michael + */ + Signer[] memory signers = _getSigners(MAX_ORACLES); + address[] memory signerAddrs = _getSignerAddresses(signers); + s_reportContext[0] = bytes32(abi.encode(uint32(5), uint8(1))); + + vm.warp(block.timestamp + 100); + + V3Report memory reportAtTMinus100 = V3Report({ + feedId: FEED_ID_V3, + observationsTimestamp: OBSERVATIONS_TIMESTAMP, + validFromTimestamp: uint32(block.timestamp - 100), + nativeFee: uint192(DEFAULT_REPORT_NATIVE_FEE), + linkFee: uint192(DEFAULT_REPORT_LINK_FEE), + expiresAt: uint32(block.timestamp), + benchmarkPrice: MEDIAN, + bid: BID, + ask: ASK + }); + + s_verifier.setConfig(signerAddrs, FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + vm.warp(block.timestamp + 100); + s_verifier.setConfig(signerAddrs, FAULT_TOLERANCE - 1, new Common.AddressAndWeight[](0)); + + bytes memory signedReport = _generateV3EncodedBlob(reportAtTMinus100, s_reportContext, signers); + + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.BadVerification.selector)); + s_verifierProxy.verify(signedReport, abi.encode(native)); + } + + function test_scenarioRollingNewChainWithHistoricConfigs() public { + /* + This test is checking that we can roll out in a new network and set historic configurations : + - Stars with a chain at blocktimestamp 1000 + - SetConfigA with teimstamp 100 + - SetConfigB with timesmtap 200 + - SetConfigC with timestamp current + - tries verifying reports for all the configs + */ + + vm.warp(block.timestamp + 1000); + + Signer[] memory signers = _getSigners(MAX_ORACLES); + + uint8 MINIMAL_FAULT_TOLERANCE = 2; + BaseTest.Signer[] memory signersA = new BaseTest.Signer[](7); + signersA[0] = signers[0]; + signersA[1] = signers[1]; + signersA[2] = signers[2]; + signersA[3] = signers[3]; + signersA[4] = signers[4]; + signersA[5] = signers[5]; + signersA[6] = signers[6]; + + // ConfigA (historical config) + uint32 configATimestmap = 100; + address[] memory signersAddrA = _getSignerAddresses(signersA); + s_verifier.setConfigWithActivationTime( + signersAddrA, + MINIMAL_FAULT_TOLERANCE, + new Common.AddressAndWeight[](0), + configATimestmap + ); + + // ConfigB (historical config) + uint32 configBTimestmap = 200; + // Config B + BaseTest.Signer[] memory signersB = new BaseTest.Signer[](7); + // signers in ConfigA + signersB[0] = signers[8]; + signersB[1] = signers[9]; + signersB[2] = signers[10]; + signersB[3] = signers[11]; + signersB[4] = signers[12]; + signersB[5] = signers[13]; + signersB[6] = signers[14]; + address[] memory signersAddrsB = _getSignerAddresses(signersB); + s_verifier.setConfigWithActivationTime( + signersAddrsB, + MINIMAL_FAULT_TOLERANCE, + new Common.AddressAndWeight[](0), + configBTimestmap + ); + + // ConfigC (config at current timestamp) + // BaseTest.Signer[] memory signersC = new BaseTest.Signer[](7); + // signers in ConfigA + signersB[6] = signers[15]; + address[] memory signersAddrsC = _getSignerAddresses(signersB); + s_verifier.setConfig(signersAddrsC, MINIMAL_FAULT_TOLERANCE, new Common.AddressAndWeight[](0)); + + vm.warp(block.timestamp + 10); + + // historical report + V3Report memory s_testReportA = V3Report({ + feedId: FEED_ID_V3, + observationsTimestamp: OBSERVATIONS_TIMESTAMP, + validFromTimestamp: uint32(101), + nativeFee: uint192(DEFAULT_REPORT_NATIVE_FEE), + linkFee: uint192(DEFAULT_REPORT_LINK_FEE), + expiresAt: uint32(block.timestamp + 1000), + benchmarkPrice: MEDIAN, + bid: BID, + ask: ASK + }); + + // historical report + V3Report memory s_testReportB = V3Report({ + feedId: FEED_ID_V3, + observationsTimestamp: OBSERVATIONS_TIMESTAMP, + validFromTimestamp: uint32(201), + nativeFee: uint192(DEFAULT_REPORT_NATIVE_FEE), + linkFee: uint192(DEFAULT_REPORT_LINK_FEE), + expiresAt: uint32(block.timestamp + 1000), + benchmarkPrice: MEDIAN, + bid: BID, + ask: ASK + }); + + // report at recent timestamp + V3Report memory s_testReportC = V3Report({ + feedId: FEED_ID_V3, + observationsTimestamp: OBSERVATIONS_TIMESTAMP, + validFromTimestamp: uint32(block.timestamp), + nativeFee: uint192(DEFAULT_REPORT_NATIVE_FEE), + linkFee: uint192(DEFAULT_REPORT_LINK_FEE), + expiresAt: uint32(block.timestamp + 1000), + benchmarkPrice: MEDIAN, + bid: BID, + ask: ASK + }); + + BaseTest.Signer[] memory reportSignersA = new BaseTest.Signer[](3); + reportSignersA[0] = signers[0]; + reportSignersA[1] = signers[1]; + reportSignersA[2] = signers[2]; + + BaseTest.Signer[] memory reportSignersB = new BaseTest.Signer[](3); + reportSignersB[0] = signers[8]; + reportSignersB[1] = signers[9]; + reportSignersB[2] = signers[14]; + + BaseTest.Signer[] memory reportSignersC = new BaseTest.Signer[](3); + reportSignersC[0] = signers[15]; + reportSignersC[1] = signers[13]; + reportSignersC[2] = signers[12]; + + bytes memory signedReportA = _generateV3EncodedBlob(s_testReportA, s_reportContext, reportSignersA); + bytes memory signedReportB = _generateV3EncodedBlob(s_testReportB, s_reportContext, reportSignersB); + bytes memory signedReportC = _generateV3EncodedBlob(s_testReportC, s_reportContext, reportSignersC); + + // verifying historical reports + s_verifierProxy.verify(signedReportA, abi.encode(native)); + s_verifierProxy.verify(signedReportB, abi.encode(native)); + // verifiying a current report + s_verifierProxy.verify(signedReportC, abi.encode(native)); + + // current report verified by historical report fails + bytes memory signedNewReportWithOldSignatures = _generateV3EncodedBlob( + s_testReportC, + s_reportContext, + reportSignersA + ); + vm.expectRevert(abi.encodeWithSelector(DestinationVerifier.BadVerification.selector)); + s_verifierProxy.verify(signedNewReportWithOldSignatures, abi.encode(native)); + } +} diff --git a/contracts/src/v0.8/shared/test/helpers/ChainReaderTester.sol b/contracts/src/v0.8/shared/test/helpers/ChainReaderTester.sol index 58a4b9a25c..709d00cc38 100644 --- a/contracts/src/v0.8/shared/test/helpers/ChainReaderTester.sol +++ b/contracts/src/v0.8/shared/test/helpers/ChainReaderTester.sol @@ -40,6 +40,9 @@ contract ChainReaderTester { // First topic is event hash event TriggeredWithFourTopics(int32 indexed field1, int32 indexed field2, int32 indexed field3); + // first topic is event hash, second and third topics get hashed before getting stored + event TriggeredWithFourTopicsWithHashed(string indexed field1, uint8[32] indexed field2, bytes32 indexed field3); + TestStruct[] private s_seen; uint64[] private s_arr; uint64 private s_value; @@ -125,4 +128,9 @@ contract ChainReaderTester { function triggerWithFourTopics(int32 field1, int32 field2, int32 field3) public { emit TriggeredWithFourTopics(field1, field2, field3); } + + // first topic is event hash, second and third topics get hashed before getting stored + function triggerWithFourTopicsWithHashed(string memory field1, uint8[32] memory field2, bytes32 field3) public { + emit TriggeredWithFourTopicsWithHashed(field1, field2, field3); + } } diff --git a/contracts/src/v0.8/tests/MockGasBoundCaller.sol b/contracts/src/v0.8/tests/MockGasBoundCaller.sol new file mode 100644 index 0000000000..3184f9dba3 --- /dev/null +++ b/contracts/src/v0.8/tests/MockGasBoundCaller.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.19; + +contract MockGasBoundCaller { + error TransactionFailed(address target); + + function gasBoundCall(address target, uint256 gasAmount, bytes memory data) external payable { + bool success; + assembly { + success := call(gasAmount, target, 0, add(data, 0x20), mload(data), 0, 0) + } + + // gas bound caller will propagate the revert + if (!success) { + revert TransactionFailed(target); + } + + uint256 pubdataGas = 500000; + bytes memory returnData = abi.encode(address(0), pubdataGas); + + uint256 paddedReturndataLen = returnData.length + 96; + if (paddedReturndataLen % 32 != 0) { + paddedReturndataLen += 32 - (paddedReturndataLen % 32); + } + + assembly { + mstore(sub(returnData, 0x40), 0x40) + mstore(sub(returnData, 0x20), pubdataGas) + return(sub(returnData, 0x40), paddedReturndataLen) + } + } +} diff --git a/contracts/src/v0.8/tests/MockOVMGasPriceOracle.sol b/contracts/src/v0.8/tests/MockOVMGasPriceOracle.sol index 29790b0e15..6bb0dae645 100644 --- a/contracts/src/v0.8/tests/MockOVMGasPriceOracle.sol +++ b/contracts/src/v0.8/tests/MockOVMGasPriceOracle.sol @@ -1,7 +1,12 @@ +// SPDX-License-Identifier: MIT pragma solidity 0.8.6; contract MockOVMGasPriceOracle { - function getL1Fee(bytes memory _data) public view returns (uint256) { + function getL1Fee(bytes memory) public pure returns (uint256) { + return 2000000; + } + + function getL1FeeUpperBound(uint256) public pure returns (uint256) { return 2000000; } } diff --git a/contracts/src/v0.8/tests/MockZKSyncSystemContext.sol b/contracts/src/v0.8/tests/MockZKSyncSystemContext.sol new file mode 100644 index 0000000000..265d4b678a --- /dev/null +++ b/contracts/src/v0.8/tests/MockZKSyncSystemContext.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.19; + +contract MockZKSyncSystemContext { + function gasPrice() external pure returns (uint256) { + return 250000000; // 0.25 gwei + } + + function gasPerPubdataByte() external pure returns (uint256) { + return 500; + } + + function getCurrentPubdataSpent() external pure returns (uint256 currentPubdataSpent) { + return 1000; + } +} diff --git a/contracts/src/v0.8/vrf/dev/testhelpers/VRFCoordinatorTestV2_5.sol b/contracts/src/v0.8/vrf/dev/testhelpers/VRFCoordinatorTestV2_5.sol new file mode 100644 index 0000000000..2e9c4a2da7 --- /dev/null +++ b/contracts/src/v0.8/vrf/dev/testhelpers/VRFCoordinatorTestV2_5.sol @@ -0,0 +1,773 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {BlockhashStoreInterface} from "../../interfaces/BlockhashStoreInterface.sol"; +import {VRFOld} from "./VRFOld.sol"; +import {VRFTypes} from "../../VRFTypes.sol"; +import {VRFConsumerBaseV2Plus, IVRFMigratableConsumerV2Plus} from "../VRFConsumerBaseV2Plus.sol"; +import {ChainSpecificUtil} from "../../../ChainSpecificUtil.sol"; +import {SubscriptionAPI} from "../SubscriptionAPI.sol"; +import {VRFV2PlusClient} from "../libraries/VRFV2PlusClient.sol"; +import {IVRFCoordinatorV2PlusMigration} from "../interfaces/IVRFCoordinatorV2PlusMigration.sol"; +// solhint-disable-next-line no-unused-import +import {IVRFCoordinatorV2Plus, IVRFSubscriptionV2Plus} from "../interfaces/IVRFCoordinatorV2Plus.sol"; + +// solhint-disable-next-line contract-name-camelcase +contract VRFCoordinatorTestV2_5 is VRFOld, SubscriptionAPI, IVRFCoordinatorV2Plus { + /// @dev should always be available + // solhint-disable-next-line chainlink-solidity/prefix-immutable-variables-with-i + BlockhashStoreInterface public immutable BLOCKHASH_STORE; + + // Set this maximum to 200 to give us a 56 block window to fulfill + // the request before requiring the block hash feeder. + uint16 public constant MAX_REQUEST_CONFIRMATIONS = 200; + uint32 public constant MAX_NUM_WORDS = 500; + // 5k is plenty for an EXTCODESIZE call (2600) + warm CALL (100) + // and some arithmetic operations. + uint256 private constant GAS_FOR_CALL_EXACT_CHECK = 5_000; + // upper bound limit for premium percentages to make sure fee calculations don't overflow + uint8 private constant PREMIUM_PERCENTAGE_MAX = 155; + error InvalidRequestConfirmations(uint16 have, uint16 min, uint16 max); + error GasLimitTooBig(uint32 have, uint32 want); + error NumWordsTooBig(uint32 have, uint32 want); + error MsgDataTooBig(uint256 have, uint32 max); + error ProvingKeyAlreadyRegistered(bytes32 keyHash); + error NoSuchProvingKey(bytes32 keyHash); + error InvalidLinkWeiPrice(int256 linkWei); + error LinkDiscountTooHigh(uint32 flatFeeLinkDiscountPPM, uint32 flatFeeNativePPM); + error InvalidPremiumPercentage(uint8 premiumPercentage, uint8 max); + error NoCorrespondingRequest(); + error IncorrectCommitment(); + error BlockhashNotInStore(uint256 blockNum); + error PaymentTooLarge(); + error InvalidExtraArgsTag(); + error GasPriceExceeded(uint256 gasPrice, uint256 maxGas); + + struct ProvingKey { + bool exists; // proving key exists + uint64 maxGas; // gas lane max gas price for fulfilling requests + } + + mapping(bytes32 => ProvingKey) /* keyHash */ /* provingKey */ public s_provingKeys; + bytes32[] public s_provingKeyHashes; + mapping(uint256 => bytes32) /* requestID */ /* commitment */ public s_requestCommitments; + event ProvingKeyRegistered(bytes32 keyHash, uint64 maxGas); + event ProvingKeyDeregistered(bytes32 keyHash, uint64 maxGas); + + event RandomWordsRequested( + bytes32 indexed keyHash, + uint256 requestId, + uint256 preSeed, + uint256 indexed subId, + uint16 minimumRequestConfirmations, + uint32 callbackGasLimit, + uint32 numWords, + bytes extraArgs, + address indexed sender + ); + + event RandomWordsFulfilled( + uint256 indexed requestId, + uint256 outputSeed, + uint256 indexed subId, + uint96 payment, + bool nativePayment, + bool success, + bool onlyPremium + ); + + int256 public s_fallbackWeiPerUnitLink; + + event ConfigSet( + uint16 minimumRequestConfirmations, + uint32 maxGasLimit, + uint32 stalenessSeconds, + uint32 gasAfterPaymentCalculation, + int256 fallbackWeiPerUnitLink, + uint32 fulfillmentFlatFeeNativePPM, + uint32 fulfillmentFlatFeeLinkDiscountPPM, + uint8 nativePremiumPercentage, + uint8 linkPremiumPercentage + ); + + event FallbackWeiPerUnitLinkUsed(uint256 requestId, int256 fallbackWeiPerUnitLink); + + constructor(address blockhashStore) SubscriptionAPI() { + BLOCKHASH_STORE = BlockhashStoreInterface(blockhashStore); + } + + /** + * @notice Registers a proving key to. + * @param publicProvingKey key that oracle can use to submit vrf fulfillments + */ + function registerProvingKey(uint256[2] calldata publicProvingKey, uint64 maxGas) external onlyOwner { + bytes32 kh = hashOfKey(publicProvingKey); + if (s_provingKeys[kh].exists) { + revert ProvingKeyAlreadyRegistered(kh); + } + s_provingKeys[kh] = ProvingKey({exists: true, maxGas: maxGas}); + s_provingKeyHashes.push(kh); + emit ProvingKeyRegistered(kh, maxGas); + } + + /** + * @notice Deregisters a proving key. + * @param publicProvingKey key that oracle can use to submit vrf fulfillments + */ + function deregisterProvingKey(uint256[2] calldata publicProvingKey) external onlyOwner { + bytes32 kh = hashOfKey(publicProvingKey); + ProvingKey memory key = s_provingKeys[kh]; + if (!key.exists) { + revert NoSuchProvingKey(kh); + } + delete s_provingKeys[kh]; + uint256 s_provingKeyHashesLength = s_provingKeyHashes.length; + for (uint256 i = 0; i < s_provingKeyHashesLength; ++i) { + if (s_provingKeyHashes[i] == kh) { + // Copy last element and overwrite kh to be deleted with it + s_provingKeyHashes[i] = s_provingKeyHashes[s_provingKeyHashesLength - 1]; + s_provingKeyHashes.pop(); + break; + } + } + emit ProvingKeyDeregistered(kh, key.maxGas); + } + + /** + * @notice Returns the proving key hash key associated with this public key + * @param publicKey the key to return the hash of + */ + function hashOfKey(uint256[2] memory publicKey) public pure returns (bytes32) { + return keccak256(abi.encode(publicKey)); + } + + /** + * @notice Sets the configuration of the vrfv2 coordinator + * @param minimumRequestConfirmations global min for request confirmations + * @param maxGasLimit global max for request gas limit + * @param stalenessSeconds if the native/link feed is more stale then this, use the fallback price + * @param gasAfterPaymentCalculation gas used in doing accounting after completing the gas measurement + * @param fallbackWeiPerUnitLink fallback native/link price in the case of a stale feed + * @param fulfillmentFlatFeeNativePPM flat fee in native for native payment + * @param fulfillmentFlatFeeLinkDiscountPPM flat fee discount for link payment in native + * @param nativePremiumPercentage native premium percentage + * @param linkPremiumPercentage link premium percentage + */ + function setConfig( + uint16 minimumRequestConfirmations, + uint32 maxGasLimit, + uint32 stalenessSeconds, + uint32 gasAfterPaymentCalculation, + int256 fallbackWeiPerUnitLink, + uint32 fulfillmentFlatFeeNativePPM, + uint32 fulfillmentFlatFeeLinkDiscountPPM, + uint8 nativePremiumPercentage, + uint8 linkPremiumPercentage + ) external onlyOwner { + if (minimumRequestConfirmations > MAX_REQUEST_CONFIRMATIONS) { + revert InvalidRequestConfirmations( + minimumRequestConfirmations, + minimumRequestConfirmations, + MAX_REQUEST_CONFIRMATIONS + ); + } + if (fallbackWeiPerUnitLink <= 0) { + revert InvalidLinkWeiPrice(fallbackWeiPerUnitLink); + } + if (fulfillmentFlatFeeLinkDiscountPPM > fulfillmentFlatFeeNativePPM) { + revert LinkDiscountTooHigh(fulfillmentFlatFeeLinkDiscountPPM, fulfillmentFlatFeeNativePPM); + } + if (nativePremiumPercentage > PREMIUM_PERCENTAGE_MAX) { + revert InvalidPremiumPercentage(nativePremiumPercentage, PREMIUM_PERCENTAGE_MAX); + } + if (linkPremiumPercentage > PREMIUM_PERCENTAGE_MAX) { + revert InvalidPremiumPercentage(linkPremiumPercentage, PREMIUM_PERCENTAGE_MAX); + } + s_config = Config({ + minimumRequestConfirmations: minimumRequestConfirmations, + maxGasLimit: maxGasLimit, + stalenessSeconds: stalenessSeconds, + gasAfterPaymentCalculation: gasAfterPaymentCalculation, + reentrancyLock: false, + fulfillmentFlatFeeNativePPM: fulfillmentFlatFeeNativePPM, + fulfillmentFlatFeeLinkDiscountPPM: fulfillmentFlatFeeLinkDiscountPPM, + nativePremiumPercentage: nativePremiumPercentage, + linkPremiumPercentage: linkPremiumPercentage + }); + s_fallbackWeiPerUnitLink = fallbackWeiPerUnitLink; + emit ConfigSet( + minimumRequestConfirmations, + maxGasLimit, + stalenessSeconds, + gasAfterPaymentCalculation, + fallbackWeiPerUnitLink, + fulfillmentFlatFeeNativePPM, + fulfillmentFlatFeeLinkDiscountPPM, + nativePremiumPercentage, + linkPremiumPercentage + ); + } + + /// @dev Convert the extra args bytes into a struct + /// @param extraArgs The extra args bytes + /// @return The extra args struct + function _fromBytes(bytes calldata extraArgs) internal pure returns (VRFV2PlusClient.ExtraArgsV1 memory) { + if (extraArgs.length == 0) { + return VRFV2PlusClient.ExtraArgsV1({nativePayment: false}); + } + if (bytes4(extraArgs) != VRFV2PlusClient.EXTRA_ARGS_V1_TAG) revert InvalidExtraArgsTag(); + return abi.decode(extraArgs[4:], (VRFV2PlusClient.ExtraArgsV1)); + } + + /** + * @notice Request a set of random words. + * @param req - a struct containing following fiels for randomness request: + * keyHash - Corresponds to a particular oracle job which uses + * that key for generating the VRF proof. Different keyHash's have different gas price + * ceilings, so you can select a specific one to bound your maximum per request cost. + * subId - The ID of the VRF subscription. Must be funded + * with the minimum subscription balance required for the selected keyHash. + * requestConfirmations - How many blocks you'd like the + * oracle to wait before responding to the request. See SECURITY CONSIDERATIONS + * for why you may want to request more. The acceptable range is + * [minimumRequestBlockConfirmations, 200]. + * callbackGasLimit - How much gas you'd like to receive in your + * fulfillRandomWords callback. Note that gasleft() inside fulfillRandomWords + * may be slightly less than this amount because of gas used calling the function + * (argument decoding etc.), so you may need to request slightly more than you expect + * to have inside fulfillRandomWords. The acceptable range is + * [0, maxGasLimit] + * numWords - The number of uint256 random values you'd like to receive + * in your fulfillRandomWords callback. Note these numbers are expanded in a + * secure way by the VRFCoordinator from a single random value supplied by the oracle. + * extraArgs - Encoded extra arguments that has a boolean flag for whether payment + * should be made in native or LINK. Payment in LINK is only available if the LINK token is available to this contract. + * @return requestId - A unique identifier of the request. Can be used to match + * a request to a response in fulfillRandomWords. + */ + function requestRandomWords( + VRFV2PlusClient.RandomWordsRequest calldata req + ) external override nonReentrant returns (uint256 requestId) { + // Input validation using the subscription storage. + uint256 subId = req.subId; + if (s_subscriptionConfigs[subId].owner == address(0)) { + revert InvalidSubscription(); + } + // Its important to ensure that the consumer is in fact who they say they + // are, otherwise they could use someone else's subscription balance. + mapping(uint256 => ConsumerConfig) storage consumerConfigs = s_consumers[msg.sender]; + ConsumerConfig memory consumerConfig = consumerConfigs[subId]; + if (!consumerConfig.active) { + revert InvalidConsumer(subId, msg.sender); + } + // Input validation using the config storage word. + if ( + req.requestConfirmations < s_config.minimumRequestConfirmations || + req.requestConfirmations > MAX_REQUEST_CONFIRMATIONS + ) { + revert InvalidRequestConfirmations( + req.requestConfirmations, + s_config.minimumRequestConfirmations, + MAX_REQUEST_CONFIRMATIONS + ); + } + // No lower bound on the requested gas limit. A user could request 0 + // and they would simply be billed for the proof verification and wouldn't be + // able to do anything with the random value. + if (req.callbackGasLimit > s_config.maxGasLimit) { + revert GasLimitTooBig(req.callbackGasLimit, s_config.maxGasLimit); + } + if (req.numWords > MAX_NUM_WORDS) { + revert NumWordsTooBig(req.numWords, MAX_NUM_WORDS); + } + + // Note we do not check whether the keyHash is valid to save gas. + // The consequence for users is that they can send requests + // for invalid keyHashes which will simply not be fulfilled. + ++consumerConfig.nonce; + ++consumerConfig.pendingReqCount; + uint256 preSeed; + (requestId, preSeed) = _computeRequestId(req.keyHash, msg.sender, subId, consumerConfig.nonce); + + bytes memory extraArgsBytes = VRFV2PlusClient._argsToBytes(_fromBytes(req.extraArgs)); + s_requestCommitments[requestId] = keccak256( + abi.encode( + requestId, + ChainSpecificUtil._getBlockNumber(), + subId, + req.callbackGasLimit, + req.numWords, + msg.sender, + extraArgsBytes + ) + ); + emit RandomWordsRequested( + req.keyHash, + requestId, + preSeed, + subId, + req.requestConfirmations, + req.callbackGasLimit, + req.numWords, + extraArgsBytes, + msg.sender + ); + consumerConfigs[subId] = consumerConfig; + + return requestId; + } + + function _computeRequestId( + bytes32 keyHash, + address sender, + uint256 subId, + uint64 nonce + ) internal pure returns (uint256, uint256) { + uint256 preSeed = uint256(keccak256(abi.encode(keyHash, sender, subId, nonce))); + return (uint256(keccak256(abi.encode(keyHash, preSeed))), preSeed); + } + + /** + * @dev calls target address with exactly gasAmount gas and data as calldata + * or reverts if at least gasAmount gas is not available. + */ + function _callWithExactGas(uint256 gasAmount, address target, bytes memory data) private returns (bool success) { + assembly { + let g := gas() + // Compute g -= GAS_FOR_CALL_EXACT_CHECK and check for underflow + // The gas actually passed to the callee is min(gasAmount, 63//64*gas available). + // We want to ensure that we revert if gasAmount > 63//64*gas available + // as we do not want to provide them with less, however that check itself costs + // gas. GAS_FOR_CALL_EXACT_CHECK ensures we have at least enough gas to be able + // to revert if gasAmount > 63//64*gas available. + if lt(g, GAS_FOR_CALL_EXACT_CHECK) { + revert(0, 0) + } + g := sub(g, GAS_FOR_CALL_EXACT_CHECK) + // if g - g//64 <= gasAmount, revert + // (we subtract g//64 because of EIP-150) + if iszero(gt(sub(g, div(g, 64)), gasAmount)) { + revert(0, 0) + } + // solidity calls check that a contract actually exists at the destination, so we do the same + if iszero(extcodesize(target)) { + revert(0, 0) + } + // call and return whether we succeeded. ignore return data + // call(gas,addr,value,argsOffset,argsLength,retOffset,retLength) + success := call(gasAmount, target, 0, add(data, 0x20), mload(data), 0, 0) + } + return success; + } + + struct Output { + ProvingKey provingKey; + uint256 requestId; + uint256 randomness; + } + + function _getRandomnessFromProof( + Proof memory proof, + VRFTypes.RequestCommitmentV2Plus memory rc + ) internal view returns (Output memory) { + bytes32 keyHash = hashOfKey(proof.pk); + ProvingKey memory key = s_provingKeys[keyHash]; + // Only registered proving keys are permitted. + if (!key.exists) { + revert NoSuchProvingKey(keyHash); + } + uint256 requestId = uint256(keccak256(abi.encode(keyHash, proof.seed))); + bytes32 commitment = s_requestCommitments[requestId]; + if (commitment == 0) { + revert NoCorrespondingRequest(); + } + if ( + commitment != + keccak256(abi.encode(requestId, rc.blockNum, rc.subId, rc.callbackGasLimit, rc.numWords, rc.sender, rc.extraArgs)) + ) { + revert IncorrectCommitment(); + } + + bytes32 blockHash = ChainSpecificUtil._getBlockhash(rc.blockNum); + if (blockHash == bytes32(0)) { + blockHash = BLOCKHASH_STORE.getBlockhash(rc.blockNum); + if (blockHash == bytes32(0)) { + revert BlockhashNotInStore(rc.blockNum); + } + } + + // The seed actually used by the VRF machinery, mixing in the blockhash + uint256 actualSeed = uint256(keccak256(abi.encodePacked(proof.seed, blockHash))); + uint256 randomness = VRFOld._randomValueFromVRFProof(proof, actualSeed); // Reverts on failure + return Output(key, requestId, randomness); + } + + function _getValidatedGasPrice(bool onlyPremium, uint64 gasLaneMaxGas) internal view returns (uint256 gasPrice) { + if (tx.gasprice > gasLaneMaxGas) { + if (onlyPremium) { + // if only the premium amount needs to be billed, then the premium is capped by the gas lane max + return uint256(gasLaneMaxGas); + } else { + // Ensure gas price does not exceed the gas lane max gas price + revert GasPriceExceeded(tx.gasprice, gasLaneMaxGas); + } + } + return tx.gasprice; + } + + function _deliverRandomness( + uint256 requestId, + VRFTypes.RequestCommitmentV2Plus memory rc, + uint256[] memory randomWords + ) internal returns (bool success) { + VRFConsumerBaseV2Plus v; + bytes memory resp = abi.encodeWithSelector(v.rawFulfillRandomWords.selector, requestId, randomWords); + // Call with explicitly the amount of callback gas requested + // Important to not let them exhaust the gas budget and avoid oracle payment. + // Do not allow any non-view/non-pure coordinator functions to be called + // during the consumers callback code via reentrancyLock. + // Note that _callWithExactGas will revert if we do not have sufficient gas + // to give the callee their requested amount. + s_config.reentrancyLock = true; + success = _callWithExactGas(rc.callbackGasLimit, rc.sender, resp); + s_config.reentrancyLock = false; + return success; + } + + /* + * @notice Fulfill a randomness request. + * @param proof contains the proof and randomness + * @param rc request commitment pre-image, committed to at request time + * @param onlyPremium only charge premium + * @return payment amount billed to the subscription + * @dev simulated offchain to determine if sufficient balance is present to fulfill the request + */ + function fulfillRandomWords( + Proof memory proof, + VRFTypes.RequestCommitmentV2Plus memory rc, + bool onlyPremium + ) external nonReentrant returns (uint96 payment) { + uint256 startGas = gasleft(); + // fulfillRandomWords msg.data has 772 bytes and with an additional + // buffer of 32 bytes, we get 804 bytes. + /* Data size split: + * fulfillRandomWords function signature - 4 bytes + * proof - 416 bytes + * pk - 64 bytes + * gamma - 64 bytes + * c - 32 bytes + * s - 32 bytes + * seed - 32 bytes + * uWitness - 32 bytes + * cGammaWitness - 64 bytes + * sHashWitness - 64 bytes + * zInv - 32 bytes + * requestCommitment - 320 bytes + * blockNum - 32 bytes + * subId - 32 bytes + * callbackGasLimit - 32 bytes + * numWords - 32 bytes + * sender - 32 bytes + * extraArgs - 128 bytes + * onlyPremium - 32 bytes + */ + if (msg.data.length > 804) { + revert MsgDataTooBig(msg.data.length, 804); + } + Output memory output = _getRandomnessFromProof(proof, rc); + uint256 gasPrice = _getValidatedGasPrice(onlyPremium, output.provingKey.maxGas); + + uint256[] memory randomWords; + uint256 randomness = output.randomness; + // stack too deep error + { + uint256 numWords = rc.numWords; + randomWords = new uint256[](numWords); + for (uint256 i = 0; i < numWords; ++i) { + randomWords[i] = uint256(keccak256(abi.encode(randomness, i))); + } + } + + delete s_requestCommitments[output.requestId]; + bool success = _deliverRandomness(output.requestId, rc, randomWords); + + // Increment the req count for the subscription. + ++s_subscriptions[rc.subId].reqCount; + // Decrement the pending req count for the consumer. + --s_consumers[rc.sender][rc.subId].pendingReqCount; + + bool nativePayment = uint8(rc.extraArgs[rc.extraArgs.length - 1]) == 1; + + // stack too deep error + { + // We want to charge users exactly for how much gas they use in their callback with + // an additional premium. If onlyPremium is true, only premium is charged without + // the gas cost. The gasAfterPaymentCalculation is meant to cover these additional + // operations where we decrement the subscription balance and increment the + // withdrawable balance. + bool isFeedStale; + (payment, isFeedStale) = _calculatePaymentAmount(startGas, gasPrice, nativePayment, onlyPremium); + if (isFeedStale) { + emit FallbackWeiPerUnitLinkUsed(output.requestId, s_fallbackWeiPerUnitLink); + } + } + + _chargePayment(payment, nativePayment, rc.subId); + + // Include payment in the event for tracking costs. + emit RandomWordsFulfilled(output.requestId, randomness, rc.subId, payment, nativePayment, success, onlyPremium); + + return payment; + } + + function _chargePayment(uint96 payment, bool nativePayment, uint256 subId) internal { + Subscription storage subcription = s_subscriptions[subId]; + if (nativePayment) { + uint96 prevBal = subcription.nativeBalance; + if (prevBal < payment) { + revert InsufficientBalance(); + } + subcription.nativeBalance = prevBal - payment; + s_withdrawableNative += payment; + } else { + uint96 prevBal = subcription.balance; + if (prevBal < payment) { + revert InsufficientBalance(); + } + subcription.balance = prevBal - payment; + s_withdrawableTokens += payment; + } + } + + function _calculatePaymentAmount( + uint256 startGas, + uint256 weiPerUnitGas, + bool nativePayment, + bool onlyPremium + ) internal view returns (uint96, bool) { + if (nativePayment) { + return (_calculatePaymentAmountNative(startGas, weiPerUnitGas, onlyPremium), false); + } + return _calculatePaymentAmountLink(startGas, weiPerUnitGas, onlyPremium); + } + + function _calculatePaymentAmountNative( + uint256 startGas, + uint256 weiPerUnitGas, + bool onlyPremium + ) internal view returns (uint96) { + // Will return non-zero on chains that have this enabled + uint256 l1CostWei = ChainSpecificUtil._getCurrentTxL1GasFees(msg.data); + // calculate the payment without the premium + uint256 baseFeeWei = weiPerUnitGas * (s_config.gasAfterPaymentCalculation + startGas - gasleft()); + // calculate flat fee in native + uint256 flatFeeWei = 1e12 * uint256(s_config.fulfillmentFlatFeeNativePPM); + if (onlyPremium) { + return uint96((((l1CostWei + baseFeeWei) * (s_config.nativePremiumPercentage)) / 100) + flatFeeWei); + } else { + return uint96((((l1CostWei + baseFeeWei) * (100 + s_config.nativePremiumPercentage)) / 100) + flatFeeWei); + } + } + + // Get the amount of gas used for fulfillment + function _calculatePaymentAmountLink( + uint256 startGas, + uint256 weiPerUnitGas, + bool onlyPremium + ) internal view returns (uint96, bool) { + (int256 weiPerUnitLink, bool isFeedStale) = _getFeedData(); + if (weiPerUnitLink <= 0) { + revert InvalidLinkWeiPrice(weiPerUnitLink); + } + // Will return non-zero on chains that have this enabled + uint256 l1CostWei = ChainSpecificUtil._getCurrentTxL1GasFees(msg.data); + // (1e18 juels/link) ((wei/gas * gas) + l1wei) / (wei/link) = juels + uint256 paymentNoFee = (1e18 * + (weiPerUnitGas * (s_config.gasAfterPaymentCalculation + startGas - gasleft()) + l1CostWei)) / + uint256(weiPerUnitLink); + // calculate the flat fee in wei + uint256 flatFeeWei = 1e12 * + uint256(s_config.fulfillmentFlatFeeNativePPM - s_config.fulfillmentFlatFeeLinkDiscountPPM); + uint256 flatFeeJuels = (1e18 * flatFeeWei) / uint256(weiPerUnitLink); + uint256 payment; + if (onlyPremium) { + payment = ((paymentNoFee * (s_config.linkPremiumPercentage)) / 100 + flatFeeJuels); + } else { + payment = ((paymentNoFee * (100 + s_config.linkPremiumPercentage)) / 100 + flatFeeJuels); + } + if (payment > 1e27) { + revert PaymentTooLarge(); // Payment + fee cannot be more than all of the link in existence. + } + return (uint96(payment), isFeedStale); + } + + function _getFeedData() private view returns (int256 weiPerUnitLink, bool isFeedStale) { + uint32 stalenessSeconds = s_config.stalenessSeconds; + uint256 timestamp; + (, weiPerUnitLink, , timestamp, ) = LINK_NATIVE_FEED.latestRoundData(); + // solhint-disable-next-line not-rely-on-time + isFeedStale = stalenessSeconds > 0 && stalenessSeconds < block.timestamp - timestamp; + if (isFeedStale) { + weiPerUnitLink = s_fallbackWeiPerUnitLink; + } + return (weiPerUnitLink, isFeedStale); + } + + /** + * @inheritdoc IVRFSubscriptionV2Plus + */ + function pendingRequestExists(uint256 subId) public view override returns (bool) { + address[] storage consumers = s_subscriptionConfigs[subId].consumers; + uint256 consumersLength = consumers.length; + if (consumersLength == 0) { + return false; + } + for (uint256 i = 0; i < consumersLength; ++i) { + if (s_consumers[consumers[i]][subId].pendingReqCount > 0) { + return true; + } + } + return false; + } + + /** + * @inheritdoc IVRFSubscriptionV2Plus + */ + function removeConsumer(uint256 subId, address consumer) external override onlySubOwner(subId) nonReentrant { + if (pendingRequestExists(subId)) { + revert PendingRequestExists(); + } + if (!s_consumers[consumer][subId].active) { + revert InvalidConsumer(subId, consumer); + } + // Note bounded by MAX_CONSUMERS + address[] memory consumers = s_subscriptionConfigs[subId].consumers; + uint256 lastConsumerIndex = consumers.length - 1; + for (uint256 i = 0; i < consumers.length; ++i) { + if (consumers[i] == consumer) { + address last = consumers[lastConsumerIndex]; + // Storage write to preserve last element + s_subscriptionConfigs[subId].consumers[i] = last; + // Storage remove last element + s_subscriptionConfigs[subId].consumers.pop(); + break; + } + } + s_consumers[consumer][subId].active = false; + emit SubscriptionConsumerRemoved(subId, consumer); + } + + /** + * @inheritdoc IVRFSubscriptionV2Plus + */ + function cancelSubscription(uint256 subId, address to) external override onlySubOwner(subId) nonReentrant { + if (pendingRequestExists(subId)) { + revert PendingRequestExists(); + } + _cancelSubscriptionHelper(subId, to); + } + + /*************************************************************************** + * Section: Migration + ***************************************************************************/ + + address[] internal s_migrationTargets; + + /// @dev Emitted when new coordinator is registered as migratable target + event CoordinatorRegistered(address coordinatorAddress); + + /// @dev Emitted when new coordinator is deregistered + event CoordinatorDeregistered(address coordinatorAddress); + + /// @notice emitted when migration to new coordinator completes successfully + /// @param newCoordinator coordinator address after migration + /// @param subId subscription ID + event MigrationCompleted(address newCoordinator, uint256 subId); + + /// @notice emitted when migrate() is called and given coordinator is not registered as migratable target + error CoordinatorNotRegistered(address coordinatorAddress); + + /// @notice emitted when migrate() is called and given coordinator is registered as migratable target + error CoordinatorAlreadyRegistered(address coordinatorAddress); + + /// @dev encapsulates data to be migrated from current coordinator + // solhint-disable-next-line gas-struct-packing + struct V1MigrationData { + uint8 fromVersion; + uint256 subId; + address subOwner; + address[] consumers; + uint96 linkBalance; + uint96 nativeBalance; + } + + function _isTargetRegistered(address target) internal view returns (bool) { + uint256 migrationTargetsLength = s_migrationTargets.length; + for (uint256 i = 0; i < migrationTargetsLength; ++i) { + if (s_migrationTargets[i] == target) { + return true; + } + } + return false; + } + + function registerMigratableCoordinator(address target) external onlyOwner { + if (_isTargetRegistered(target)) { + revert CoordinatorAlreadyRegistered(target); + } + s_migrationTargets.push(target); + emit CoordinatorRegistered(target); + } + + function deregisterMigratableCoordinator(address target) external onlyOwner { + uint256 nTargets = s_migrationTargets.length; + for (uint256 i = 0; i < nTargets; ++i) { + if (s_migrationTargets[i] == target) { + s_migrationTargets[i] = s_migrationTargets[nTargets - 1]; + s_migrationTargets.pop(); + emit CoordinatorDeregistered(target); + return; + } + } + revert CoordinatorNotRegistered(target); + } + + function migrate(uint256 subId, address newCoordinator) external nonReentrant { + if (!_isTargetRegistered(newCoordinator)) { + revert CoordinatorNotRegistered(newCoordinator); + } + (uint96 balance, uint96 nativeBalance, , address subOwner, address[] memory consumers) = getSubscription(subId); + // solhint-disable-next-line gas-custom-errors + require(subOwner == msg.sender, "Not subscription owner"); + // solhint-disable-next-line gas-custom-errors + require(!pendingRequestExists(subId), "Pending request exists"); + + V1MigrationData memory migrationData = V1MigrationData({ + fromVersion: 1, + subId: subId, + subOwner: subOwner, + consumers: consumers, + linkBalance: balance, + nativeBalance: nativeBalance + }); + bytes memory encodedData = abi.encode(migrationData); + _deleteSubscription(subId); + IVRFCoordinatorV2PlusMigration(newCoordinator).onMigration{value: nativeBalance}(encodedData); + + // Only transfer LINK if the token is active and there is a balance. + if (address(LINK) != address(0) && balance != 0) { + // solhint-disable-next-line gas-custom-errors + require(LINK.transfer(address(newCoordinator), balance), "insufficient funds"); + } + + // despite the fact that we follow best practices this is still probably safest + // to prevent any re-entrancy possibilities. + s_config.reentrancyLock = true; + for (uint256 i = 0; i < consumers.length; ++i) { + IVRFMigratableConsumerV2Plus(consumers[i]).setCoordinator(newCoordinator); + } + s_config.reentrancyLock = false; + + emit MigrationCompleted(newCoordinator, subId); + } +} diff --git a/contracts/src/v0.8/vrf/dev/testhelpers/VRFOld.sol b/contracts/src/v0.8/vrf/dev/testhelpers/VRFOld.sol new file mode 100644 index 0000000000..137235fd0a --- /dev/null +++ b/contracts/src/v0.8/vrf/dev/testhelpers/VRFOld.sol @@ -0,0 +1,588 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/** **************************************************************************** + * @notice Verification of verifiable-random-function (VRF) proofs, following + * @notice https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.3 + * @notice See https://eprint.iacr.org/2017/099.pdf for security proofs. + + * @dev Bibliographic references: + + * @dev Goldberg, et al., "Verifiable Random Functions (VRFs)", Internet Draft + * @dev draft-irtf-cfrg-vrf-05, IETF, Aug 11 2019, + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05 + + * @dev Papadopoulos, et al., "Making NSEC5 Practical for DNSSEC", Cryptology + * @dev ePrint Archive, Report 2017/099, https://eprint.iacr.org/2017/099.pdf + * **************************************************************************** + * @dev USAGE + + * @dev The main entry point is _randomValueFromVRFProof. See its docstring. + * **************************************************************************** + * @dev PURPOSE + + * @dev Reggie the Random Oracle (not his real job) wants to provide randomness + * @dev to Vera the verifier in such a way that Vera can be sure he's not + * @dev making his output up to suit himself. Reggie provides Vera a public key + * @dev to which he knows the secret key. Each time Vera provides a seed to + * @dev Reggie, he gives back a value which is computed completely + * @dev deterministically from the seed and the secret key. + + * @dev Reggie provides a proof by which Vera can verify that the output was + * @dev correctly computed once Reggie tells it to her, but without that proof, + * @dev the output is computationally indistinguishable to her from a uniform + * @dev random sample from the output space. + + * @dev The purpose of this contract is to perform that verification. + * **************************************************************************** + * @dev DESIGN NOTES + + * @dev The VRF algorithm verified here satisfies the full uniqueness, full + * @dev collision resistance, and full pseudo-randomness security properties. + * @dev See "SECURITY PROPERTIES" below, and + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-3 + + * @dev An elliptic curve point is generally represented in the solidity code + * @dev as a uint256[2], corresponding to its affine coordinates in + * @dev GF(FIELD_SIZE). + + * @dev For the sake of efficiency, this implementation deviates from the spec + * @dev in some minor ways: + + * @dev - Keccak hash rather than the SHA256 hash recommended in + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.5 + * @dev Keccak costs much less gas on the EVM, and provides similar security. + + * @dev - Secp256k1 curve instead of the P-256 or ED25519 curves recommended in + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.5 + * @dev For curve-point multiplication, it's much cheaper to abuse ECRECOVER + + * @dev - _hashToCurve recursively hashes until it finds a curve x-ordinate. On + * @dev the EVM, this is slightly more efficient than the recommendation in + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.4.1.1 + * @dev step 5, to concatenate with a nonce then hash, and rehash with the + * @dev nonce updated until a valid x-ordinate is found. + + * @dev - _hashToCurve does not include a cipher version string or the byte 0x1 + * @dev in the hash message, as recommended in step 5.B of the draft + * @dev standard. They are unnecessary here because no variation in the + * @dev cipher suite is allowed. + + * @dev - Similarly, the hash input in _scalarFromCurvePoints does not include a + * @dev commitment to the cipher suite, either, which differs from step 2 of + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.4.3 + * @dev . Also, the hash input is the concatenation of the uncompressed + * @dev points, not the compressed points as recommended in step 3. + + * @dev - In the calculation of the challenge value "c", the "u" value (i.e. + * @dev the value computed by Reggie as the nonce times the secp256k1 + * @dev generator point, see steps 5 and 7 of + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.3 + * @dev ) is replaced by its ethereum address, i.e. the lower 160 bits of the + * @dev keccak hash of the original u. This is because we only verify the + * @dev calculation of u up to its address, by abusing ECRECOVER. + * **************************************************************************** + * @dev SECURITY PROPERTIES + + * @dev Here are the security properties for this VRF: + + * @dev Full uniqueness: For any seed and valid VRF public key, there is + * @dev exactly one VRF output which can be proved to come from that seed, in + * @dev the sense that the proof will pass _verifyVRFProof. + + * @dev Full collision resistance: It's cryptographically infeasible to find + * @dev two seeds with same VRF output from a fixed, valid VRF key + + * @dev Full pseudorandomness: Absent the proofs that the VRF outputs are + * @dev derived from a given seed, the outputs are computationally + * @dev indistinguishable from randomness. + + * @dev https://eprint.iacr.org/2017/099.pdf, Appendix B contains the proofs + * @dev for these properties. + + * @dev For secp256k1, the key validation described in section + * @dev https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.6 + * @dev is unnecessary, because secp256k1 has cofactor 1, and the + * @dev representation of the public key used here (affine x- and y-ordinates + * @dev of the secp256k1 point on the standard y^2=x^3+7 curve) cannot refer to + * @dev the point at infinity. + * **************************************************************************** + * @dev OTHER SECURITY CONSIDERATIONS + * + * @dev The seed input to the VRF could in principle force an arbitrary amount + * @dev of work in _hashToCurve, by requiring extra rounds of hashing and + * @dev checking whether that's yielded the x ordinate of a secp256k1 point. + * @dev However, under the Random Oracle Model the probability of choosing a + * @dev point which forces n extra rounds in _hashToCurve is 2⁻ⁿ. The base cost + * @dev for calling _hashToCurve is about 25,000 gas, and each round of checking + * @dev for a valid x ordinate costs about 15,555 gas, so to find a seed for + * @dev which _hashToCurve would cost more than 2,017,000 gas, one would have to + * @dev try, in expectation, about 2¹²⁸ seeds, which is infeasible for any + * @dev foreseeable computational resources. (25,000 + 128 * 15,555 < 2,017,000.) + + * @dev Since the gas block limit for the Ethereum main net is 10,000,000 gas, + * @dev this means it is infeasible for an adversary to prevent correct + * @dev operation of this contract by choosing an adverse seed. + + * @dev (See TestMeasureHashToCurveGasCost for verification of the gas cost for + * @dev _hashToCurve.) + + * @dev It may be possible to make a secure constant-time _hashToCurve function. + * @dev See notes in _hashToCurve docstring. +*/ +contract VRFOld { + // See https://www.secg.org/sec2-v2.pdf, section 2.4.1, for these constants. + // Number of points in Secp256k1 + uint256 private constant GROUP_ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141; + // Prime characteristic of the galois field over which Secp256k1 is defined + uint256 private constant FIELD_SIZE = + // solium-disable-next-line indentation + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F; + uint256 private constant WORD_LENGTH_BYTES = 0x20; + + // (base^exponent) % FIELD_SIZE + // Cribbed from https://medium.com/@rbkhmrcr/precompiles-solidity-e5d29bd428c4 + function _bigModExp(uint256 base, uint256 exponent) internal view returns (uint256 exponentiation) { + uint256 callResult; + uint256[6] memory bigModExpContractInputs; + bigModExpContractInputs[0] = WORD_LENGTH_BYTES; // Length of base + bigModExpContractInputs[1] = WORD_LENGTH_BYTES; // Length of exponent + bigModExpContractInputs[2] = WORD_LENGTH_BYTES; // Length of modulus + bigModExpContractInputs[3] = base; + bigModExpContractInputs[4] = exponent; + bigModExpContractInputs[5] = FIELD_SIZE; + uint256[1] memory output; + assembly { + callResult := staticcall( + not(0), // Gas cost: no limit + 0x05, // Bigmodexp contract address + bigModExpContractInputs, + 0xc0, // Length of input segment: 6*0x20-bytes + output, + 0x20 // Length of output segment + ) + } + if (callResult == 0) { + // solhint-disable-next-line gas-custom-errors + revert("bigModExp failure!"); + } + return output[0]; + } + + // Let q=FIELD_SIZE. q % 4 = 3, ∴ x≡r^2 mod q ⇒ x^SQRT_POWER≡±r mod q. See + // https://en.wikipedia.org/wiki/Modular_square_root#Prime_or_prime_power_modulus + uint256 private constant SQRT_POWER = (FIELD_SIZE + 1) >> 2; + + // Computes a s.t. a^2 = x in the field. Assumes a exists + function _squareRoot(uint256 x) internal view returns (uint256) { + return _bigModExp(x, SQRT_POWER); + } + + // The value of y^2 given that (x,y) is on secp256k1. + function _ySquared(uint256 x) internal pure returns (uint256) { + // Curve is y^2=x^3+7. See section 2.4.1 of https://www.secg.org/sec2-v2.pdf + uint256 xCubed = mulmod(x, mulmod(x, x, FIELD_SIZE), FIELD_SIZE); + return addmod(xCubed, 7, FIELD_SIZE); + } + + // True iff p is on secp256k1 + function _isOnCurve(uint256[2] memory p) internal pure returns (bool) { + // Section 2.3.6. in https://www.secg.org/sec1-v2.pdf + // requires each ordinate to be in [0, ..., FIELD_SIZE-1] + // solhint-disable-next-line gas-custom-errors + require(p[0] < FIELD_SIZE, "invalid x-ordinate"); + // solhint-disable-next-line gas-custom-errors + require(p[1] < FIELD_SIZE, "invalid y-ordinate"); + return _ySquared(p[0]) == mulmod(p[1], p[1], FIELD_SIZE); + } + + // Hash x uniformly into {0, ..., FIELD_SIZE-1}. + function _fieldHash(bytes memory b) internal pure returns (uint256 x_) { + x_ = uint256(keccak256(b)); + // Rejecting if x >= FIELD_SIZE corresponds to step 2.1 in section 2.3.4 of + // http://www.secg.org/sec1-v2.pdf , which is part of the definition of + // string_to_point in the IETF draft + while (x_ >= FIELD_SIZE) { + x_ = uint256(keccak256(abi.encodePacked(x_))); + } + return x_; + } + + // Hash b to a random point which hopefully lies on secp256k1. The y ordinate + // is always even, due to + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.4.1.1 + // step 5.C, which references arbitrary_string_to_point, defined in + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.5 as + // returning the point with given x ordinate, and even y ordinate. + function _newCandidateSecp256k1Point(bytes memory b) internal view returns (uint256[2] memory p) { + unchecked { + p[0] = _fieldHash(b); + p[1] = _squareRoot(_ySquared(p[0])); + if (p[1] % 2 == 1) { + // Note that 0 <= p[1] < FIELD_SIZE + // so this cannot wrap, we use unchecked to save gas. + p[1] = FIELD_SIZE - p[1]; + } + } + return p; + } + + // Domain-separation tag for initial hash in _hashToCurve. Corresponds to + // vrf.go/hashToCurveHashPrefix + uint256 internal constant HASH_TO_CURVE_HASH_PREFIX = 1; + + // Cryptographic hash function onto the curve. + // + // Corresponds to algorithm in section 5.4.1.1 of the draft standard. (But see + // DESIGN NOTES above for slight differences.) + // + // TODO(alx): Implement a bounded-computation hash-to-curve, as described in + // "Construction of Rational Points on Elliptic Curves over Finite Fields" + // http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.831.5299&rep=rep1&type=pdf + // and suggested by + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-01#section-5.2.2 + // (Though we can't used exactly that because secp256k1's j-invariant is 0.) + // + // This would greatly simplify the analysis in "OTHER SECURITY CONSIDERATIONS" + // https://www.pivotaltracker.com/story/show/171120900 + function _hashToCurve(uint256[2] memory pk, uint256 input) internal view returns (uint256[2] memory rv) { + rv = _newCandidateSecp256k1Point(abi.encodePacked(HASH_TO_CURVE_HASH_PREFIX, pk, input)); + while (!_isOnCurve(rv)) { + rv = _newCandidateSecp256k1Point(abi.encodePacked(rv[0])); + } + return rv; + } + + /** ********************************************************************* + * @notice Check that product==scalar*multiplicand + * + * @dev Based on Vitalik Buterin's idea in ethresear.ch post cited below. + * + * @param multiplicand: secp256k1 point + * @param scalar: non-zero GF(GROUP_ORDER) scalar + * @param product: secp256k1 expected to be multiplier * multiplicand + * @return verifies true iff product==scalar*multiplicand, with cryptographically high probability + */ + function _ecmulVerify( + uint256[2] memory multiplicand, + uint256 scalar, + uint256[2] memory product + ) internal pure returns (bool verifies) { + // solhint-disable-next-line gas-custom-errors + require(scalar != 0, "zero scalar"); // Rules out an ecrecover failure case + uint256 x = multiplicand[0]; // x ordinate of multiplicand + uint8 v = multiplicand[1] % 2 == 0 ? 27 : 28; // parity of y ordinate + // https://ethresear.ch/t/you-can-kinda-abuse-ecrecover-to-do-ecmul-in-secp256k1-today/2384/9 + // Point corresponding to address ecrecover(0, v, x, s=scalar*x) is + // (x⁻¹ mod GROUP_ORDER) * (scalar * x * multiplicand - 0 * g), i.e. + // scalar*multiplicand. See https://crypto.stackexchange.com/a/18106 + bytes32 scalarTimesX = bytes32(mulmod(scalar, x, GROUP_ORDER)); + address actual = ecrecover(bytes32(0), v, bytes32(x), scalarTimesX); + // Explicit conversion to address takes bottom 160 bits + address expected = address(uint160(uint256(keccak256(abi.encodePacked(product))))); + return (actual == expected); + } + + // Returns x1/z1-x2/z2=(x1z2-x2z1)/(z1z2) in projective coordinates on P¹(𝔽ₙ) + function _projectiveSub( + uint256 x1, + uint256 z1, + uint256 x2, + uint256 z2 + ) internal pure returns (uint256 x3, uint256 z3) { + unchecked { + uint256 num1 = mulmod(z2, x1, FIELD_SIZE); + // Note this cannot wrap since x2 is a point in [0, FIELD_SIZE-1] + // we use unchecked to save gas. + uint256 num2 = mulmod(FIELD_SIZE - x2, z1, FIELD_SIZE); + (x3, z3) = (addmod(num1, num2, FIELD_SIZE), mulmod(z1, z2, FIELD_SIZE)); + } + return (x3, z3); + } + + // Returns x1/z1*x2/z2=(x1x2)/(z1z2), in projective coordinates on P¹(𝔽ₙ) + function _projectiveMul( + uint256 x1, + uint256 z1, + uint256 x2, + uint256 z2 + ) internal pure returns (uint256 x3, uint256 z3) { + (x3, z3) = (mulmod(x1, x2, FIELD_SIZE), mulmod(z1, z2, FIELD_SIZE)); + return (x3, z3); + } + + /** ************************************************************************** + @notice Computes elliptic-curve sum, in projective co-ordinates + + @dev Using projective coordinates avoids costly divisions + + @dev To use this with p and q in affine coordinates, call + @dev _projectiveECAdd(px, py, qx, qy). This will return + @dev the addition of (px, py, 1) and (qx, qy, 1), in the + @dev secp256k1 group. + + @dev This can be used to calculate the z which is the inverse to zInv + @dev in isValidVRFOutput. But consider using a faster + @dev re-implementation such as ProjectiveECAdd in the golang vrf package. + + @dev This function assumes [px,py,1],[qx,qy,1] are valid projective + coordinates of secp256k1 points. That is safe in this contract, + because this method is only used by _linearCombination, which checks + points are on the curve via ecrecover. + ************************************************************************** + @param px The first affine coordinate of the first summand + @param py The second affine coordinate of the first summand + @param qx The first affine coordinate of the second summand + @param qy The second affine coordinate of the second summand + + (px,py) and (qx,qy) must be distinct, valid secp256k1 points. + ************************************************************************** + Return values are projective coordinates of [px,py,1]+[qx,qy,1] as points + on secp256k1, in P²(𝔽ₙ) + @return sx + @return sy + @return sz + */ + function _projectiveECAdd( + uint256 px, + uint256 py, + uint256 qx, + uint256 qy + ) internal pure returns (uint256 sx, uint256 sy, uint256 sz) { + unchecked { + // See "Group law for E/K : y^2 = x^3 + ax + b", in section 3.1.2, p. 80, + // "Guide to Elliptic Curve Cryptography" by Hankerson, Menezes and Vanstone + // We take the equations there for (sx,sy), and homogenize them to + // projective coordinates. That way, no inverses are required, here, and we + // only need the one inverse in _affineECAdd. + + // We only need the "point addition" equations from Hankerson et al. Can + // skip the "point doubling" equations because p1 == p2 is cryptographically + // impossible, and required not to be the case in _linearCombination. + + // Add extra "projective coordinate" to the two points + (uint256 z1, uint256 z2) = (1, 1); + + // (lx, lz) = (qy-py)/(qx-px), i.e., gradient of secant line. + // Cannot wrap since px and py are in [0, FIELD_SIZE-1] + uint256 lx = addmod(qy, FIELD_SIZE - py, FIELD_SIZE); + uint256 lz = addmod(qx, FIELD_SIZE - px, FIELD_SIZE); + + uint256 dx; // Accumulates denominator from sx calculation + // sx=((qy-py)/(qx-px))^2-px-qx + (sx, dx) = _projectiveMul(lx, lz, lx, lz); // ((qy-py)/(qx-px))^2 + (sx, dx) = _projectiveSub(sx, dx, px, z1); // ((qy-py)/(qx-px))^2-px + (sx, dx) = _projectiveSub(sx, dx, qx, z2); // ((qy-py)/(qx-px))^2-px-qx + + uint256 dy; // Accumulates denominator from sy calculation + // sy=((qy-py)/(qx-px))(px-sx)-py + (sy, dy) = _projectiveSub(px, z1, sx, dx); // px-sx + (sy, dy) = _projectiveMul(sy, dy, lx, lz); // ((qy-py)/(qx-px))(px-sx) + (sy, dy) = _projectiveSub(sy, dy, py, z1); // ((qy-py)/(qx-px))(px-sx)-py + + if (dx != dy) { + // Cross-multiply to put everything over a common denominator + sx = mulmod(sx, dy, FIELD_SIZE); + sy = mulmod(sy, dx, FIELD_SIZE); + sz = mulmod(dx, dy, FIELD_SIZE); + } else { + // Already over a common denominator, use that for z ordinate + sz = dx; + } + } + return (sx, sy, sz); + } + + // p1+p2, as affine points on secp256k1. + // + // invZ must be the inverse of the z returned by _projectiveECAdd(p1, p2). + // It is computed off-chain to save gas. + // + // p1 and p2 must be distinct, because _projectiveECAdd doesn't handle + // point doubling. + function _affineECAdd( + uint256[2] memory p1, + uint256[2] memory p2, + uint256 invZ + ) internal pure returns (uint256[2] memory) { + uint256 x; + uint256 y; + uint256 z; + (x, y, z) = _projectiveECAdd(p1[0], p1[1], p2[0], p2[1]); + // solhint-disable-next-line gas-custom-errors + require(mulmod(z, invZ, FIELD_SIZE) == 1, "invZ must be inverse of z"); + // Clear the z ordinate of the projective representation by dividing through + // by it, to obtain the affine representation + return [mulmod(x, invZ, FIELD_SIZE), mulmod(y, invZ, FIELD_SIZE)]; + } + + // True iff address(c*p+s*g) == lcWitness, where g is generator. (With + // cryptographically high probability.) + function _verifyLinearCombinationWithGenerator( + uint256 c, + uint256[2] memory p, + uint256 s, + address lcWitness + ) internal pure returns (bool) { + // Rule out ecrecover failure modes which return address 0. + unchecked { + // solhint-disable-next-line gas-custom-errors + require(lcWitness != address(0), "bad witness"); + uint8 v = (p[1] % 2 == 0) ? 27 : 28; // parity of y-ordinate of p + // Note this cannot wrap (X - Y % X), but we use unchecked to save + // gas. + bytes32 pseudoHash = bytes32(GROUP_ORDER - mulmod(p[0], s, GROUP_ORDER)); // -s*p[0] + bytes32 pseudoSignature = bytes32(mulmod(c, p[0], GROUP_ORDER)); // c*p[0] + // https://ethresear.ch/t/you-can-kinda-abuse-ecrecover-to-do-ecmul-in-secp256k1-today/2384/9 + // The point corresponding to the address returned by + // ecrecover(-s*p[0],v,p[0],c*p[0]) is + // (p[0]⁻¹ mod GROUP_ORDER)*(c*p[0]-(-s)*p[0]*g)=c*p+s*g. + // See https://crypto.stackexchange.com/a/18106 + // https://bitcoin.stackexchange.com/questions/38351/ecdsa-v-r-s-what-is-v + address computed = ecrecover(pseudoHash, v, bytes32(p[0]), pseudoSignature); + return computed == lcWitness; + } + } + + // c*p1 + s*p2. Requires cp1Witness=c*p1 and sp2Witness=s*p2. Also + // requires cp1Witness != sp2Witness (which is fine for this application, + // since it is cryptographically impossible for them to be equal. In the + // (cryptographically impossible) case that a prover accidentally derives + // a proof with equal c*p1 and s*p2, they should retry with a different + // proof nonce.) Assumes that all points are on secp256k1 + // (which is checked in _verifyVRFProof below.) + function _linearCombination( + uint256 c, + uint256[2] memory p1, + uint256[2] memory cp1Witness, + uint256 s, + uint256[2] memory p2, + uint256[2] memory sp2Witness, + uint256 zInv + ) internal pure returns (uint256[2] memory) { + unchecked { + // Note we are relying on the wrap around here + // solhint-disable-next-line gas-custom-errors + require((cp1Witness[0] % FIELD_SIZE) != (sp2Witness[0] % FIELD_SIZE), "points in sum must be distinct"); + // solhint-disable-next-line gas-custom-errors + require(_ecmulVerify(p1, c, cp1Witness), "First mul check failed"); + // solhint-disable-next-line gas-custom-errors + require(_ecmulVerify(p2, s, sp2Witness), "Second mul check failed"); + return _affineECAdd(cp1Witness, sp2Witness, zInv); + } + } + + // Domain-separation tag for the hash taken in _scalarFromCurvePoints. + // Corresponds to scalarFromCurveHashPrefix in vrf.go + uint256 internal constant SCALAR_FROM_CURVE_POINTS_HASH_PREFIX = 2; + + // Pseudo-random number from inputs. Matches vrf.go/_scalarFromCurvePoints, and + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.4.3 + // The draft calls (in step 7, via the definition of string_to_int, in + // https://datatracker.ietf.org/doc/html/rfc8017#section-4.2 ) for taking the + // first hash without checking that it corresponds to a number less than the + // group order, which will lead to a slight bias in the sample. + // + // TODO(alx): We could save a bit of gas by following the standard here and + // using the compressed representation of the points, if we collated the y + // parities into a single bytes32. + // https://www.pivotaltracker.com/story/show/171120588 + function _scalarFromCurvePoints( + uint256[2] memory hash, + uint256[2] memory pk, + uint256[2] memory gamma, + address uWitness, + uint256[2] memory v + ) internal pure returns (uint256 s) { + return uint256(keccak256(abi.encodePacked(SCALAR_FROM_CURVE_POINTS_HASH_PREFIX, hash, pk, gamma, v, uWitness))); + } + + // True if (gamma, c, s) is a correctly constructed randomness proof from pk + // and seed. zInv must be the inverse of the third ordinate from + // _projectiveECAdd applied to cGammaWitness and sHashWitness. Corresponds to + // section 5.3 of the IETF draft. + // + // TODO(alx): Since I'm only using pk in the ecrecover call, I could only pass + // the x ordinate, and the parity of the y ordinate in the top bit of uWitness + // (which I could make a uint256 without using any extra space.) Would save + // about 2000 gas. https://www.pivotaltracker.com/story/show/170828567 + function _verifyVRFProof( + uint256[2] memory pk, + uint256[2] memory gamma, + uint256 c, + uint256 s, + uint256 seed, + address uWitness, + uint256[2] memory cGammaWitness, + uint256[2] memory sHashWitness, + uint256 zInv + ) internal view { + unchecked { + // solhint-disable-next-line gas-custom-errors + require(_isOnCurve(pk), "public key is not on curve"); + // solhint-disable-next-line gas-custom-errors + require(_isOnCurve(gamma), "gamma is not on curve"); + // solhint-disable-next-line gas-custom-errors + require(_isOnCurve(cGammaWitness), "cGammaWitness is not on curve"); + // solhint-disable-next-line gas-custom-errors + require(_isOnCurve(sHashWitness), "sHashWitness is not on curve"); + // Step 5. of IETF draft section 5.3 (pk corresponds to 5.3's Y, and here + // we use the address of u instead of u itself. Also, here we add the + // terms instead of taking the difference, and in the proof construction in + // vrf.GenerateProof, we correspondingly take the difference instead of + // taking the sum as they do in step 7 of section 5.1.) + // solhint-disable-next-line gas-custom-errors + require(_verifyLinearCombinationWithGenerator(c, pk, s, uWitness), "addr(c*pk+s*g)!=_uWitness"); + // Step 4. of IETF draft section 5.3 (pk corresponds to Y, seed to alpha_string) + uint256[2] memory hash = _hashToCurve(pk, seed); + // Step 6. of IETF draft section 5.3, but see note for step 5 about +/- terms + uint256[2] memory v = _linearCombination(c, gamma, cGammaWitness, s, hash, sHashWitness, zInv); + // Steps 7. and 8. of IETF draft section 5.3 + uint256 derivedC = _scalarFromCurvePoints(hash, pk, gamma, uWitness, v); + // solhint-disable-next-line gas-custom-errors + require(c == derivedC, "invalid proof"); + } + } + + // Domain-separation tag for the hash used as the final VRF output. + // Corresponds to vrfRandomOutputHashPrefix in vrf.go + uint256 internal constant VRF_RANDOM_OUTPUT_HASH_PREFIX = 3; + + struct Proof { + uint256[2] pk; + uint256[2] gamma; + uint256 c; + uint256 s; + uint256 seed; + address uWitness; + uint256[2] cGammaWitness; + uint256[2] sHashWitness; + uint256 zInv; + } + + /* *************************************************************************** + * @notice Returns proof's output, if proof is valid. Otherwise reverts + + * @param proof vrf proof components + * @param seed seed used to generate the vrf output + * + * Throws if proof is invalid, otherwise: + * @return output i.e., the random output implied by the proof + * *************************************************************************** + */ + function _randomValueFromVRFProof(Proof memory proof, uint256 seed) internal view returns (uint256 output) { + _verifyVRFProof( + proof.pk, + proof.gamma, + proof.c, + proof.s, + seed, + proof.uWitness, + proof.cGammaWitness, + proof.sHashWitness, + proof.zInv + ); + output = uint256(keccak256(abi.encode(VRF_RANDOM_OUTPUT_HASH_PREFIX, proof.gamma))); + return output; + } +} diff --git a/contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts b/contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts index 62733bcad4..6b220f2f7c 100644 --- a/contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts +++ b/contracts/test/v0.8/automation/AutomationRegistry2_2.test.ts @@ -23,7 +23,7 @@ import { MockArbGasInfo__factory as MockArbGasInfoFactory } from '../../../typec import { MockOVMGasPriceOracle__factory as MockOVMGasPriceOracleFactory } from '../../../typechain/factories/MockOVMGasPriceOracle__factory' import { ChainModuleBase__factory as ChainModuleBaseFactory } from '../../../typechain/factories/ChainModuleBase__factory' import { ArbitrumModule__factory as ArbitrumModuleFactory } from '../../../typechain/factories/ArbitrumModule__factory' -import { OptimismModule__factory as OptimismModuleFactory } from '../../../typechain/factories/OptimismModule__factory' +import { OptimismModuleV2__factory as OptimismModuleV2Factory } from '../../../typechain/factories/OptimismModuleV2__factory' import { ILogAutomation__factory as ILogAutomationactory } from '../../../typechain/factories/ILogAutomation__factory' import { IAutomationForwarder__factory as IAutomationForwarderFactory } from '../../../typechain/factories/IAutomationForwarder__factory' import { MockArbSys__factory as MockArbSysFactory } from '../../../typechain/factories/MockArbSys__factory' @@ -35,7 +35,7 @@ import { MockV3Aggregator } from '../../../typechain/MockV3Aggregator' import { UpkeepMock } from '../../../typechain/UpkeepMock' import { ChainModuleBase } from '../../../typechain/ChainModuleBase' import { ArbitrumModule } from '../../../typechain/ArbitrumModule' -import { OptimismModule } from '../../../typechain/OptimismModule' +import { OptimismModuleV2 } from '../../../typechain/OptimismModuleV2' import { UpkeepTranscoder } from '../../../typechain/UpkeepTranscoder' import { IChainModule, UpkeepAutoFunder } from '../../../typechain' import { @@ -148,7 +148,7 @@ let upkeepMockFactory: UpkeepMockFactory let upkeepAutoFunderFactory: UpkeepAutoFunderFactory let chainModuleBaseFactory: ChainModuleBaseFactory let arbitrumModuleFactory: ArbitrumModuleFactory -let optimismModuleFactory: OptimismModuleFactory +let optimismModuleV2Factory: OptimismModuleV2Factory let streamsLookupUpkeepFactory: StreamsLookupUpkeepFactory let personas: Personas @@ -169,7 +169,7 @@ let ltUpkeep: MockContract let transcoder: UpkeepTranscoder let chainModuleBase: ChainModuleBase let arbitrumModule: ArbitrumModule -let optimismModule: OptimismModule +let optimismModule: OptimismModuleV2 let streamsLookupUpkeep: StreamsLookupUpkeep let automationUtils: AutomationCompatibleUtils @@ -430,7 +430,8 @@ describe('AutomationRegistry2_2', () => { await ethers.getContractFactory('UpkeepAutoFunder') chainModuleBaseFactory = await ethers.getContractFactory('ChainModuleBase') arbitrumModuleFactory = await ethers.getContractFactory('ArbitrumModule') - optimismModuleFactory = await ethers.getContractFactory('OptimismModule') + optimismModuleV2Factory = + await ethers.getContractFactory('OptimismModuleV2') streamsLookupUpkeepFactory = await ethers.getContractFactory( 'StreamsLookupUpkeep', ) @@ -844,7 +845,7 @@ describe('AutomationRegistry2_2', () => { .deploy() chainModuleBase = await chainModuleBaseFactory.connect(owner).deploy() arbitrumModule = await arbitrumModuleFactory.connect(owner).deploy() - optimismModule = await optimismModuleFactory.connect(owner).deploy() + optimismModule = await optimismModuleV2Factory.connect(owner).deploy() streamsLookupUpkeep = await streamsLookupUpkeepFactory .connect(owner) .deploy( diff --git a/contracts/test/v0.8/automation/AutomationRegistry2_3.test.ts b/contracts/test/v0.8/automation/AutomationRegistry2_3.test.ts index 9a57226969..f3c2d9bb98 100644 --- a/contracts/test/v0.8/automation/AutomationRegistry2_3.test.ts +++ b/contracts/test/v0.8/automation/AutomationRegistry2_3.test.ts @@ -23,9 +23,8 @@ import { MockArbGasInfo__factory as MockArbGasInfoFactory } from '../../../typec import { MockOVMGasPriceOracle__factory as MockOVMGasPriceOracleFactory } from '../../../typechain/factories/MockOVMGasPriceOracle__factory' import { ChainModuleBase__factory as ChainModuleBaseFactory } from '../../../typechain/factories/ChainModuleBase__factory' import { ArbitrumModule__factory as ArbitrumModuleFactory } from '../../../typechain/factories/ArbitrumModule__factory' -import { OptimismModule__factory as OptimismModuleFactory } from '../../../typechain/factories/OptimismModule__factory' +import { OptimismModuleV2__factory as OptimismModuleV2Factory } from '../../../typechain/factories/OptimismModuleV2__factory' import { ILogAutomation__factory as ILogAutomationactory } from '../../../typechain/factories/ILogAutomation__factory' -import { IAutomationForwarder__factory as IAutomationForwarderFactory } from '../../../typechain/factories/IAutomationForwarder__factory' import { MockArbSys__factory as MockArbSysFactory } from '../../../typechain/factories/MockArbSys__factory' import { AutomationCompatibleUtils } from '../../../typechain/AutomationCompatibleUtils' import { MockArbGasInfo } from '../../../typechain/MockArbGasInfo' @@ -35,7 +34,7 @@ import { MockV3Aggregator } from '../../../typechain/MockV3Aggregator' import { UpkeepMock } from '../../../typechain/UpkeepMock' import { ChainModuleBase } from '../../../typechain/ChainModuleBase' import { ArbitrumModule } from '../../../typechain/ArbitrumModule' -import { OptimismModule } from '../../../typechain/OptimismModule' +import { OptimismModuleV2 } from '../../../typechain/OptimismModuleV2' import { UpkeepTranscoder } from '../../../typechain/UpkeepTranscoder' import { IChainModule, UpkeepAutoFunder } from '../../../typechain' import { @@ -153,7 +152,7 @@ let upkeepMockFactory: UpkeepMockFactory let upkeepAutoFunderFactory: UpkeepAutoFunderFactory let chainModuleBaseFactory: ChainModuleBaseFactory let arbitrumModuleFactory: ArbitrumModuleFactory -let optimismModuleFactory: OptimismModuleFactory +let optimismModuleFactory: OptimismModuleV2Factory let streamsLookupUpkeepFactory: StreamsLookupUpkeepFactory let personas: Personas @@ -175,7 +174,7 @@ let ltUpkeep: MockContract let transcoder: UpkeepTranscoder let chainModuleBase: ChainModuleBase let arbitrumModule: ArbitrumModule -let optimismModule: OptimismModule +let optimismModule: OptimismModuleV2 let streamsLookupUpkeep: StreamsLookupUpkeep let automationUtils: AutomationCompatibleUtils let automationUtils2_3: AutomationUtils2_3 @@ -443,7 +442,7 @@ describe('AutomationRegistry2_3', () => { await ethers.getContractFactory('UpkeepAutoFunder') chainModuleBaseFactory = await ethers.getContractFactory('ChainModuleBase') arbitrumModuleFactory = await ethers.getContractFactory('ArbitrumModule') - optimismModuleFactory = await ethers.getContractFactory('OptimismModule') + optimismModuleFactory = await ethers.getContractFactory('OptimismModuleV2') streamsLookupUpkeepFactory = await ethers.getContractFactory( 'StreamsLookupUpkeep', ) @@ -3107,44 +3106,6 @@ describe('AutomationRegistry2_3', () => { await getTransmitTx(registry, keeper1, [upkeepId2]) }) - it('reverts if called on a non existing ID', async () => { - await evmRevertCustomError( - registry - .connect(admin) - .withdrawFunds(upkeepId.add(1), await payee1.getAddress()), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('reverts if called by anyone but the admin', async () => { - await evmRevertCustomError( - registry - .connect(owner) - .withdrawFunds(upkeepId, await payee1.getAddress()), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('reverts if called on an uncanceled upkeep', async () => { - await evmRevertCustomError( - registry - .connect(admin) - .withdrawFunds(upkeepId, await payee1.getAddress()), - registry, - 'UpkeepNotCanceled', - ) - }) - - it('reverts if called with the 0 address', async () => { - await evmRevertCustomError( - registry.connect(admin).withdrawFunds(upkeepId, zeroAddress), - registry, - 'InvalidRecipient', - ) - }) - describe('after the registration is paused, then cancelled', () => { it('allows the admin to withdraw', async () => { const balance = await registry.getBalance(upkeepId) @@ -3514,46 +3475,6 @@ describe('AutomationRegistry2_3', () => { }) }) - describe('#getActiveUpkeepIDs', () => { - it('reverts if startIndex is out of bounds ', async () => { - await evmRevertCustomError( - registry.getActiveUpkeepIDs(numUpkeeps, 0), - registry, - 'IndexOutOfRange', - ) - await evmRevertCustomError( - registry.getActiveUpkeepIDs(numUpkeeps + 1, 0), - registry, - 'IndexOutOfRange', - ) - }) - - it('returns upkeep IDs bounded by maxCount', async () => { - let upkeepIds = await registry.getActiveUpkeepIDs(0, 1) - assert(upkeepIds.length == 1) - assert(upkeepIds[0].eq(upkeepId)) - upkeepIds = await registry.getActiveUpkeepIDs(1, 3) - assert(upkeepIds.length == 3) - expect(upkeepIds).to.deep.equal([ - afUpkeepId, - logUpkeepId, - streamsLookupUpkeepId, - ]) - }) - - it('returns as many ids as possible if maxCount > num available', async () => { - const upkeepIds = await registry.getActiveUpkeepIDs(1, numUpkeeps + 100) - assert(upkeepIds.length == numUpkeeps - 1) - }) - - it('returns all upkeep IDs if maxCount is 0', async () => { - let upkeepIds = await registry.getActiveUpkeepIDs(0, 0) - assert(upkeepIds.length == numUpkeeps) - upkeepIds = await registry.getActiveUpkeepIDs(2, 0) - assert(upkeepIds.length == numUpkeeps - 2) - }) - }) - describe('#getMaxPaymentForGas', () => { let maxl1CostWeiArbWithoutMultiplier: BigNumber let maxl1CostWeiOptWithoutMultiplier: BigNumber @@ -4225,1140 +4146,180 @@ describe('AutomationRegistry2_3', () => { }) }) - describe('#setPeerRegistryMigrationPermission() / #getPeerRegistryMigrationPermission()', () => { - const peer = randomAddress() - it('allows the owner to set the peer registries', async () => { - let permission = await registry.getPeerRegistryMigrationPermission(peer) - expect(permission).to.equal(0) - await registry.setPeerRegistryMigrationPermission(peer, 1) - permission = await registry.getPeerRegistryMigrationPermission(peer) - expect(permission).to.equal(1) - await registry.setPeerRegistryMigrationPermission(peer, 2) - permission = await registry.getPeerRegistryMigrationPermission(peer) - expect(permission).to.equal(2) - await registry.setPeerRegistryMigrationPermission(peer, 0) - permission = await registry.getPeerRegistryMigrationPermission(peer) - expect(permission).to.equal(0) - }) - it('reverts if passed an unsupported permission', async () => { - await expect( - registry.connect(admin).setPeerRegistryMigrationPermission(peer, 10), - ).to.be.reverted - }) - it('reverts if not called by the owner', async () => { - await expect( - registry.connect(admin).setPeerRegistryMigrationPermission(peer, 1), - ).to.be.revertedWith('Only callable by owner') - }) - }) - - describe('#pauseUpkeep', () => { - it('reverts if the registration does not exist', async () => { - await evmRevertCustomError( - registry.connect(keeper1).pauseUpkeep(upkeepId.add(1)), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('reverts if the upkeep is already canceled', async () => { - await registry.connect(admin).cancelUpkeep(upkeepId) - - await evmRevertCustomError( - registry.connect(admin).pauseUpkeep(upkeepId), - registry, - 'UpkeepCancelled', - ) - }) - - it('reverts if the upkeep is already paused', async () => { - await registry.connect(admin).pauseUpkeep(upkeepId) - - await evmRevertCustomError( - registry.connect(admin).pauseUpkeep(upkeepId), - registry, - 'OnlyUnpausedUpkeep', - ) - }) - - it('reverts if the caller is not the upkeep admin', async () => { - await evmRevertCustomError( - registry.connect(keeper1).pauseUpkeep(upkeepId), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('pauses the upkeep and emits an event', async () => { - const tx = await registry.connect(admin).pauseUpkeep(upkeepId) - await expect(tx).to.emit(registry, 'UpkeepPaused').withArgs(upkeepId) + describe('#cancelUpkeep', () => { + describe('when called by the owner', async () => { + it('immediately prevents upkeep', async () => { + await registry.connect(owner).cancelUpkeep(upkeepId) - const registration = await registry.getUpkeep(upkeepId) - assert.equal(registration.paused, true) + const tx = await getTransmitTx(registry, keeper1, [upkeepId]) + const receipt = await tx.wait() + const cancelledUpkeepReportLogs = + parseCancelledUpkeepReportLogs(receipt) + // exactly 1 CancelledUpkeepReport log should be emitted + assert.equal(cancelledUpkeepReportLogs.length, 1) + }) }) - }) - describe('#unpauseUpkeep', () => { - it('reverts if the registration does not exist', async () => { - await evmRevertCustomError( - registry.connect(keeper1).unpauseUpkeep(upkeepId.add(1)), - registry, - 'OnlyCallableByAdmin', - ) - }) + describe('when called by the admin', async () => { + it('immediately prevents upkeep', async () => { + await linkToken.connect(owner).approve(registry.address, toWei('100')) + await registry.connect(owner).addFunds(upkeepId, toWei('100')) + await registry.connect(admin).cancelUpkeep(upkeepId) - it('reverts if the upkeep is already canceled', async () => { - await registry.connect(owner).cancelUpkeep(upkeepId) + await getTransmitTx(registry, keeper1, [upkeepId]) - await evmRevertCustomError( - registry.connect(admin).unpauseUpkeep(upkeepId), - registry, - 'UpkeepCancelled', - ) - }) + for (let i = 0; i < cancellationDelay; i++) { + await ethers.provider.send('evm_mine', []) + } - it('marks the contract as paused', async () => { - assert.isFalse((await registry.getState()).state.paused) + const tx = await getTransmitTx(registry, keeper1, [upkeepId]) - await registry.connect(owner).pause() + const receipt = await tx.wait() + const cancelledUpkeepReportLogs = + parseCancelledUpkeepReportLogs(receipt) + // exactly 1 CancelledUpkeepReport log should be emitted + assert.equal(cancelledUpkeepReportLogs.length, 1) + }) - assert.isTrue((await registry.getState()).state.paused) - }) + describeMaybe('when an upkeep has been performed', async () => { + beforeEach(async () => { + await linkToken.connect(owner).approve(registry.address, toWei('100')) + await registry.connect(owner).addFunds(upkeepId, toWei('100')) + await getTransmitTx(registry, keeper1, [upkeepId]) + }) - it('reverts if the upkeep is not paused', async () => { - await evmRevertCustomError( - registry.connect(admin).unpauseUpkeep(upkeepId), - registry, - 'OnlyPausedUpkeep', - ) - }) + it('deducts a cancellation fee from the upkeep and adds to reserve', async () => { + const newMinUpkeepSpend = toWei('10') + const financeAdminAddress = await financeAdmin.getAddress() - it('reverts if the caller is not the upkeep admin', async () => { - await registry.connect(admin).pauseUpkeep(upkeepId) + await registry.connect(owner).setConfigTypeSafe( + signerAddresses, + keeperAddresses, + f, + { + checkGasLimit, + stalenessSeconds, + gasCeilingMultiplier, + maxCheckDataSize, + maxPerformDataSize, + maxRevertDataSize, + maxPerformGas, + fallbackGasPrice, + fallbackLinkPrice, + fallbackNativePrice, + transcoder: transcoder.address, + registrars: [], + upkeepPrivilegeManager: upkeepManager, + chainModule: chainModuleBase.address, + reorgProtectionEnabled: true, + financeAdmin: financeAdminAddress, + }, + offchainVersion, + offchainBytes, + [linkToken.address], + [ + { + gasFeePPB: paymentPremiumPPB, + flatFeeMilliCents, + priceFeed: linkUSDFeed.address, + fallbackPrice: fallbackLinkPrice, + minSpend: newMinUpkeepSpend, + decimals: 18, + }, + ], + ) - const registration = await registry.getUpkeep(upkeepId) + const payee1Before = await linkToken.balanceOf( + await payee1.getAddress(), + ) + const upkeepBefore = (await registry.getUpkeep(upkeepId)).balance + const ownerBefore = await registry.linkAvailableForPayment() - assert.equal(registration.paused, true) + const amountSpent = toWei('100').sub(upkeepBefore) + const cancellationFee = newMinUpkeepSpend.sub(amountSpent) - await evmRevertCustomError( - registry.connect(keeper1).unpauseUpkeep(upkeepId), - registry, - 'OnlyCallableByAdmin', - ) - }) + await registry.connect(admin).cancelUpkeep(upkeepId) - it('unpauses the upkeep and emits an event', async () => { - const originalCount = (await registry.getActiveUpkeepIDs(0, 0)).length + const payee1After = await linkToken.balanceOf( + await payee1.getAddress(), + ) + const upkeepAfter = (await registry.getUpkeep(upkeepId)).balance + const ownerAfter = await registry.linkAvailableForPayment() - await registry.connect(admin).pauseUpkeep(upkeepId) + // post upkeep balance should be previous balance minus cancellation fee + assert.isTrue(upkeepBefore.sub(cancellationFee).eq(upkeepAfter)) + // payee balance should not change + assert.isTrue(payee1Before.eq(payee1After)) + // owner should receive the cancellation fee + assert.isTrue(ownerAfter.sub(ownerBefore).eq(cancellationFee)) + }) - const tx = await registry.connect(admin).unpauseUpkeep(upkeepId) + it('deducts up to balance as cancellation fee', async () => { + // Very high min spend, should deduct whole balance as cancellation fees + const newMinUpkeepSpend = toWei('1000') + const financeAdminAddress = await financeAdmin.getAddress() - await expect(tx).to.emit(registry, 'UpkeepUnpaused').withArgs(upkeepId) + await registry.connect(owner).setConfigTypeSafe( + signerAddresses, + keeperAddresses, + f, + { + checkGasLimit, + stalenessSeconds, + gasCeilingMultiplier, + maxCheckDataSize, + maxPerformDataSize, + maxRevertDataSize, + maxPerformGas, + fallbackGasPrice, + fallbackLinkPrice, + fallbackNativePrice, + transcoder: transcoder.address, + registrars: [], + upkeepPrivilegeManager: upkeepManager, + chainModule: chainModuleBase.address, + reorgProtectionEnabled: true, + financeAdmin: financeAdminAddress, + }, + offchainVersion, + offchainBytes, + [linkToken.address], + [ + { + gasFeePPB: paymentPremiumPPB, + flatFeeMilliCents, + priceFeed: linkUSDFeed.address, + fallbackPrice: fallbackLinkPrice, + minSpend: newMinUpkeepSpend, + decimals: 18, + }, + ], + ) + const payee1Before = await linkToken.balanceOf( + await payee1.getAddress(), + ) + const upkeepBefore = (await registry.getUpkeep(upkeepId)).balance + const ownerBefore = await registry.linkAvailableForPayment() - const registration = await registry.getUpkeep(upkeepId) - assert.equal(registration.paused, false) + await registry.connect(admin).cancelUpkeep(upkeepId) + const payee1After = await linkToken.balanceOf( + await payee1.getAddress(), + ) + const ownerAfter = await registry.linkAvailableForPayment() + const upkeepAfter = (await registry.getUpkeep(upkeepId)).balance - const upkeepIds = await registry.getActiveUpkeepIDs(0, 0) - assert.equal(upkeepIds.length, originalCount) - }) - }) + // all upkeep balance is deducted for cancellation fee + assert.equal(upkeepAfter.toNumber(), 0) + // payee balance should not change + assert.isTrue(payee1After.eq(payee1Before)) + // all upkeep balance is transferred to the owner + assert.isTrue(ownerAfter.sub(ownerBefore).eq(upkeepBefore)) + }) - describe('#setUpkeepCheckData', () => { - it('reverts if the registration does not exist', async () => { - await evmRevertCustomError( - registry - .connect(keeper1) - .setUpkeepCheckData(upkeepId.add(1), randomBytes), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('reverts if the caller is not upkeep admin', async () => { - await evmRevertCustomError( - registry.connect(keeper1).setUpkeepCheckData(upkeepId, randomBytes), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('reverts if the upkeep is cancelled', async () => { - await registry.connect(admin).cancelUpkeep(upkeepId) - - await evmRevertCustomError( - registry.connect(admin).setUpkeepCheckData(upkeepId, randomBytes), - registry, - 'UpkeepCancelled', - ) - }) - - it('is allowed to update on paused upkeep', async () => { - await registry.connect(admin).pauseUpkeep(upkeepId) - await registry.connect(admin).setUpkeepCheckData(upkeepId, randomBytes) - - const registration = await registry.getUpkeep(upkeepId) - assert.equal(randomBytes, registration.checkData) - }) - - it('reverts if new data exceeds limit', async () => { - let longBytes = '0x' - for (let i = 0; i < 10000; i++) { - longBytes += '1' - } - - await evmRevertCustomError( - registry.connect(admin).setUpkeepCheckData(upkeepId, longBytes), - registry, - 'CheckDataExceedsLimit', - ) - }) - - it('updates the upkeep check data and emits an event', async () => { - const tx = await registry - .connect(admin) - .setUpkeepCheckData(upkeepId, randomBytes) - await expect(tx) - .to.emit(registry, 'UpkeepCheckDataSet') - .withArgs(upkeepId, randomBytes) - - const registration = await registry.getUpkeep(upkeepId) - assert.equal(randomBytes, registration.checkData) - }) - }) - - describe('#setUpkeepGasLimit', () => { - const newGasLimit = BigNumber.from('300000') - - it('reverts if the registration does not exist', async () => { - await evmRevertCustomError( - registry.connect(admin).setUpkeepGasLimit(upkeepId.add(1), newGasLimit), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('reverts if the upkeep is canceled', async () => { - await registry.connect(admin).cancelUpkeep(upkeepId) - await evmRevertCustomError( - registry.connect(admin).setUpkeepGasLimit(upkeepId, newGasLimit), - registry, - 'UpkeepCancelled', - ) - }) - - it('reverts if called by anyone but the admin', async () => { - await evmRevertCustomError( - registry.connect(owner).setUpkeepGasLimit(upkeepId, newGasLimit), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('reverts if new gas limit is out of bounds', async () => { - await evmRevertCustomError( - registry - .connect(admin) - .setUpkeepGasLimit(upkeepId, BigNumber.from('100')), - registry, - 'GasLimitOutsideRange', - ) - await evmRevertCustomError( - registry - .connect(admin) - .setUpkeepGasLimit(upkeepId, BigNumber.from('6000000')), - registry, - 'GasLimitOutsideRange', - ) - }) - - it('updates the gas limit successfully', async () => { - const initialGasLimit = (await registry.getUpkeep(upkeepId)).performGas - assert.equal(initialGasLimit, performGas.toNumber()) - await registry.connect(admin).setUpkeepGasLimit(upkeepId, newGasLimit) - const updatedGasLimit = (await registry.getUpkeep(upkeepId)).performGas - assert.equal(updatedGasLimit, newGasLimit.toNumber()) - }) - - it('emits a log', async () => { - const tx = await registry - .connect(admin) - .setUpkeepGasLimit(upkeepId, newGasLimit) - await expect(tx) - .to.emit(registry, 'UpkeepGasLimitSet') - .withArgs(upkeepId, newGasLimit) - }) - }) - - describe('#setUpkeepOffchainConfig', () => { - const newConfig = '0xc0ffeec0ffee' - - it('reverts if the registration does not exist', async () => { - await evmRevertCustomError( - registry - .connect(admin) - .setUpkeepOffchainConfig(upkeepId.add(1), newConfig), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('reverts if the upkeep is canceled', async () => { - await registry.connect(admin).cancelUpkeep(upkeepId) - await evmRevertCustomError( - registry.connect(admin).setUpkeepOffchainConfig(upkeepId, newConfig), - registry, - 'UpkeepCancelled', - ) - }) - - it('reverts if called by anyone but the admin', async () => { - await evmRevertCustomError( - registry.connect(owner).setUpkeepOffchainConfig(upkeepId, newConfig), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('updates the config successfully', async () => { - const initialConfig = (await registry.getUpkeep(upkeepId)).offchainConfig - assert.equal(initialConfig, '0x') - await registry.connect(admin).setUpkeepOffchainConfig(upkeepId, newConfig) - const updatedConfig = (await registry.getUpkeep(upkeepId)).offchainConfig - assert.equal(newConfig, updatedConfig) - }) - - it('emits a log', async () => { - const tx = await registry - .connect(admin) - .setUpkeepOffchainConfig(upkeepId, newConfig) - await expect(tx) - .to.emit(registry, 'UpkeepOffchainConfigSet') - .withArgs(upkeepId, newConfig) - }) - }) - - describe('#setUpkeepTriggerConfig', () => { - const newConfig = '0xdeadbeef' - - it('reverts if the registration does not exist', async () => { - await evmRevertCustomError( - registry - .connect(admin) - .setUpkeepTriggerConfig(upkeepId.add(1), newConfig), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('reverts if the upkeep is canceled', async () => { - await registry.connect(admin).cancelUpkeep(upkeepId) - await evmRevertCustomError( - registry.connect(admin).setUpkeepTriggerConfig(upkeepId, newConfig), - registry, - 'UpkeepCancelled', - ) - }) - - it('reverts if called by anyone but the admin', async () => { - await evmRevertCustomError( - registry.connect(owner).setUpkeepTriggerConfig(upkeepId, newConfig), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('emits a log', async () => { - const tx = await registry - .connect(admin) - .setUpkeepTriggerConfig(upkeepId, newConfig) - await expect(tx) - .to.emit(registry, 'UpkeepTriggerConfigSet') - .withArgs(upkeepId, newConfig) - }) - }) - - describe('#transferUpkeepAdmin', () => { - it('reverts when called by anyone but the current upkeep admin', async () => { - await evmRevertCustomError( - registry - .connect(payee1) - .transferUpkeepAdmin(upkeepId, await payee2.getAddress()), - registry, - 'OnlyCallableByAdmin', - ) - }) - - it('reverts when transferring to self', async () => { - await evmRevertCustomError( - registry - .connect(admin) - .transferUpkeepAdmin(upkeepId, await admin.getAddress()), - registry, - 'ValueNotChanged', - ) - }) - - it('reverts when the upkeep is cancelled', async () => { - await registry.connect(admin).cancelUpkeep(upkeepId) - - await evmRevertCustomError( - registry - .connect(admin) - .transferUpkeepAdmin(upkeepId, await keeper1.getAddress()), - registry, - 'UpkeepCancelled', - ) - }) - - it('allows cancelling transfer by reverting to zero address', async () => { - await registry - .connect(admin) - .transferUpkeepAdmin(upkeepId, await payee1.getAddress()) - const tx = await registry - .connect(admin) - .transferUpkeepAdmin(upkeepId, ethers.constants.AddressZero) - - await expect(tx) - .to.emit(registry, 'UpkeepAdminTransferRequested') - .withArgs( - upkeepId, - await admin.getAddress(), - ethers.constants.AddressZero, - ) - }) - - it('does not change the upkeep admin', async () => { - await registry - .connect(admin) - .transferUpkeepAdmin(upkeepId, await payee1.getAddress()) - - const upkeep = await registry.getUpkeep(upkeepId) - assert.equal(await admin.getAddress(), upkeep.admin) - }) - - it('emits an event announcing the new upkeep admin', async () => { - const tx = await registry - .connect(admin) - .transferUpkeepAdmin(upkeepId, await payee1.getAddress()) - - await expect(tx) - .to.emit(registry, 'UpkeepAdminTransferRequested') - .withArgs(upkeepId, await admin.getAddress(), await payee1.getAddress()) - }) - - it('does not emit an event when called with the same proposed upkeep admin', async () => { - await registry - .connect(admin) - .transferUpkeepAdmin(upkeepId, await payee1.getAddress()) - - const tx = await registry - .connect(admin) - .transferUpkeepAdmin(upkeepId, await payee1.getAddress()) - const receipt = await tx.wait() - assert.equal(receipt.logs.length, 0) - }) - }) - - describe('#acceptUpkeepAdmin', () => { - beforeEach(async () => { - // Start admin transfer to payee1 - await registry - .connect(admin) - .transferUpkeepAdmin(upkeepId, await payee1.getAddress()) - }) - - it('reverts when not called by the proposed upkeep admin', async () => { - await evmRevertCustomError( - registry.connect(payee2).acceptUpkeepAdmin(upkeepId), - registry, - 'OnlyCallableByProposedAdmin', - ) - }) - - it('reverts when the upkeep is cancelled', async () => { - await registry.connect(admin).cancelUpkeep(upkeepId) - - await evmRevertCustomError( - registry.connect(payee1).acceptUpkeepAdmin(upkeepId), - registry, - 'UpkeepCancelled', - ) - }) - - it('does change the admin', async () => { - await registry.connect(payee1).acceptUpkeepAdmin(upkeepId) - - const upkeep = await registry.getUpkeep(upkeepId) - assert.equal(await payee1.getAddress(), upkeep.admin) - }) - - it('emits an event announcing the new upkeep admin', async () => { - const tx = await registry.connect(payee1).acceptUpkeepAdmin(upkeepId) - await expect(tx) - .to.emit(registry, 'UpkeepAdminTransferred') - .withArgs(upkeepId, await admin.getAddress(), await payee1.getAddress()) - }) - }) - - describe('#withdrawOwnerFunds', () => { - it('can only be called by finance admin', async () => { - await evmRevertCustomError( - registry.connect(keeper1).withdrawLink(zeroAddress, 1), - registry, - 'OnlyFinanceAdmin', - ) - }) - - itMaybe('withdraws the collected fees to owner', async () => { - await registry.connect(admin).addFunds(upkeepId, toWei('100')) - const financeAdminAddress = await financeAdmin.getAddress() - // Very high min spend, whole balance as cancellation fees - const newMinUpkeepSpend = toWei('1000') - await registry.connect(owner).setConfigTypeSafe( - signerAddresses, - keeperAddresses, - f, - { - checkGasLimit, - stalenessSeconds, - gasCeilingMultiplier, - maxCheckDataSize, - maxPerformDataSize, - maxRevertDataSize, - maxPerformGas, - fallbackGasPrice, - fallbackLinkPrice, - fallbackNativePrice, - transcoder: transcoder.address, - registrars: [], - upkeepPrivilegeManager: upkeepManager, - chainModule: chainModuleBase.address, - reorgProtectionEnabled: true, - financeAdmin: financeAdminAddress, - }, - offchainVersion, - offchainBytes, - [linkToken.address], - [ - { - gasFeePPB: paymentPremiumPPB, - flatFeeMilliCents, - priceFeed: linkUSDFeed.address, - fallbackPrice: fallbackLinkPrice, - minSpend: newMinUpkeepSpend, - decimals: 18, - }, - ], - ) - const upkeepBalance = (await registry.getUpkeep(upkeepId)).balance - const ownerBefore = await linkToken.balanceOf(await owner.getAddress()) - - await registry.connect(owner).cancelUpkeep(upkeepId) - - // Transfered to owner balance on registry - let ownerRegistryBalance = await registry.linkAvailableForPayment() - assert.isTrue(ownerRegistryBalance.eq(upkeepBalance)) - - // Now withdraw - await registry - .connect(financeAdmin) - .withdrawLink(await owner.getAddress(), ownerRegistryBalance) - - ownerRegistryBalance = await registry.linkAvailableForPayment() - const ownerAfter = await linkToken.balanceOf(await owner.getAddress()) - - // Owner registry balance should be changed to 0 - assert.isTrue(ownerRegistryBalance.eq(BigNumber.from('0'))) - - // Owner should be credited with the balance - assert.isTrue(ownerBefore.add(upkeepBalance).eq(ownerAfter)) - }) - }) - - describe('#transferPayeeship', () => { - it('reverts when called by anyone but the current payee', async () => { - await evmRevertCustomError( - registry - .connect(payee2) - .transferPayeeship( - await keeper1.getAddress(), - await payee2.getAddress(), - ), - registry, - 'OnlyCallableByPayee', - ) - }) - - it('reverts when transferring to self', async () => { - await evmRevertCustomError( - registry - .connect(payee1) - .transferPayeeship( - await keeper1.getAddress(), - await payee1.getAddress(), - ), - registry, - 'ValueNotChanged', - ) - }) - - it('does not change the payee', async () => { - await registry - .connect(payee1) - .transferPayeeship( - await keeper1.getAddress(), - await payee2.getAddress(), - ) - - const info = await registry.getTransmitterInfo(await keeper1.getAddress()) - assert.equal(await payee1.getAddress(), info.payee) - }) - - it('emits an event announcing the new payee', async () => { - const tx = await registry - .connect(payee1) - .transferPayeeship( - await keeper1.getAddress(), - await payee2.getAddress(), - ) - await expect(tx) - .to.emit(registry, 'PayeeshipTransferRequested') - .withArgs( - await keeper1.getAddress(), - await payee1.getAddress(), - await payee2.getAddress(), - ) - }) - - it('does not emit an event when called with the same proposal', async () => { - await registry - .connect(payee1) - .transferPayeeship( - await keeper1.getAddress(), - await payee2.getAddress(), - ) - - const tx = await registry - .connect(payee1) - .transferPayeeship( - await keeper1.getAddress(), - await payee2.getAddress(), - ) - const receipt = await tx.wait() - assert.equal(receipt.logs.length, 0) - }) - }) - - describe('#acceptPayeeship', () => { - beforeEach(async () => { - await registry - .connect(payee1) - .transferPayeeship( - await keeper1.getAddress(), - await payee2.getAddress(), - ) - }) - - it('reverts when called by anyone but the proposed payee', async () => { - await evmRevertCustomError( - registry.connect(payee1).acceptPayeeship(await keeper1.getAddress()), - registry, - 'OnlyCallableByProposedPayee', - ) - }) - - it('emits an event announcing the new payee', async () => { - const tx = await registry - .connect(payee2) - .acceptPayeeship(await keeper1.getAddress()) - await expect(tx) - .to.emit(registry, 'PayeeshipTransferred') - .withArgs( - await keeper1.getAddress(), - await payee1.getAddress(), - await payee2.getAddress(), - ) - }) - - it('does change the payee', async () => { - await registry.connect(payee2).acceptPayeeship(await keeper1.getAddress()) - - const info = await registry.getTransmitterInfo(await keeper1.getAddress()) - assert.equal(await payee2.getAddress(), info.payee) - }) - }) - - describe('#pause', () => { - it('reverts if called by a non-owner', async () => { - await evmRevert( - registry.connect(keeper1).pause(), - 'Only callable by owner', - ) - }) - - it('marks the contract as paused', async () => { - assert.isFalse((await registry.getState()).state.paused) - - await registry.connect(owner).pause() - - assert.isTrue((await registry.getState()).state.paused) - }) - - it('Does not allow transmits when paused', async () => { - await registry.connect(owner).pause() - - await evmRevertCustomError( - getTransmitTx(registry, keeper1, [upkeepId]), - registry, - 'RegistryPaused', - ) - }) - - it('Does not allow creation of new upkeeps when paused', async () => { - await registry.connect(owner).pause() - - await evmRevertCustomError( - registry - .connect(owner) - .registerUpkeep( - mock.address, - performGas, - await admin.getAddress(), - Trigger.CONDITION, - linkToken.address, - '0x', - '0x', - '0x', - ), - registry, - 'RegistryPaused', - ) - }) - }) - - describe('#unpause', () => { - beforeEach(async () => { - await registry.connect(owner).pause() - }) - - it('reverts if called by a non-owner', async () => { - await evmRevert( - registry.connect(keeper1).unpause(), - 'Only callable by owner', - ) - }) - - it('marks the contract as not paused', async () => { - assert.isTrue((await registry.getState()).state.paused) - - await registry.connect(owner).unpause() - - assert.isFalse((await registry.getState()).state.paused) - }) - }) - - describe('#setPayees', () => { - const IGNORE_ADDRESS = '0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF' - - it('reverts when not called by the owner', async () => { - await evmRevert( - registry.connect(keeper1).setPayees(payees), - 'Only callable by owner', - ) - }) - - it('reverts with different numbers of payees than transmitters', async () => { - await evmRevertCustomError( - registry.connect(owner).setPayees([...payees, randomAddress()]), - registry, - 'ParameterLengthError', - ) - }) - - it('reverts if the payee is the zero address', async () => { - await blankRegistry.connect(owner).setConfigTypeSafe(...baseConfig) // used to test initial config - - await evmRevertCustomError( - blankRegistry // used to test initial config - .connect(owner) - .setPayees([ethers.constants.AddressZero, ...payees.slice(1)]), - registry, - 'InvalidPayee', - ) - }) - - itMaybe( - 'sets the payees when exisitng payees are zero address', - async () => { - //Initial payees should be zero address - await blankRegistry.connect(owner).setConfigTypeSafe(...baseConfig) // used to test initial config - - for (let i = 0; i < keeperAddresses.length; i++) { - const payee = ( - await blankRegistry.getTransmitterInfo(keeperAddresses[i]) - ).payee // used to test initial config - assert.equal(payee, zeroAddress) - } - - await blankRegistry.connect(owner).setPayees(payees) // used to test initial config - - for (let i = 0; i < keeperAddresses.length; i++) { - const payee = ( - await blankRegistry.getTransmitterInfo(keeperAddresses[i]) - ).payee - assert.equal(payee, payees[i]) - } - }, - ) - - it('does not change the payee if IGNORE_ADDRESS is used as payee', async () => { - const signers = Array.from({ length: 5 }, randomAddress) - const keepers = Array.from({ length: 5 }, randomAddress) - const payees = Array.from({ length: 5 }, randomAddress) - const newTransmitter = randomAddress() - const newPayee = randomAddress() - const ignoreAddresses = new Array(payees.length).fill(IGNORE_ADDRESS) - const newPayees = [...ignoreAddresses, newPayee] - // arbitrum registry - // configure registry with 5 keepers // optimism registry - await blankRegistry // used to test initial configurations - .connect(owner) - .setConfigTypeSafe( - signers, - keepers, - f, - config, - offchainVersion, - offchainBytes, - [], - [], - ) - // arbitrum registry - // set initial payees // optimism registry - await blankRegistry.connect(owner).setPayees(payees) // used to test initial configurations - // arbitrum registry - // add another keeper // optimism registry - await blankRegistry // used to test initial configurations - .connect(owner) - .setConfigTypeSafe( - [...signers, randomAddress()], - [...keepers, newTransmitter], - f, - config, - offchainVersion, - offchainBytes, - [], - [], - ) - // arbitrum registry - // update payee list // optimism registry // arbitrum registry - await blankRegistry.connect(owner).setPayees(newPayees) // used to test initial configurations // optimism registry - const ignored = await blankRegistry.getTransmitterInfo(newTransmitter) // used to test initial configurations - assert.equal(newPayee, ignored.payee) - assert.equal(ignored.active, true) - }) - - it('reverts if payee is non zero and owner tries to change payee', async () => { - const newPayees = [randomAddress(), ...payees.slice(1)] - - await evmRevertCustomError( - registry.connect(owner).setPayees(newPayees), - registry, - 'InvalidPayee', - ) - }) - - it('emits events for every payee added and removed', async () => { - const tx = await registry.connect(owner).setPayees(payees) - await expect(tx) - .to.emit(registry, 'PayeesUpdated') - .withArgs(keeperAddresses, payees) - }) - }) - - describe('#cancelUpkeep', () => { - it('reverts if the ID is not valid', async () => { - await evmRevertCustomError( - registry.connect(owner).cancelUpkeep(upkeepId.add(1)), - registry, - 'CannotCancel', - ) - }) - - it('reverts if called by a non-owner/non-admin', async () => { - await evmRevertCustomError( - registry.connect(keeper1).cancelUpkeep(upkeepId), - registry, - 'OnlyCallableByOwnerOrAdmin', - ) - }) - - describe('when called by the owner', async () => { - it('sets the registration to invalid immediately', async () => { - const tx = await registry.connect(owner).cancelUpkeep(upkeepId) - const receipt = await tx.wait() - const registration = await registry.getUpkeep(upkeepId) - assert.equal( - registration.maxValidBlocknumber.toNumber(), - receipt.blockNumber, - ) - }) - - it('emits an event', async () => { - const tx = await registry.connect(owner).cancelUpkeep(upkeepId) - const receipt = await tx.wait() - await expect(tx) - .to.emit(registry, 'UpkeepCanceled') - .withArgs(upkeepId, BigNumber.from(receipt.blockNumber)) - }) - - it('immediately prevents upkeep', async () => { - await registry.connect(owner).cancelUpkeep(upkeepId) - - const tx = await getTransmitTx(registry, keeper1, [upkeepId]) - const receipt = await tx.wait() - const cancelledUpkeepReportLogs = - parseCancelledUpkeepReportLogs(receipt) - // exactly 1 CancelledUpkeepReport log should be emitted - assert.equal(cancelledUpkeepReportLogs.length, 1) - }) - - it('does not revert if reverts if called multiple times', async () => { - await registry.connect(owner).cancelUpkeep(upkeepId) - await evmRevertCustomError( - registry.connect(owner).cancelUpkeep(upkeepId), - registry, - 'UpkeepCancelled', - ) - }) - - describe('when called by the owner when the admin has just canceled', () => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - // @ts-ignore - let oldExpiration: BigNumber - - beforeEach(async () => { - await registry.connect(admin).cancelUpkeep(upkeepId) - const registration = await registry.getUpkeep(upkeepId) - oldExpiration = registration.maxValidBlocknumber - }) - - it('reverts with proper error', async () => { - await evmRevertCustomError( - registry.connect(owner).cancelUpkeep(upkeepId), - registry, - 'UpkeepCancelled', - ) - }) - }) - }) - - describe('when called by the admin', async () => { - it('reverts if called again by the admin', async () => { - await registry.connect(admin).cancelUpkeep(upkeepId) - - await evmRevertCustomError( - registry.connect(admin).cancelUpkeep(upkeepId), - registry, - 'UpkeepCancelled', - ) - }) - - it('reverts if called by the owner after the timeout', async () => { - await registry.connect(admin).cancelUpkeep(upkeepId) - - for (let i = 0; i < cancellationDelay; i++) { - await ethers.provider.send('evm_mine', []) - } - - await evmRevertCustomError( - registry.connect(owner).cancelUpkeep(upkeepId), - registry, - 'UpkeepCancelled', - ) - }) - - it('sets the registration to invalid in 50 blocks', async () => { - const tx = await registry.connect(admin).cancelUpkeep(upkeepId) - const receipt = await tx.wait() - const registration = await registry.getUpkeep(upkeepId) - assert.equal( - registration.maxValidBlocknumber.toNumber(), - receipt.blockNumber + 50, - ) - }) - - it('emits an event', async () => { - const tx = await registry.connect(admin).cancelUpkeep(upkeepId) - const receipt = await tx.wait() - await expect(tx) - .to.emit(registry, 'UpkeepCanceled') - .withArgs( - upkeepId, - BigNumber.from(receipt.blockNumber + cancellationDelay), - ) - }) - - it('immediately prevents upkeep', async () => { - await linkToken.connect(owner).approve(registry.address, toWei('100')) - await registry.connect(owner).addFunds(upkeepId, toWei('100')) - await registry.connect(admin).cancelUpkeep(upkeepId) - - await getTransmitTx(registry, keeper1, [upkeepId]) - - for (let i = 0; i < cancellationDelay; i++) { - await ethers.provider.send('evm_mine', []) - } - - const tx = await getTransmitTx(registry, keeper1, [upkeepId]) - - const receipt = await tx.wait() - const cancelledUpkeepReportLogs = - parseCancelledUpkeepReportLogs(receipt) - // exactly 1 CancelledUpkeepReport log should be emitted - assert.equal(cancelledUpkeepReportLogs.length, 1) - }) - - describeMaybe('when an upkeep has been performed', async () => { - beforeEach(async () => { - await linkToken.connect(owner).approve(registry.address, toWei('100')) - await registry.connect(owner).addFunds(upkeepId, toWei('100')) - await getTransmitTx(registry, keeper1, [upkeepId]) - }) - - it('deducts a cancellation fee from the upkeep and adds to reserve', async () => { - const newMinUpkeepSpend = toWei('10') - const financeAdminAddress = await financeAdmin.getAddress() - - await registry.connect(owner).setConfigTypeSafe( - signerAddresses, - keeperAddresses, - f, - { - checkGasLimit, - stalenessSeconds, - gasCeilingMultiplier, - maxCheckDataSize, - maxPerformDataSize, - maxRevertDataSize, - maxPerformGas, - fallbackGasPrice, - fallbackLinkPrice, - fallbackNativePrice, - transcoder: transcoder.address, - registrars: [], - upkeepPrivilegeManager: upkeepManager, - chainModule: chainModuleBase.address, - reorgProtectionEnabled: true, - financeAdmin: financeAdminAddress, - }, - offchainVersion, - offchainBytes, - [linkToken.address], - [ - { - gasFeePPB: paymentPremiumPPB, - flatFeeMilliCents, - priceFeed: linkUSDFeed.address, - fallbackPrice: fallbackLinkPrice, - minSpend: newMinUpkeepSpend, - decimals: 18, - }, - ], - ) - - const payee1Before = await linkToken.balanceOf( - await payee1.getAddress(), - ) - const upkeepBefore = (await registry.getUpkeep(upkeepId)).balance - const ownerBefore = await registry.linkAvailableForPayment() - - const amountSpent = toWei('100').sub(upkeepBefore) - const cancellationFee = newMinUpkeepSpend.sub(amountSpent) - - await registry.connect(admin).cancelUpkeep(upkeepId) - - const payee1After = await linkToken.balanceOf( - await payee1.getAddress(), - ) - const upkeepAfter = (await registry.getUpkeep(upkeepId)).balance - const ownerAfter = await registry.linkAvailableForPayment() - - // post upkeep balance should be previous balance minus cancellation fee - assert.isTrue(upkeepBefore.sub(cancellationFee).eq(upkeepAfter)) - // payee balance should not change - assert.isTrue(payee1Before.eq(payee1After)) - // owner should receive the cancellation fee - assert.isTrue(ownerAfter.sub(ownerBefore).eq(cancellationFee)) - }) - - it('deducts up to balance as cancellation fee', async () => { - // Very high min spend, should deduct whole balance as cancellation fees - const newMinUpkeepSpend = toWei('1000') - const financeAdminAddress = await financeAdmin.getAddress() - - await registry.connect(owner).setConfigTypeSafe( - signerAddresses, - keeperAddresses, - f, - { - checkGasLimit, - stalenessSeconds, - gasCeilingMultiplier, - maxCheckDataSize, - maxPerformDataSize, - maxRevertDataSize, - maxPerformGas, - fallbackGasPrice, - fallbackLinkPrice, - fallbackNativePrice, - transcoder: transcoder.address, - registrars: [], - upkeepPrivilegeManager: upkeepManager, - chainModule: chainModuleBase.address, - reorgProtectionEnabled: true, - financeAdmin: financeAdminAddress, - }, - offchainVersion, - offchainBytes, - [linkToken.address], - [ - { - gasFeePPB: paymentPremiumPPB, - flatFeeMilliCents, - priceFeed: linkUSDFeed.address, - fallbackPrice: fallbackLinkPrice, - minSpend: newMinUpkeepSpend, - decimals: 18, - }, - ], - ) - const payee1Before = await linkToken.balanceOf( - await payee1.getAddress(), - ) - const upkeepBefore = (await registry.getUpkeep(upkeepId)).balance - const ownerBefore = await registry.linkAvailableForPayment() - - await registry.connect(admin).cancelUpkeep(upkeepId) - const payee1After = await linkToken.balanceOf( - await payee1.getAddress(), - ) - const ownerAfter = await registry.linkAvailableForPayment() - const upkeepAfter = (await registry.getUpkeep(upkeepId)).balance - - // all upkeep balance is deducted for cancellation fee - assert.equal(upkeepAfter.toNumber(), 0) - // payee balance should not change - assert.isTrue(payee1After.eq(payee1Before)) - // all upkeep balance is transferred to the owner - assert.isTrue(ownerAfter.sub(ownerBefore).eq(upkeepBefore)) - }) - - it('does not deduct cancellation fee if more than minUpkeepSpendDollars is spent', async () => { - // Very low min spend, already spent in one perform upkeep - const newMinUpkeepSpend = BigNumber.from(420) - const financeAdminAddress = await financeAdmin.getAddress() + it('does not deduct cancellation fee if more than minUpkeepSpendDollars is spent', async () => { + // Very low min spend, already spent in one perform upkeep + const newMinUpkeepSpend = BigNumber.from(420) + const financeAdminAddress = await financeAdmin.getAddress() await registry.connect(owner).setConfigTypeSafe( signerAddresses, @@ -5595,62 +4556,6 @@ describe('AutomationRegistry2_3', () => { }) }) - describe('#setUpkeepPrivilegeConfig() / #getUpkeepPrivilegeConfig()', () => { - it('reverts when non manager tries to set privilege config', async () => { - await evmRevertCustomError( - registry.connect(payee3).setUpkeepPrivilegeConfig(upkeepId, '0x1234'), - registry, - 'OnlyCallableByUpkeepPrivilegeManager', - ) - }) - - it('returns empty bytes for upkeep privilege config before setting', async () => { - const cfg = await registry.getUpkeepPrivilegeConfig(upkeepId) - assert.equal(cfg, '0x') - }) - - it('allows upkeep manager to set privilege config', async () => { - const tx = await registry - .connect(personas.Norbert) - .setUpkeepPrivilegeConfig(upkeepId, '0x1234') - await expect(tx) - .to.emit(registry, 'UpkeepPrivilegeConfigSet') - .withArgs(upkeepId, '0x1234') - - const cfg = await registry.getUpkeepPrivilegeConfig(upkeepId) - assert.equal(cfg, '0x1234') - }) - }) - - describe('#setAdminPrivilegeConfig() / #getAdminPrivilegeConfig()', () => { - const admin = randomAddress() - - it('reverts when non manager tries to set privilege config', async () => { - await evmRevertCustomError( - registry.connect(payee3).setAdminPrivilegeConfig(admin, '0x1234'), - registry, - 'OnlyCallableByUpkeepPrivilegeManager', - ) - }) - - it('returns empty bytes for upkeep privilege config before setting', async () => { - const cfg = await registry.getAdminPrivilegeConfig(admin) - assert.equal(cfg, '0x') - }) - - it('allows upkeep manager to set privilege config', async () => { - const tx = await registry - .connect(personas.Norbert) - .setAdminPrivilegeConfig(admin, '0x1234') - await expect(tx) - .to.emit(registry, 'AdminPrivilegeConfigSet') - .withArgs(admin, '0x1234') - - const cfg = await registry.getAdminPrivilegeConfig(admin) - assert.equal(cfg, '0x1234') - }) - }) - describe('transmitterPremiumSplit [ @skip-coverage ]', () => { beforeEach(async () => { await linkToken.connect(owner).approve(registry.address, toWei('100')) diff --git a/contracts/test/v0.8/automation/ZKSyncAutomationRegistry2_3.test.ts b/contracts/test/v0.8/automation/ZKSyncAutomationRegistry2_3.test.ts new file mode 100644 index 0000000000..95210cf644 --- /dev/null +++ b/contracts/test/v0.8/automation/ZKSyncAutomationRegistry2_3.test.ts @@ -0,0 +1,4403 @@ +import { ethers } from 'hardhat' +import { loadFixture } from '@nomicfoundation/hardhat-network-helpers' +import { assert, expect } from 'chai' +import { + BigNumber, + BigNumberish, + BytesLike, + Contract, + ContractFactory, + ContractReceipt, + ContractTransaction, + Signer, + Wallet, +} from 'ethers' +import { evmRevert, evmRevertCustomError } from '../../test-helpers/matchers' +import { getUsers, Personas } from '../../test-helpers/setup' +import { randomAddress, toWei } from '../../test-helpers/helpers' +import { StreamsLookupUpkeep__factory as StreamsLookupUpkeepFactory } from '../../../typechain/factories/StreamsLookupUpkeep__factory' +import { MockV3Aggregator__factory as MockV3AggregatorFactory } from '../../../typechain/factories/MockV3Aggregator__factory' +import { UpkeepMock__factory as UpkeepMockFactory } from '../../../typechain/factories/UpkeepMock__factory' +import { UpkeepAutoFunder__factory as UpkeepAutoFunderFactory } from '../../../typechain/factories/UpkeepAutoFunder__factory' +import { MockZKSyncSystemContext__factory as MockZKSyncSystemContextFactory } from '../../../typechain/factories/MockZKSyncSystemContext__factory' +import { ChainModuleBase__factory as ChainModuleBaseFactory } from '../../../typechain/factories/ChainModuleBase__factory' +import { MockGasBoundCaller__factory as MockGasBoundCallerFactory } from '../../../typechain/factories/MockGasBoundCaller__factory' +import { ILogAutomation__factory as ILogAutomationactory } from '../../../typechain/factories/ILogAutomation__factory' +import { AutomationCompatibleUtils } from '../../../typechain/AutomationCompatibleUtils' +import { StreamsLookupUpkeep } from '../../../typechain/StreamsLookupUpkeep' +import { MockV3Aggregator } from '../../../typechain/MockV3Aggregator' +import { MockGasBoundCaller } from '../../../typechain/MockGasBoundCaller' +import { UpkeepMock } from '../../../typechain/UpkeepMock' +import { ChainModuleBase } from '../../../typechain/ChainModuleBase' +import { UpkeepTranscoder } from '../../../typechain/UpkeepTranscoder' +import { MockZKSyncSystemContext } from '../../../typechain/MockZKSyncSystemContext' +import { IChainModule, UpkeepAutoFunder } from '../../../typechain' +import { + CancelledUpkeepReportEvent, + IAutomationRegistryMaster2_3 as IAutomationRegistry, + ReorgedUpkeepReportEvent, + StaleUpkeepReportEvent, + UpkeepPerformedEvent, +} from '../../../typechain/IAutomationRegistryMaster2_3' +import { + deployMockContract, + MockContract, +} from '@ethereum-waffle/mock-contract' +import { deployZKSyncRegistry23 } from './helpers' +import { AutomationUtils2_3 } from '../../../typechain/AutomationUtils2_3' + +const describeMaybe = process.env.SKIP_SLOW ? describe.skip : describe +const itMaybe = process.env.SKIP_SLOW ? it.skip : it + +// copied from AutomationRegistryInterface2_3.sol +enum UpkeepFailureReason { + NONE, + UPKEEP_CANCELLED, + UPKEEP_PAUSED, + TARGET_CHECK_REVERTED, + UPKEEP_NOT_NEEDED, + PERFORM_DATA_EXCEEDS_LIMIT, + INSUFFICIENT_BALANCE, + CHECK_CALLBACK_REVERTED, + REVERT_DATA_EXCEEDS_LIMIT, + REGISTRY_PAUSED, +} + +// copied from AutomationRegistryBase2_3.sol +enum Trigger { + CONDITION, + LOG, +} + +// un-exported types that must be extracted from the utils contract +type Report = Parameters[0] +type LogTrigger = Parameters[0] +type ConditionalTrigger = Parameters< + AutomationCompatibleUtils['_conditionalTrigger'] +>[0] +type Log = Parameters[0] +type OnChainConfig = Parameters[3] + +// ----------------------------------------------------------------------------------------------- + +// These values should match the constants declared in registry +let registryConditionalOverhead: BigNumber +let registryLogOverhead: BigNumber +let registryPerSignerGasOverhead: BigNumber +// let registryPerPerformByteGasOverhead: BigNumber +// let registryTransmitCalldataFixedBytesOverhead: BigNumber +// let registryTransmitCalldataPerSignerBytesOverhead: BigNumber +let cancellationDelay: number + +// This is the margin for gas that we test for. Gas charged should always be greater +// than total gas used in tx but should not increase beyond this margin +// const gasCalculationMargin = BigNumber.from(50_000) +// This is the margin for gas overhead estimation in checkUpkeep. The estimated gas +// overhead should be larger than actual gas overhead but should not increase beyond this margin +// const gasEstimationMargin = BigNumber.from(50_000) + +// 1 Link = 0.005 Eth +const linkUSD = BigNumber.from('2000000000') // 1 LINK = $20 +const nativeUSD = BigNumber.from('400000000000') // 1 ETH = $4000 +const gasWei = BigNumber.from(1000000000) // 1 gwei +// ----------------------------------------------------------------------------------------------- +// test-wide configs for upkeeps +const performGas = BigNumber.from('1000000') +const paymentPremiumBase = BigNumber.from('1000000000') +const paymentPremiumPPB = BigNumber.from('250000000') +const flatFeeMilliCents = BigNumber.from(0) + +const randomBytes = '0x1234abcd' +const emptyBytes = '0x' +const emptyBytes32 = + '0x0000000000000000000000000000000000000000000000000000000000000000' + +const pubdataGas = BigNumber.from(500000) +const transmitGasOverhead = 1_040_000 +const checkGasOverhead = 600_000 + +const stalenessSeconds = BigNumber.from(43820) +const gasCeilingMultiplier = BigNumber.from(2) +const checkGasLimit = BigNumber.from(10000000) +const fallbackGasPrice = gasWei.mul(BigNumber.from('2')) +const fallbackLinkPrice = linkUSD.div(BigNumber.from('2')) +const fallbackNativePrice = nativeUSD.div(BigNumber.from('2')) +const maxCheckDataSize = BigNumber.from(1000) +const maxPerformDataSize = BigNumber.from(1000) +const maxRevertDataSize = BigNumber.from(1000) +const maxPerformGas = BigNumber.from(5000000) +const minUpkeepSpend = BigNumber.from(0) +const f = 1 +const offchainVersion = 1 +const offchainBytes = '0x' +const zeroAddress = ethers.constants.AddressZero +const wrappedNativeTokenAddress = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2' +const epochAndRound5_1 = + '0x0000000000000000000000000000000000000000000000000000000000000501' + +let logTriggerConfig: string + +// ----------------------------------------------------------------------------------------------- + +// Smart contract factories +let linkTokenFactory: ContractFactory +let mockV3AggregatorFactory: MockV3AggregatorFactory +let mockGasBoundCallerFactory: MockGasBoundCallerFactory +let upkeepMockFactory: UpkeepMockFactory +let upkeepAutoFunderFactory: UpkeepAutoFunderFactory +let moduleBaseFactory: ChainModuleBaseFactory +let mockZKSyncSystemContextFactory: MockZKSyncSystemContextFactory +let streamsLookupUpkeepFactory: StreamsLookupUpkeepFactory +let personas: Personas + +// contracts +let linkToken: Contract +let linkUSDFeed: MockV3Aggregator +let nativeUSDFeed: MockV3Aggregator +let gasPriceFeed: MockV3Aggregator +let registry: IAutomationRegistry // default registry, used for most tests +let mgRegistry: IAutomationRegistry // "migrate registry" used in migration tests +let mock: UpkeepMock +let autoFunderUpkeep: UpkeepAutoFunder +let ltUpkeep: MockContract +let transcoder: UpkeepTranscoder +let moduleBase: ChainModuleBase +let mockGasBoundCaller: MockGasBoundCaller +let mockZKSyncSystemContext: MockZKSyncSystemContext +let streamsLookupUpkeep: StreamsLookupUpkeep +let automationUtils: AutomationCompatibleUtils +let automationUtils2_3: AutomationUtils2_3 + +function now() { + return Math.floor(Date.now() / 1000) +} + +async function getUpkeepID(tx: ContractTransaction): Promise { + const receipt = await tx.wait() + for (const event of receipt.events || []) { + if ( + event.args && + event.eventSignature == 'UpkeepRegistered(uint256,uint32,address)' + ) { + return event.args[0] + } + } + throw new Error('could not find upkeep ID in tx event logs') +} + +const getTriggerType = (upkeepId: BigNumber): Trigger => { + const hexBytes = ethers.utils.defaultAbiCoder.encode(['uint256'], [upkeepId]) + const bytes = ethers.utils.arrayify(hexBytes) + for (let idx = 4; idx < 15; idx++) { + if (bytes[idx] != 0) { + return Trigger.CONDITION + } + } + return bytes[15] as Trigger +} + +const encodeBlockTrigger = (conditionalTrigger: ConditionalTrigger) => { + return ( + '0x' + + automationUtils.interface + .encodeFunctionData('_conditionalTrigger', [conditionalTrigger]) + .slice(10) + ) +} + +const encodeLogTrigger = (logTrigger: LogTrigger) => { + return ( + '0x' + + automationUtils.interface + .encodeFunctionData('_logTrigger', [logTrigger]) + .slice(10) + ) +} + +const encodeLog = (log: Log) => { + return ( + '0x' + automationUtils.interface.encodeFunctionData('_log', [log]).slice(10) + ) +} + +const encodeReport = (report: Report) => { + return ( + '0x' + + automationUtils2_3.interface + .encodeFunctionData('_report', [report]) + .slice(10) + ) +} + +type UpkeepData = { + Id: BigNumberish + performGas: BigNumberish + performData: BytesLike + trigger: BytesLike +} + +const makeReport = (upkeeps: UpkeepData[]) => { + const upkeepIds = upkeeps.map((u) => u.Id) + const performGases = upkeeps.map((u) => u.performGas) + const triggers = upkeeps.map((u) => u.trigger) + const performDatas = upkeeps.map((u) => u.performData) + return encodeReport({ + fastGasWei: gasWei, + linkUSD, + upkeepIds, + gasLimits: performGases, + triggers, + performDatas, + }) +} + +const makeLatestBlockReport = async (upkeepsIDs: BigNumberish[]) => { + const latestBlock = await ethers.provider.getBlock('latest') + const upkeeps: UpkeepData[] = [] + for (let i = 0; i < upkeepsIDs.length; i++) { + upkeeps.push({ + Id: upkeepsIDs[i], + performGas, + trigger: encodeBlockTrigger({ + blockNum: latestBlock.number, + blockHash: latestBlock.hash, + }), + performData: '0x', + }) + } + return makeReport(upkeeps) +} + +const signReport = ( + reportContext: string[], + report: any, + signers: Wallet[], +) => { + const reportDigest = ethers.utils.keccak256(report) + const packedArgs = ethers.utils.solidityPack( + ['bytes32', 'bytes32[3]'], + [reportDigest, reportContext], + ) + const packedDigest = ethers.utils.keccak256(packedArgs) + + const signatures = [] + for (const signer of signers) { + signatures.push(signer._signingKey().signDigest(packedDigest)) + } + const vs = signatures.map((i) => '0' + (i.v - 27).toString(16)).join('') + return { + vs: '0x' + vs.padEnd(64, '0'), + rs: signatures.map((i) => i.r), + ss: signatures.map((i) => i.s), + } +} + +const parseUpkeepPerformedLogs = (receipt: ContractReceipt) => { + const parsedLogs = [] + for (const rawLog of receipt.logs) { + try { + const log = registry.interface.parseLog(rawLog) + if ( + log.name == + registry.interface.events[ + 'UpkeepPerformed(uint256,bool,uint96,uint256,uint256,bytes)' + ].name + ) { + parsedLogs.push(log as unknown as UpkeepPerformedEvent) + } + } catch { + continue + } + } + return parsedLogs +} + +const parseReorgedUpkeepReportLogs = (receipt: ContractReceipt) => { + const parsedLogs = [] + for (const rawLog of receipt.logs) { + try { + const log = registry.interface.parseLog(rawLog) + if ( + log.name == + registry.interface.events['ReorgedUpkeepReport(uint256,bytes)'].name + ) { + parsedLogs.push(log as unknown as ReorgedUpkeepReportEvent) + } + } catch { + continue + } + } + return parsedLogs +} + +const parseStaleUpkeepReportLogs = (receipt: ContractReceipt) => { + const parsedLogs = [] + for (const rawLog of receipt.logs) { + try { + const log = registry.interface.parseLog(rawLog) + if ( + log.name == + registry.interface.events['StaleUpkeepReport(uint256,bytes)'].name + ) { + parsedLogs.push(log as unknown as StaleUpkeepReportEvent) + } + } catch { + continue + } + } + return parsedLogs +} + +const parseCancelledUpkeepReportLogs = (receipt: ContractReceipt) => { + const parsedLogs = [] + for (const rawLog of receipt.logs) { + try { + const log = registry.interface.parseLog(rawLog) + if ( + log.name == + registry.interface.events['CancelledUpkeepReport(uint256,bytes)'].name + ) { + parsedLogs.push(log as unknown as CancelledUpkeepReportEvent) + } + } catch { + continue + } + } + return parsedLogs +} + +describe('ZKSyncAutomationRegistry2_3', () => { + let owner: Signer + let keeper1: Signer + let keeper2: Signer + let keeper3: Signer + let keeper4: Signer + let keeper5: Signer + let nonkeeper: Signer + let signer1: Wallet + let signer2: Wallet + let signer3: Wallet + let signer4: Wallet + let signer5: Wallet + let admin: Signer + let payee1: Signer + let payee2: Signer + let payee3: Signer + let payee4: Signer + let payee5: Signer + let financeAdmin: Signer + + let upkeepId: BigNumber // conditional upkeep + let afUpkeepId: BigNumber // auto funding upkeep + let logUpkeepId: BigNumber // log trigger upkeepID + let streamsLookupUpkeepId: BigNumber // streams lookup upkeep + // const numUpkeeps = 4 // see above + let keeperAddresses: string[] + let payees: string[] + let signers: Wallet[] + let signerAddresses: string[] + let config: OnChainConfig + let baseConfig: Parameters + let upkeepManager: string + + before(async () => { + personas = (await getUsers()).personas + + const compatibleUtilsFactory = await ethers.getContractFactory( + 'AutomationCompatibleUtils', + ) + automationUtils = await compatibleUtilsFactory.deploy() + + const utilsFactory = await ethers.getContractFactory('AutomationUtils2_3') + automationUtils2_3 = await utilsFactory.deploy() + + linkTokenFactory = await ethers.getContractFactory( + 'src/v0.8/shared/test/helpers/LinkTokenTestHelper.sol:LinkTokenTestHelper', + ) + // need full path because there are two contracts with name MockV3Aggregator + mockV3AggregatorFactory = (await ethers.getContractFactory( + 'src/v0.8/tests/MockV3Aggregator.sol:MockV3Aggregator', + )) as unknown as MockV3AggregatorFactory + mockZKSyncSystemContextFactory = await ethers.getContractFactory( + 'MockZKSyncSystemContext', + ) + mockGasBoundCallerFactory = + await ethers.getContractFactory('MockGasBoundCaller') + upkeepMockFactory = await ethers.getContractFactory('UpkeepMock') + upkeepAutoFunderFactory = + await ethers.getContractFactory('UpkeepAutoFunder') + moduleBaseFactory = await ethers.getContractFactory('ChainModuleBase') + streamsLookupUpkeepFactory = await ethers.getContractFactory( + 'StreamsLookupUpkeep', + ) + + owner = personas.Default + keeper1 = personas.Carol + keeper2 = personas.Eddy + keeper3 = personas.Nancy + keeper4 = personas.Norbert + keeper5 = personas.Nick + nonkeeper = personas.Ned + admin = personas.Neil + payee1 = personas.Nelly + payee2 = personas.Norbert + payee3 = personas.Nick + payee4 = personas.Eddy + payee5 = personas.Carol + upkeepManager = await personas.Norbert.getAddress() + financeAdmin = personas.Nick + // signers + signer1 = new ethers.Wallet( + '0x7777777000000000000000000000000000000000000000000000000000000001', + ) + signer2 = new ethers.Wallet( + '0x7777777000000000000000000000000000000000000000000000000000000002', + ) + signer3 = new ethers.Wallet( + '0x7777777000000000000000000000000000000000000000000000000000000003', + ) + signer4 = new ethers.Wallet( + '0x7777777000000000000000000000000000000000000000000000000000000004', + ) + signer5 = new ethers.Wallet( + '0x7777777000000000000000000000000000000000000000000000000000000005', + ) + + keeperAddresses = [ + await keeper1.getAddress(), + await keeper2.getAddress(), + await keeper3.getAddress(), + await keeper4.getAddress(), + await keeper5.getAddress(), + ] + payees = [ + await payee1.getAddress(), + await payee2.getAddress(), + await payee3.getAddress(), + await payee4.getAddress(), + await payee5.getAddress(), + ] + signers = [signer1, signer2, signer3, signer4, signer5] + + // We append 26 random addresses to keepers, payees and signers to get a system of 31 oracles + // This allows f value of 1 - 10 + for (let i = 0; i < 26; i++) { + keeperAddresses.push(randomAddress()) + payees.push(randomAddress()) + signers.push(ethers.Wallet.createRandom()) + } + signerAddresses = [] + for (const signer of signers) { + signerAddresses.push(await signer.getAddress()) + } + + logTriggerConfig = + '0x' + + automationUtils.interface + .encodeFunctionData('_logTriggerConfig', [ + { + contractAddress: randomAddress(), + filterSelector: 0, + topic0: ethers.utils.randomBytes(32), + topic1: ethers.utils.randomBytes(32), + topic2: ethers.utils.randomBytes(32), + topic3: ethers.utils.randomBytes(32), + }, + ]) + .slice(10) + }) + + // This function is similar to registry's _calculatePaymentAmount + // It uses global fastGasWei, linkEth, and assumes isExecution = false (gasFee = fastGasWei*multiplier) + // rest of the parameters are the same + const linkForGas = ( + upkeepGasSpent: BigNumber, + gasOverhead: BigNumber, + gasMultiplier: BigNumber, + premiumPPB: BigNumber, + flatFee: BigNumber, // in millicents + ) => { + const gasSpent = gasOverhead.add(BigNumber.from(upkeepGasSpent)) + const gasPayment = gasWei + .mul(gasMultiplier) + .mul(gasSpent) + .mul(nativeUSD) + .div(linkUSD) + + const premium = gasWei + .mul(gasMultiplier) + .mul(upkeepGasSpent) + .mul(premiumPPB) + .mul(nativeUSD) + .div(paymentPremiumBase) + .add(flatFee.mul(BigNumber.from(10).pow(21))) + .div(linkUSD) + + return { + total: gasPayment.add(premium), + gasPayment, + premium, + } + } + + const verifyMaxPayment = async ( + registry: IAutomationRegistry, + chainModule: IChainModule, + ) => { + type TestCase = { + name: string + multiplier: number + gas: number + premium: number + flatFee: number + } + + const tests: TestCase[] = [ + { + name: 'no fees', + multiplier: 1, + gas: 100000, + premium: 0, + flatFee: 0, + }, + { + name: 'basic fees', + multiplier: 1, + gas: 100000, + premium: 250000000, + flatFee: 1000000, + }, + { + name: 'max fees', + multiplier: 3, + gas: 10000000, + premium: 250000000, + flatFee: 1000000, + }, + ] + + const fPlusOne = BigNumber.from(f + 1) + const chainModuleOverheads = await chainModule.getGasOverhead() + const totalConditionalOverhead = registryConditionalOverhead + .add(registryPerSignerGasOverhead.mul(fPlusOne)) + .add(chainModuleOverheads.chainModuleFixedOverhead) + + const totalLogOverhead = registryLogOverhead + .add(registryPerSignerGasOverhead.mul(fPlusOne)) + .add(chainModuleOverheads.chainModuleFixedOverhead) + + const financeAdminAddress = await financeAdmin.getAddress() + + for (const test of tests) { + await registry.connect(owner).setConfigTypeSafe( + signerAddresses, + keeperAddresses, + f, + { + checkGasLimit, + stalenessSeconds, + gasCeilingMultiplier: test.multiplier, + maxCheckDataSize, + maxPerformDataSize, + maxRevertDataSize, + maxPerformGas, + fallbackGasPrice, + fallbackLinkPrice, + fallbackNativePrice, + transcoder: transcoder.address, + registrars: [], + upkeepPrivilegeManager: upkeepManager, + chainModule: chainModule.address, + reorgProtectionEnabled: true, + financeAdmin: financeAdminAddress, + }, + offchainVersion, + offchainBytes, + [linkToken.address], + [ + { + gasFeePPB: test.premium, + flatFeeMilliCents: test.flatFee, + priceFeed: linkUSDFeed.address, + fallbackPrice: fallbackLinkPrice, + minSpend: minUpkeepSpend, + decimals: 18, + }, + ], + ) + + const conditionalPrice = await registry.getMaxPaymentForGas( + upkeepId, + Trigger.CONDITION, + test.gas, + linkToken.address, + ) + expect(conditionalPrice).to.equal( + linkForGas( + BigNumber.from(test.gas), + totalConditionalOverhead, + BigNumber.from(test.multiplier), + BigNumber.from(test.premium), + BigNumber.from(test.flatFee), + ).total, + ) + + const logPrice = await registry.getMaxPaymentForGas( + upkeepId, + Trigger.LOG, + test.gas, + linkToken.address, + ) + expect(logPrice).to.equal( + linkForGas( + BigNumber.from(test.gas), + totalLogOverhead, + BigNumber.from(test.multiplier), + BigNumber.from(test.premium), + BigNumber.from(test.flatFee), + ).total, + ) + } + } + + const verifyConsistentAccounting = async ( + maxAllowedSpareChange: BigNumber, + ) => { + const expectedLinkBalance = await registry.getReserveAmount( + linkToken.address, + ) + const linkTokenBalance = await linkToken.balanceOf(registry.address) + const upkeepIdBalance = (await registry.getUpkeep(upkeepId)).balance + let totalKeeperBalance = BigNumber.from(0) + for (let i = 0; i < keeperAddresses.length; i++) { + totalKeeperBalance = totalKeeperBalance.add( + (await registry.getTransmitterInfo(keeperAddresses[i])).balance, + ) + } + + const linkAvailableForPayment = await registry.linkAvailableForPayment() + assert.isTrue(expectedLinkBalance.eq(linkTokenBalance)) + assert.isTrue( + upkeepIdBalance + .add(totalKeeperBalance) + .add(linkAvailableForPayment) + .lte(expectedLinkBalance), + ) + assert.isTrue( + expectedLinkBalance + .sub(upkeepIdBalance) + .sub(totalKeeperBalance) + .sub(linkAvailableForPayment) + .lte(maxAllowedSpareChange), + ) + } + + interface GetTransmitTXOptions { + numSigners?: number + startingSignerIndex?: number + gasLimit?: BigNumberish + gasPrice?: BigNumberish + performGas?: BigNumberish + performDatas?: string[] + checkBlockNum?: number + checkBlockHash?: string + logBlockHash?: BytesLike + txHash?: BytesLike + logIndex?: number + timestamp?: number + } + + const getTransmitTx = async ( + registry: IAutomationRegistry, + transmitter: Signer, + upkeepIds: BigNumber[], + overrides: GetTransmitTXOptions = {}, + ) => { + const latestBlock = await ethers.provider.getBlock('latest') + const configDigest = (await registry.getState()).state.latestConfigDigest + const config = { + numSigners: f + 1, + startingSignerIndex: 0, + performDatas: undefined, + performGas, + checkBlockNum: latestBlock.number, + checkBlockHash: latestBlock.hash, + logIndex: 0, + txHash: undefined, // assigned uniquely below + logBlockHash: undefined, // assigned uniquely below + timestamp: now(), + gasLimit: undefined, + gasPrice: undefined, + } + Object.assign(config, overrides) + const upkeeps: UpkeepData[] = [] + for (let i = 0; i < upkeepIds.length; i++) { + let trigger: string + switch (getTriggerType(upkeepIds[i])) { + case Trigger.CONDITION: + trigger = encodeBlockTrigger({ + blockNum: config.checkBlockNum, + blockHash: config.checkBlockHash, + }) + break + case Trigger.LOG: + trigger = encodeLogTrigger({ + logBlockHash: config.logBlockHash || ethers.utils.randomBytes(32), + txHash: config.txHash || ethers.utils.randomBytes(32), + logIndex: config.logIndex, + blockNum: config.checkBlockNum, + blockHash: config.checkBlockHash, + }) + break + } + upkeeps.push({ + Id: upkeepIds[i], + performGas: config.performGas, + trigger, + performData: config.performDatas ? config.performDatas[i] : '0x', + }) + } + + const report = makeReport(upkeeps) + const reportContext = [configDigest, epochAndRound5_1, emptyBytes32] + const sigs = signReport( + reportContext, + report, + signers.slice( + config.startingSignerIndex, + config.startingSignerIndex + config.numSigners, + ), + ) + + type txOverride = { + gasLimit?: BigNumberish | Promise + gasPrice?: BigNumberish | Promise + } + const txOverrides: txOverride = {} + if (config.gasLimit) { + txOverrides.gasLimit = config.gasLimit + } + if (config.gasPrice) { + txOverrides.gasPrice = config.gasPrice + } + + return registry + .connect(transmitter) + .transmit( + [configDigest, epochAndRound5_1, emptyBytes32], + report, + sigs.rs, + sigs.ss, + sigs.vs, + txOverrides, + ) + } + + const getTransmitTxWithReport = async ( + registry: IAutomationRegistry, + transmitter: Signer, + report: BytesLike, + ) => { + const configDigest = (await registry.getState()).state.latestConfigDigest + const reportContext = [configDigest, epochAndRound5_1, emptyBytes32] + const sigs = signReport(reportContext, report, signers.slice(0, f + 1)) + + return registry + .connect(transmitter) + .transmit( + [configDigest, epochAndRound5_1, emptyBytes32], + report, + sigs.rs, + sigs.ss, + sigs.vs, + ) + } + + const setup = async () => { + linkToken = await linkTokenFactory.connect(owner).deploy() + gasPriceFeed = await mockV3AggregatorFactory + .connect(owner) + .deploy(0, gasWei) + linkUSDFeed = await mockV3AggregatorFactory + .connect(owner) + .deploy(8, linkUSD) + nativeUSDFeed = await mockV3AggregatorFactory + .connect(owner) + .deploy(8, nativeUSD) + const upkeepTranscoderFactory = await ethers.getContractFactory( + 'UpkeepTranscoder5_0', + ) + transcoder = await upkeepTranscoderFactory.connect(owner).deploy() + mockZKSyncSystemContext = await mockZKSyncSystemContextFactory + .connect(owner) + .deploy() + mockGasBoundCaller = await mockGasBoundCallerFactory.connect(owner).deploy() + moduleBase = await moduleBaseFactory.connect(owner).deploy() + streamsLookupUpkeep = await streamsLookupUpkeepFactory + .connect(owner) + .deploy( + BigNumber.from('10000'), + BigNumber.from('100'), + false /* useArbBlock */, + true /* staging */, + false /* verify mercury response */, + ) + + const zksyncSystemContextCode = await ethers.provider.send('eth_getCode', [ + mockZKSyncSystemContext.address, + ]) + await ethers.provider.send('hardhat_setCode', [ + '0x000000000000000000000000000000000000800B', + zksyncSystemContextCode, + ]) + + const gasBoundCallerCode = await ethers.provider.send('eth_getCode', [ + mockGasBoundCaller.address, + ]) + await ethers.provider.send('hardhat_setCode', [ + '0xc706EC7dfA5D4Dc87f29f859094165E8290530f5', + gasBoundCallerCode, + ]) + + const financeAdminAddress = await financeAdmin.getAddress() + + config = { + checkGasLimit, + stalenessSeconds, + gasCeilingMultiplier, + maxCheckDataSize, + maxPerformDataSize, + maxRevertDataSize, + maxPerformGas, + fallbackGasPrice, + fallbackLinkPrice, + fallbackNativePrice, + transcoder: transcoder.address, + registrars: [], + upkeepPrivilegeManager: upkeepManager, + chainModule: moduleBase.address, + reorgProtectionEnabled: true, + financeAdmin: financeAdminAddress, + } + + baseConfig = [ + signerAddresses, + keeperAddresses, + f, + config, + offchainVersion, + offchainBytes, + [linkToken.address], + [ + { + gasFeePPB: paymentPremiumPPB, + flatFeeMilliCents, + priceFeed: linkUSDFeed.address, + fallbackPrice: fallbackLinkPrice, + minSpend: minUpkeepSpend, + decimals: 18, + }, + ], + ] + + const registryParams: Parameters = [ + owner, + linkToken.address, + linkUSDFeed.address, + nativeUSDFeed.address, + gasPriceFeed.address, + zeroAddress, + 0, // onchain payout mode + wrappedNativeTokenAddress, + ] + + registry = await deployZKSyncRegistry23(...registryParams) + mgRegistry = await deployZKSyncRegistry23(...registryParams) + + registryConditionalOverhead = await registry.getConditionalGasOverhead() + registryLogOverhead = await registry.getLogGasOverhead() + registryPerSignerGasOverhead = await registry.getPerSignerGasOverhead() + // registryPerPerformByteGasOverhead = + // await registry.getPerPerformByteGasOverhead() + // registryTransmitCalldataFixedBytesOverhead = + // await registry.getTransmitCalldataFixedBytesOverhead() + // registryTransmitCalldataPerSignerBytesOverhead = + // await registry.getTransmitCalldataPerSignerBytesOverhead() + cancellationDelay = (await registry.getCancellationDelay()).toNumber() + + await registry.connect(owner).setConfigTypeSafe(...baseConfig) + await mgRegistry.connect(owner).setConfigTypeSafe(...baseConfig) + for (const reg of [registry, mgRegistry]) { + await reg.connect(owner).setPayees(payees) + await linkToken.connect(admin).approve(reg.address, toWei('1000')) + await linkToken.connect(owner).approve(reg.address, toWei('1000')) + } + + mock = await upkeepMockFactory.deploy() + await linkToken + .connect(owner) + .transfer(await admin.getAddress(), toWei('1000')) + let tx = await registry + .connect(owner) + .registerUpkeep( + mock.address, + performGas, + await admin.getAddress(), + Trigger.CONDITION, + linkToken.address, + randomBytes, + '0x', + '0x', + ) + upkeepId = await getUpkeepID(tx) + + autoFunderUpkeep = await upkeepAutoFunderFactory + .connect(owner) + .deploy(linkToken.address, registry.address) + tx = await registry + .connect(owner) + .registerUpkeep( + autoFunderUpkeep.address, + performGas, + autoFunderUpkeep.address, + Trigger.CONDITION, + linkToken.address, + '0x', + '0x', + '0x', + ) + afUpkeepId = await getUpkeepID(tx) + + ltUpkeep = await deployMockContract(owner, ILogAutomationactory.abi) + tx = await registry + .connect(owner) + .registerUpkeep( + ltUpkeep.address, + performGas, + await admin.getAddress(), + Trigger.LOG, + linkToken.address, + '0x', + logTriggerConfig, + emptyBytes, + ) + logUpkeepId = await getUpkeepID(tx) + + await autoFunderUpkeep.setUpkeepId(afUpkeepId) + // Give enough funds for upkeep as well as to the upkeep contract + await linkToken + .connect(owner) + .transfer(autoFunderUpkeep.address, toWei('1000')) + + tx = await registry + .connect(owner) + .registerUpkeep( + streamsLookupUpkeep.address, + performGas, + await admin.getAddress(), + Trigger.CONDITION, + linkToken.address, + '0x', + '0x', + '0x', + ) + streamsLookupUpkeepId = await getUpkeepID(tx) + } + + const getMultipleUpkeepsDeployedAndFunded = async ( + numPassingConditionalUpkeeps: number, + numPassingLogUpkeeps: number, + numFailingUpkeeps: number, + ) => { + const passingConditionalUpkeepIds = [] + const passingLogUpkeepIds = [] + const failingUpkeepIds = [] + for (let i = 0; i < numPassingConditionalUpkeeps; i++) { + const mock = await upkeepMockFactory.deploy() + await mock.setCanPerform(true) + await mock.setPerformGasToBurn(BigNumber.from('0')) + const tx = await registry + .connect(owner) + .registerUpkeep( + mock.address, + performGas, + await admin.getAddress(), + Trigger.CONDITION, + linkToken.address, + '0x', + '0x', + '0x', + ) + const condUpkeepId = await getUpkeepID(tx) + passingConditionalUpkeepIds.push(condUpkeepId) + + // Add funds to passing upkeeps + await registry.connect(admin).addFunds(condUpkeepId, toWei('100')) + } + for (let i = 0; i < numPassingLogUpkeeps; i++) { + const mock = await upkeepMockFactory.deploy() + await mock.setCanPerform(true) + await mock.setPerformGasToBurn(BigNumber.from('0')) + const tx = await registry + .connect(owner) + .registerUpkeep( + mock.address, + performGas, + await admin.getAddress(), + Trigger.LOG, + linkToken.address, + '0x', + logTriggerConfig, + emptyBytes, + ) + const logUpkeepId = await getUpkeepID(tx) + passingLogUpkeepIds.push(logUpkeepId) + + // Add funds to passing upkeeps + await registry.connect(admin).addFunds(logUpkeepId, toWei('100')) + } + for (let i = 0; i < numFailingUpkeeps; i++) { + const mock = await upkeepMockFactory.deploy() + await mock.setCanPerform(true) + await mock.setPerformGasToBurn(BigNumber.from('0')) + const tx = await registry + .connect(owner) + .registerUpkeep( + mock.address, + performGas, + await admin.getAddress(), + Trigger.CONDITION, + linkToken.address, + '0x', + '0x', + '0x', + ) + const failingUpkeepId = await getUpkeepID(tx) + failingUpkeepIds.push(failingUpkeepId) + } + return { + passingConditionalUpkeepIds, + passingLogUpkeepIds, + failingUpkeepIds, + } + } + + beforeEach(async () => { + await loadFixture(setup) + }) + + describe('#transmit', () => { + const fArray = [1, 5, 10] + + it('reverts when registry is paused', async () => { + await registry.connect(owner).pause() + await evmRevertCustomError( + getTransmitTx(registry, keeper1, [upkeepId]), + registry, + 'RegistryPaused', + ) + }) + + it('reverts when called by non active transmitter', async () => { + await evmRevertCustomError( + getTransmitTx(registry, payee1, [upkeepId]), + registry, + 'OnlyActiveTransmitters', + ) + }) + + it('reverts when report data lengths mismatches', async () => { + const upkeepIds = [] + const gasLimits: BigNumber[] = [] + const triggers: string[] = [] + const performDatas = [] + + upkeepIds.push(upkeepId) + gasLimits.push(performGas) + triggers.push('0x') + performDatas.push('0x') + // Push an extra perform data + performDatas.push('0x') + + const report = encodeReport({ + fastGasWei: 0, + linkUSD: 0, + upkeepIds, + gasLimits, + triggers, + performDatas, + }) + + await evmRevertCustomError( + getTransmitTxWithReport(registry, keeper1, report), + registry, + 'InvalidReport', + ) + }) + + it('returns early when invalid upkeepIds are included in report', async () => { + const tx = await getTransmitTx(registry, keeper1, [ + upkeepId.add(BigNumber.from('1')), + ]) + + const receipt = await tx.wait() + const cancelledUpkeepReportLogs = parseCancelledUpkeepReportLogs(receipt) + // exactly 1 CancelledUpkeepReport log should be emitted + assert.equal(cancelledUpkeepReportLogs.length, 1) + }) + + it('performs even when the upkeep has insufficient funds and the upkeep pays out all the remaining balance', async () => { + // add very little fund to this upkeep + await registry.connect(admin).addFunds(upkeepId, BigNumber.from(10)) + const tx = await getTransmitTx(registry, keeper1, [upkeepId]) + const receipt = await tx.wait() + // the upkeep is underfunded in transmit but still performed + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + assert.equal(upkeepPerformedLogs.length, 1) + const balance = (await registry.getUpkeep(upkeepId)).balance + assert.equal(balance.toNumber(), 0) + }) + + context('When the upkeep is funded', async () => { + beforeEach(async () => { + // Fund the upkeep + await Promise.all([ + registry.connect(admin).addFunds(upkeepId, toWei('100')), + registry.connect(admin).addFunds(logUpkeepId, toWei('100')), + ]) + }) + + it('handles duplicate upkeepIDs', async () => { + const tests: [string, BigNumber, number, number][] = [ + // [name, upkeep, num stale, num performed] + ['conditional', upkeepId, 1, 1], // checkBlocks must be sequential + ['log-trigger', logUpkeepId, 0, 2], // logs are deduped based on the "trigger ID" + ] + for (const [type, id, nStale, nPerformed] of tests) { + const tx = await getTransmitTx(registry, keeper1, [id, id]) + const receipt = await tx.wait() + const staleUpkeepReport = parseStaleUpkeepReportLogs(receipt) + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + assert.equal( + staleUpkeepReport.length, + nStale, + `wrong log count for ${type} upkeep`, + ) + assert.equal( + upkeepPerformedLogs.length, + nPerformed, + `wrong log count for ${type} upkeep`, + ) + } + }) + + it('handles duplicate log triggers', async () => { + const logBlockHash = ethers.utils.randomBytes(32) + const txHash = ethers.utils.randomBytes(32) + const logIndex = 0 + const expectedDedupKey = ethers.utils.solidityKeccak256( + ['uint256', 'bytes32', 'bytes32', 'uint32'], + [logUpkeepId, logBlockHash, txHash, logIndex], + ) + assert.isFalse(await registry.hasDedupKey(expectedDedupKey)) + const tx = await getTransmitTx( + registry, + keeper1, + [logUpkeepId, logUpkeepId], + { logBlockHash, txHash, logIndex }, // will result in the same dedup key + ) + const receipt = await tx.wait() + const staleUpkeepReport = parseStaleUpkeepReportLogs(receipt) + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + assert.equal(staleUpkeepReport.length, 1) + assert.equal(upkeepPerformedLogs.length, 1) + assert.isTrue(await registry.hasDedupKey(expectedDedupKey)) + await expect(tx) + .to.emit(registry, 'DedupKeyAdded') + .withArgs(expectedDedupKey) + }) + + it('returns early when check block number is less than last perform (block)', async () => { + // First perform an upkeep to put last perform block number on upkeep state + const tx = await getTransmitTx(registry, keeper1, [upkeepId]) + await tx.wait() + const lastPerformed = (await registry.getUpkeep(upkeepId)) + .lastPerformedBlockNumber + const lastPerformBlock = await ethers.provider.getBlock(lastPerformed) + assert.equal(lastPerformed.toString(), tx.blockNumber?.toString()) + // Try to transmit a report which has checkBlockNumber = lastPerformed-1, should result in stale report + const transmitTx = await getTransmitTx(registry, keeper1, [upkeepId], { + checkBlockNum: lastPerformBlock.number - 1, + checkBlockHash: lastPerformBlock.parentHash, + }) + const receipt = await transmitTx.wait() + const staleUpkeepReportLogs = parseStaleUpkeepReportLogs(receipt) + // exactly 1 StaleUpkeepReportLogs log should be emitted + assert.equal(staleUpkeepReportLogs.length, 1) + }) + + it('handles case when check block hash does not match', async () => { + const tests: [string, BigNumber][] = [ + ['conditional', upkeepId], + ['log-trigger', logUpkeepId], + ] + for (const [type, id] of tests) { + const latestBlock = await ethers.provider.getBlock('latest') + // Try to transmit a report which has incorrect checkBlockHash + const tx = await getTransmitTx(registry, keeper1, [id], { + checkBlockNum: latestBlock.number - 1, + checkBlockHash: latestBlock.hash, // should be latestBlock.parentHash + }) + + const receipt = await tx.wait() + const reorgedUpkeepReportLogs = parseReorgedUpkeepReportLogs(receipt) + // exactly 1 ReorgedUpkeepReportLogs log should be emitted + assert.equal( + reorgedUpkeepReportLogs.length, + 1, + `wrong log count for ${type} upkeep`, + ) + } + }) + + it('handles case when check block number is older than 256 blocks', async () => { + for (let i = 0; i < 256; i++) { + await ethers.provider.send('evm_mine', []) + } + const tests: [string, BigNumber][] = [ + ['conditional', upkeepId], + ['log-trigger', logUpkeepId], + ] + for (const [type, id] of tests) { + const latestBlock = await ethers.provider.getBlock('latest') + const old = await ethers.provider.getBlock(latestBlock.number - 256) + // Try to transmit a report which has incorrect checkBlockHash + const tx = await getTransmitTx(registry, keeper1, [id], { + checkBlockNum: old.number, + checkBlockHash: old.hash, + }) + + const receipt = await tx.wait() + const reorgedUpkeepReportLogs = parseReorgedUpkeepReportLogs(receipt) + // exactly 1 ReorgedUpkeepReportLogs log should be emitted + assert.equal( + reorgedUpkeepReportLogs.length, + 1, + `wrong log count for ${type} upkeep`, + ) + } + }) + + it('allows bypassing reorg protection with empty blockhash', async () => { + const tests: [string, BigNumber][] = [ + ['conditional', upkeepId], + ['log-trigger', logUpkeepId], + ] + for (const [type, id] of tests) { + const latestBlock = await ethers.provider.getBlock('latest') + const tx = await getTransmitTx(registry, keeper1, [id], { + checkBlockNum: latestBlock.number, + checkBlockHash: emptyBytes32, + }) + const receipt = await tx.wait() + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + assert.equal( + upkeepPerformedLogs.length, + 1, + `wrong log count for ${type} upkeep`, + ) + } + }) + + it('allows bypassing reorg protection with reorgProtectionEnabled false config', async () => { + const tests: [string, BigNumber][] = [ + ['conditional', upkeepId], + ['log-trigger', logUpkeepId], + ] + const newConfig = config + newConfig.reorgProtectionEnabled = false + await registry // used to test initial configurations + .connect(owner) + .setConfigTypeSafe( + signerAddresses, + keeperAddresses, + f, + newConfig, + offchainVersion, + offchainBytes, + baseConfig[6], + baseConfig[7], + ) + + for (const [type, id] of tests) { + const latestBlock = await ethers.provider.getBlock('latest') + // Try to transmit a report which has incorrect checkBlockHash + const tx = await getTransmitTx(registry, keeper1, [id], { + checkBlockNum: latestBlock.number - 1, + checkBlockHash: latestBlock.hash, // should be latestBlock.parentHash + }) + + const receipt = await tx.wait() + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + assert.equal( + upkeepPerformedLogs.length, + 1, + `wrong log count for ${type} upkeep`, + ) + } + }) + + it('allows very old trigger block numbers when bypassing reorg protection with reorgProtectionEnabled config', async () => { + const newConfig = config + newConfig.reorgProtectionEnabled = false + await registry // used to test initial configurations + .connect(owner) + .setConfigTypeSafe( + signerAddresses, + keeperAddresses, + f, + newConfig, + offchainVersion, + offchainBytes, + baseConfig[6], + baseConfig[7], + ) + for (let i = 0; i < 256; i++) { + await ethers.provider.send('evm_mine', []) + } + const tests: [string, BigNumber][] = [ + ['conditional', upkeepId], + ['log-trigger', logUpkeepId], + ] + for (const [type, id] of tests) { + const latestBlock = await ethers.provider.getBlock('latest') + const old = await ethers.provider.getBlock(latestBlock.number - 256) + // Try to transmit a report which has incorrect checkBlockHash + const tx = await getTransmitTx(registry, keeper1, [id], { + checkBlockNum: old.number, + checkBlockHash: old.hash, + }) + + const receipt = await tx.wait() + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + assert.equal( + upkeepPerformedLogs.length, + 1, + `wrong log count for ${type} upkeep`, + ) + } + }) + + it('allows very old trigger block numbers when bypassing reorg protection with empty blockhash', async () => { + // mine enough blocks so that blockhash(1) is unavailable + for (let i = 0; i <= 256; i++) { + await ethers.provider.send('evm_mine', []) + } + const tests: [string, BigNumber][] = [ + ['conditional', upkeepId], + ['log-trigger', logUpkeepId], + ] + for (const [type, id] of tests) { + const tx = await getTransmitTx(registry, keeper1, [id], { + checkBlockNum: 1, + checkBlockHash: emptyBytes32, + }) + const receipt = await tx.wait() + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + assert.equal( + upkeepPerformedLogs.length, + 1, + `wrong log count for ${type} upkeep`, + ) + } + }) + + it('returns early when future block number is provided as trigger, irrespective of blockhash being present', async () => { + const tests: [string, BigNumber][] = [ + ['conditional', upkeepId], + ['log-trigger', logUpkeepId], + ] + for (const [type, id] of tests) { + const latestBlock = await ethers.provider.getBlock('latest') + + // Should fail when blockhash is empty + let tx = await getTransmitTx(registry, keeper1, [id], { + checkBlockNum: latestBlock.number + 100, + checkBlockHash: emptyBytes32, + }) + let receipt = await tx.wait() + let reorgedUpkeepReportLogs = parseReorgedUpkeepReportLogs(receipt) + // exactly 1 ReorgedUpkeepReportLogs log should be emitted + assert.equal( + reorgedUpkeepReportLogs.length, + 1, + `wrong log count for ${type} upkeep`, + ) + + // Should also fail when blockhash is not empty + tx = await getTransmitTx(registry, keeper1, [id], { + checkBlockNum: latestBlock.number + 100, + checkBlockHash: latestBlock.hash, + }) + receipt = await tx.wait() + reorgedUpkeepReportLogs = parseReorgedUpkeepReportLogs(receipt) + // exactly 1 ReorgedUpkeepReportLogs log should be emitted + assert.equal( + reorgedUpkeepReportLogs.length, + 1, + `wrong log count for ${type} upkeep`, + ) + } + }) + + it('returns early when future block number is provided as trigger, irrespective of reorgProtectionEnabled config', async () => { + const newConfig = config + newConfig.reorgProtectionEnabled = false + await registry // used to test initial configurations + .connect(owner) + .setConfigTypeSafe( + signerAddresses, + keeperAddresses, + f, + newConfig, + offchainVersion, + offchainBytes, + baseConfig[6], + baseConfig[7], + ) + const tests: [string, BigNumber][] = [ + ['conditional', upkeepId], + ['log-trigger', logUpkeepId], + ] + for (const [type, id] of tests) { + const latestBlock = await ethers.provider.getBlock('latest') + + // Should fail when blockhash is empty + let tx = await getTransmitTx(registry, keeper1, [id], { + checkBlockNum: latestBlock.number + 100, + checkBlockHash: emptyBytes32, + }) + let receipt = await tx.wait() + let reorgedUpkeepReportLogs = parseReorgedUpkeepReportLogs(receipt) + // exactly 1 ReorgedUpkeepReportLogs log should be emitted + assert.equal( + reorgedUpkeepReportLogs.length, + 1, + `wrong log count for ${type} upkeep`, + ) + + // Should also fail when blockhash is not empty + tx = await getTransmitTx(registry, keeper1, [id], { + checkBlockNum: latestBlock.number + 100, + checkBlockHash: latestBlock.hash, + }) + receipt = await tx.wait() + reorgedUpkeepReportLogs = parseReorgedUpkeepReportLogs(receipt) + // exactly 1 ReorgedUpkeepReportLogs log should be emitted + assert.equal( + reorgedUpkeepReportLogs.length, + 1, + `wrong log count for ${type} upkeep`, + ) + } + }) + + it('returns early when upkeep is cancelled and cancellation delay has gone', async () => { + const latestBlockReport = await makeLatestBlockReport([upkeepId]) + await registry.connect(admin).cancelUpkeep(upkeepId) + + for (let i = 0; i < cancellationDelay; i++) { + await ethers.provider.send('evm_mine', []) + } + + const tx = await getTransmitTxWithReport( + registry, + keeper1, + latestBlockReport, + ) + + const receipt = await tx.wait() + const cancelledUpkeepReportLogs = + parseCancelledUpkeepReportLogs(receipt) + // exactly 1 CancelledUpkeepReport log should be emitted + assert.equal(cancelledUpkeepReportLogs.length, 1) + }) + + it('does not revert if the target cannot execute', async () => { + await mock.setCanPerform(false) + const tx = await getTransmitTx(registry, keeper1, [upkeepId]) + + const receipt = await tx.wait() + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + // exactly 1 Upkeep Performed should be emitted + assert.equal(upkeepPerformedLogs.length, 1) + const upkeepPerformedLog = upkeepPerformedLogs[0] + + const success = upkeepPerformedLog.args.success + assert.equal(success, false) + }) + + it('does not revert if the target runs out of gas', async () => { + await mock.setCanPerform(false) + + const tx = await getTransmitTx(registry, keeper1, [upkeepId], { + performGas: 10, // too little gas + }) + + const receipt = await tx.wait() + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + // exactly 1 Upkeep Performed should be emitted + assert.equal(upkeepPerformedLogs.length, 1) + const upkeepPerformedLog = upkeepPerformedLogs[0] + + const success = upkeepPerformedLog.args.success + assert.equal(success, false) + }) + + it('reverts if not enough gas supplied', async () => { + await mock.setCanPerform(true) + await evmRevert( + getTransmitTx(registry, keeper1, [upkeepId], { + gasLimit: BigNumber.from(150000), + }), + ) + }) + + it('executes the data passed to the registry', async () => { + await mock.setCanPerform(true) + + const tx = await getTransmitTx(registry, keeper1, [upkeepId], { + performDatas: [randomBytes], + }) + const receipt = await tx.wait() + + const upkeepPerformedWithABI = [ + 'event UpkeepPerformedWith(bytes upkeepData)', + ] + const iface = new ethers.utils.Interface(upkeepPerformedWithABI) + const parsedLogs = [] + for (let i = 0; i < receipt.logs.length; i++) { + const log = receipt.logs[i] + try { + parsedLogs.push(iface.parseLog(log)) + } catch (e) { + // ignore log + } + } + assert.equal(parsedLogs.length, 1) + assert.equal(parsedLogs[0].args.upkeepData, randomBytes) + }) + + it('uses actual execution price for payment and premium calculation', async () => { + // Actual multiplier is 2, but we set gasPrice to be == gasWei + const gasPrice = gasWei + await mock.setCanPerform(true) + const registryPremiumBefore = (await registry.getState()).state + .totalPremium + const tx = await getTransmitTx(registry, keeper1, [upkeepId], { + gasPrice, + }) + const receipt = await tx.wait() + const registryPremiumAfter = (await registry.getState()).state + .totalPremium + const premium = registryPremiumAfter.sub(registryPremiumBefore) + + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + // exactly 1 Upkeep Performed should be emitted + assert.equal(upkeepPerformedLogs.length, 1) + const upkeepPerformedLog = upkeepPerformedLogs[0] + + const gasUsed = upkeepPerformedLog.args.gasUsed // 14657 gasUsed + const gasOverhead = upkeepPerformedLog.args.gasOverhead // 137230 gasOverhead + const totalPayment = upkeepPerformedLog.args.totalPayment + + assert.equal( + linkForGas( + gasUsed, + gasOverhead, + BigNumber.from('1'), // Not the config multiplier, but the actual gas used + paymentPremiumPPB, + flatFeeMilliCents, + // pubdataGas.mul(gasPrice), + ).total.toString(), + totalPayment.toString(), + ) + + assert.equal( + linkForGas( + gasUsed, + gasOverhead, + BigNumber.from('1'), // Not the config multiplier, but the actual gas used + paymentPremiumPPB, + flatFeeMilliCents, + // pubdataGas.mul(gasPrice), + ).premium.toString(), + premium.toString(), + ) + }) + + it('only pays at a rate up to the gas ceiling [ @skip-coverage ]', async () => { + // Actual multiplier is 2, but we set gasPrice to be 10x + const gasPrice = gasWei.mul(BigNumber.from('10')) + await mock.setCanPerform(true) + + const tx = await getTransmitTx(registry, keeper1, [upkeepId], { + gasPrice, + }) + const receipt = await tx.wait() + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + // exactly 1 Upkeep Performed should be emitted + assert.equal(upkeepPerformedLogs.length, 1) + const upkeepPerformedLog = upkeepPerformedLogs[0] + + const gasUsed = upkeepPerformedLog.args.gasUsed + const gasOverhead = upkeepPerformedLog.args.gasOverhead + const totalPayment = upkeepPerformedLog.args.totalPayment + + assert.equal( + linkForGas( + gasUsed, + gasOverhead, + gasCeilingMultiplier, // Should be same with exisitng multiplier + paymentPremiumPPB, + flatFeeMilliCents, + // pubdataGas.mul(gasPrice), + ).total.toString(), + totalPayment.toString(), + ) + }) + + itMaybe('can self fund', async () => { + const maxPayment = await registry.getMaxPaymentForGas( + upkeepId, + Trigger.CONDITION, + performGas, + linkToken.address, + ) + + // First set auto funding amount to 0 and verify that balance is deducted upon performUpkeep + let initialBalance = toWei('100') + await registry.connect(owner).addFunds(afUpkeepId, initialBalance) + await autoFunderUpkeep.setAutoFundLink(0) + await autoFunderUpkeep.setIsEligible(true) + await getTransmitTx(registry, keeper1, [afUpkeepId]) + + let postUpkeepBalance = (await registry.getUpkeep(afUpkeepId)).balance + assert.isTrue(postUpkeepBalance.lt(initialBalance)) // Balance should be deducted + assert.isTrue(postUpkeepBalance.gte(initialBalance.sub(maxPayment))) // Balance should not be deducted more than maxPayment + + // Now set auto funding amount to 100 wei and verify that the balance increases + initialBalance = postUpkeepBalance + const autoTopupAmount = toWei('100') + await autoFunderUpkeep.setAutoFundLink(autoTopupAmount) + await autoFunderUpkeep.setIsEligible(true) + await getTransmitTx(registry, keeper1, [afUpkeepId]) + + postUpkeepBalance = (await registry.getUpkeep(afUpkeepId)).balance + // Balance should increase by autoTopupAmount and decrease by max maxPayment + assert.isTrue( + postUpkeepBalance.gte( + initialBalance.add(autoTopupAmount).sub(maxPayment), + ), + ) + }) + + it('can self cancel', async () => { + await registry.connect(owner).addFunds(afUpkeepId, toWei('100')) + + await autoFunderUpkeep.setIsEligible(true) + await autoFunderUpkeep.setShouldCancel(true) + + let registration = await registry.getUpkeep(afUpkeepId) + const oldExpiration = registration.maxValidBlocknumber + + // Do the thing + await getTransmitTx(registry, keeper1, [afUpkeepId]) + + // Verify upkeep gets cancelled + registration = await registry.getUpkeep(afUpkeepId) + const newExpiration = registration.maxValidBlocknumber + assert.isTrue(newExpiration.lt(oldExpiration)) + }) + + it('reverts when configDigest mismatches', async () => { + const report = await makeLatestBlockReport([upkeepId]) + const reportContext = [emptyBytes32, epochAndRound5_1, emptyBytes32] // wrong config digest + const sigs = signReport(reportContext, report, signers.slice(0, f + 1)) + await evmRevertCustomError( + registry + .connect(keeper1) + .transmit( + [reportContext[0], reportContext[1], reportContext[2]], + report, + sigs.rs, + sigs.ss, + sigs.vs, + ), + registry, + 'ConfigDigestMismatch', + ) + }) + + it('reverts with incorrect number of signatures', async () => { + const configDigest = (await registry.getState()).state + .latestConfigDigest + const report = await makeLatestBlockReport([upkeepId]) + const reportContext = [configDigest, epochAndRound5_1, emptyBytes32] // wrong config digest + const sigs = signReport(reportContext, report, signers.slice(0, f + 2)) + await evmRevertCustomError( + registry + .connect(keeper1) + .transmit( + [reportContext[0], reportContext[1], reportContext[2]], + report, + sigs.rs, + sigs.ss, + sigs.vs, + ), + registry, + 'IncorrectNumberOfSignatures', + ) + }) + + it('reverts with invalid signature for inactive signers', async () => { + const configDigest = (await registry.getState()).state + .latestConfigDigest + const report = await makeLatestBlockReport([upkeepId]) + const reportContext = [configDigest, epochAndRound5_1, emptyBytes32] // wrong config digest + const sigs = signReport(reportContext, report, [ + new ethers.Wallet(ethers.Wallet.createRandom()), + new ethers.Wallet(ethers.Wallet.createRandom()), + ]) + await evmRevertCustomError( + registry + .connect(keeper1) + .transmit( + [reportContext[0], reportContext[1], reportContext[2]], + report, + sigs.rs, + sigs.ss, + sigs.vs, + ), + registry, + 'OnlyActiveSigners', + ) + }) + + it('reverts with invalid signature for duplicated signers', async () => { + const configDigest = (await registry.getState()).state + .latestConfigDigest + const report = await makeLatestBlockReport([upkeepId]) + const reportContext = [configDigest, epochAndRound5_1, emptyBytes32] // wrong config digest + const sigs = signReport(reportContext, report, [signer1, signer1]) + await evmRevertCustomError( + registry + .connect(keeper1) + .transmit( + [reportContext[0], reportContext[1], reportContext[2]], + report, + sigs.rs, + sigs.ss, + sigs.vs, + ), + registry, + 'DuplicateSigners', + ) + }) + + itMaybe( + 'has a large enough gas overhead to cover upkeep that use all its gas [ @skip-coverage ]', + async () => { + await registry.connect(owner).setConfigTypeSafe( + signerAddresses, + keeperAddresses, + 10, // maximise f to maximise overhead + config, + offchainVersion, + offchainBytes, + baseConfig[6], + baseConfig[7], + ) + const tx = await registry.connect(owner).registerUpkeep( + mock.address, + maxPerformGas, // max allowed gas + await admin.getAddress(), + Trigger.CONDITION, + linkToken.address, + '0x', + '0x', + '0x', + ) + const testUpkeepId = await getUpkeepID(tx) + await registry.connect(admin).addFunds(testUpkeepId, toWei('100')) + + let performData = '0x' + for (let i = 0; i < maxPerformDataSize.toNumber(); i++) { + performData += '11' + } // max allowed performData + + await mock.setCanPerform(true) + await mock.setPerformGasToBurn(maxPerformGas) + + await getTransmitTx(registry, keeper1, [testUpkeepId], { + gasLimit: maxPerformGas.add(transmitGasOverhead), + numSigners: 11, + performDatas: [performData], + }) // Should not revert + }, + ) + + itMaybe( + 'performs upkeep, deducts payment, updates lastPerformed and emits events', + async () => { + await mock.setCanPerform(true) + + for (const i in fArray) { + const newF = fArray[i] + await registry + .connect(owner) + .setConfigTypeSafe( + signerAddresses, + keeperAddresses, + newF, + config, + offchainVersion, + offchainBytes, + baseConfig[6], + baseConfig[7], + ) + const checkBlock = await ethers.provider.getBlock('latest') + + const keeperBefore = await registry.getTransmitterInfo( + await keeper1.getAddress(), + ) + const registrationBefore = await registry.getUpkeep(upkeepId) + const registryPremiumBefore = (await registry.getState()).state + .totalPremium + const keeperLinkBefore = await linkToken.balanceOf( + await keeper1.getAddress(), + ) + const registryLinkBefore = await linkToken.balanceOf( + registry.address, + ) + + // Do the thing + const tx = await getTransmitTx(registry, keeper1, [upkeepId], { + checkBlockNum: checkBlock.number, + checkBlockHash: checkBlock.hash, + numSigners: newF + 1, + }) + + const receipt = await tx.wait() + + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + // exactly 1 Upkeep Performed should be emitted + assert.equal(upkeepPerformedLogs.length, 1) + const upkeepPerformedLog = upkeepPerformedLogs[0] + + const id = upkeepPerformedLog.args.id + const success = upkeepPerformedLog.args.success + const trigger = upkeepPerformedLog.args.trigger + const gasUsed = upkeepPerformedLog.args.gasUsed + const gasOverhead = upkeepPerformedLog.args.gasOverhead + const totalPayment = upkeepPerformedLog.args.totalPayment + assert.equal(id.toString(), upkeepId.toString()) + assert.equal(success, true) + assert.equal( + trigger, + encodeBlockTrigger({ + blockNum: checkBlock.number, + blockHash: checkBlock.hash, + }), + ) + assert.isTrue(gasUsed.gt(BigNumber.from('0'))) + assert.isTrue(gasOverhead.gt(BigNumber.from('0'))) + assert.isTrue(totalPayment.gt(BigNumber.from('0'))) + + const keeperAfter = await registry.getTransmitterInfo( + await keeper1.getAddress(), + ) + const registrationAfter = await registry.getUpkeep(upkeepId) + const keeperLinkAfter = await linkToken.balanceOf( + await keeper1.getAddress(), + ) + const registryLinkAfter = await linkToken.balanceOf( + registry.address, + ) + const registryPremiumAfter = (await registry.getState()).state + .totalPremium + const premium = registryPremiumAfter.sub(registryPremiumBefore) + // Keeper payment is gasPayment + premium / num keepers + const keeperPayment = totalPayment + .sub(premium) + .add(premium.div(BigNumber.from(keeperAddresses.length))) + + assert.equal( + keeperAfter.balance.sub(keeperPayment).toString(), + keeperBefore.balance.toString(), + ) + assert.equal( + registrationBefore.balance.sub(totalPayment).toString(), + registrationAfter.balance.toString(), + ) + assert.isTrue(keeperLinkAfter.eq(keeperLinkBefore)) + assert.isTrue(registryLinkBefore.eq(registryLinkAfter)) + + // Amount spent should be updated correctly + assert.equal( + registrationAfter.amountSpent.sub(totalPayment).toString(), + registrationBefore.amountSpent.toString(), + ) + assert.isTrue( + registrationAfter.amountSpent + .sub(registrationBefore.amountSpent) + .eq(registrationBefore.balance.sub(registrationAfter.balance)), + ) + // Last perform block number should be updated + assert.equal( + registrationAfter.lastPerformedBlockNumber.toString(), + tx.blockNumber?.toString(), + ) + + // Latest epoch should be 5 + assert.equal((await registry.getState()).state.latestEpoch, 5) + } + }, + ) + + // describe.only('Gas benchmarking conditional upkeeps [ @skip-coverage ]', function () { + // const fs = [1] + // fs.forEach(function (newF) { + // it( + // 'When f=' + + // newF + + // ' calculates gas overhead appropriately within a margin for different scenarios', + // async () => { + // // Perform the upkeep once to remove non-zero storage slots and have predictable gas measurement + // let tx = await getTransmitTx(registry, keeper1, [upkeepId]) + // await tx.wait() + // + // await registry + // .connect(admin) + // .setUpkeepGasLimit(upkeepId, performGas.mul(3)) + // + // // Different test scenarios + // let longBytes = '0x' + // for (let i = 0; i < maxPerformDataSize.toNumber(); i++) { + // longBytes += '11' + // } + // const upkeepSuccessArray = [true, false] + // const performGasArray = [5000, performGas] + // const performDataArray = ['0x', longBytes] + // const chainModuleOverheads = await moduleBase.getGasOverhead() + // + // for (const i in upkeepSuccessArray) { + // for (const j in performGasArray) { + // for (const k in performDataArray) { + // const upkeepSuccess = upkeepSuccessArray[i] + // const performGas = performGasArray[j] + // const performData = performDataArray[k] + // + // await mock.setCanPerform(upkeepSuccess) + // await mock.setPerformGasToBurn(performGas) + // await registry + // .connect(owner) + // .setConfigTypeSafe( + // signerAddresses, + // keeperAddresses, + // newF, + // config, + // offchainVersion, + // offchainBytes, + // baseConfig[6], + // baseConfig[7], + // ) + // tx = await getTransmitTx(registry, keeper1, [upkeepId], { + // numSigners: newF + 1, + // performDatas: [performData], + // }) + // const receipt = await tx.wait() + // const upkeepPerformedLogs = + // parseUpkeepPerformedLogs(receipt) + // // exactly 1 Upkeep Performed should be emitted + // assert.equal(upkeepPerformedLogs.length, 1) + // const upkeepPerformedLog = upkeepPerformedLogs[0] + // + // const upkeepGasUsed = upkeepPerformedLog.args.gasUsed + // const chargedGasOverhead = + // upkeepPerformedLog.args.gasOverhead + // const actualGasOverhead = receipt.gasUsed + // .sub(upkeepGasUsed) + // .add(500000) // the amount of pubdataGas used returned by mock gas bound caller + // const estimatedGasOverhead = registryConditionalOverhead + // .add( + // registryPerSignerGasOverhead.mul( + // BigNumber.from(newF + 1), + // ), + // ) + // .add(chainModuleOverheads.chainModuleFixedOverhead) + // .add(65_400) + // + // assert.isTrue(upkeepGasUsed.gt(BigNumber.from('0'))) + // assert.isTrue(chargedGasOverhead.gt(BigNumber.from('0'))) + // assert.isTrue(actualGasOverhead.gt(BigNumber.from('0'))) + // + // console.log( + // 'Gas Benchmarking conditional upkeeps:', + // 'upkeepSuccess=', + // upkeepSuccess, + // 'performGas=', + // performGas.toString(), + // 'performData length=', + // performData.length / 2 - 1, + // 'sig verification ( f =', + // newF, + // '): estimated overhead: ', + // estimatedGasOverhead.toString(), // 179800 + // ' charged overhead: ', + // chargedGasOverhead.toString(), // 180560 + // ' actual overhead: ', + // actualGasOverhead.toString(), // 632949 + // ' calculation margin over gasUsed: ', + // chargedGasOverhead.sub(actualGasOverhead).toString(), // 18456 + // ' estimation margin over gasUsed: ', + // estimatedGasOverhead.sub(actualGasOverhead).toString(), // -27744 + // ' upkeepGasUsed: ', + // upkeepGasUsed, // 988620 + // ' receipt.gasUsed: ', + // receipt.gasUsed, // 1121569 + // ) + // + // // The actual gas overhead should be less than charged gas overhead, but not by a lot + // // The charged gas overhead is controlled by ACCOUNTING_FIXED_GAS_OVERHEAD and + // // ACCOUNTING_PER_UPKEEP_GAS_OVERHEAD, and their correct values should be set to + // // satisfy constraints in multiple places + // assert.isTrue( + // chargedGasOverhead.gt(actualGasOverhead), + // 'Gas overhead calculated is too low, increase account gas variables (ACCOUNTING_FIXED_GAS_OVERHEAD/ACCOUNTING_PER_UPKEEP_GAS_OVERHEAD) by at least ' + + // actualGasOverhead.sub(chargedGasOverhead).toString(), + // ) + // assert.isTrue( + // chargedGasOverhead // 180560 + // .sub(actualGasOverhead) // 132940 + // .lt(gasCalculationMargin), + // 'Gas overhead calculated is too high, decrease account gas variables (ACCOUNTING_FIXED_GAS_OVERHEAD/ACCOUNTING_PER_SIGNER_GAS_OVERHEAD) by at least ' + + // chargedGasOverhead + // .sub(actualGasOverhead) + // .sub(gasCalculationMargin) + // .toString(), + // ) + // + // // The estimated overhead during checkUpkeep should be close to the actual overhead in transaction + // // It should be greater than the actual overhead but not by a lot + // // The estimated overhead is controlled by variables + // // REGISTRY_CONDITIONAL_OVERHEAD, REGISTRY_LOG_OVERHEAD, REGISTRY_PER_SIGNER_GAS_OVERHEAD + // // REGISTRY_PER_PERFORM_BYTE_GAS_OVERHEAD + // assert.isTrue( + // estimatedGasOverhead.gt(actualGasOverhead), + // 'Gas overhead estimated in check upkeep is too low, increase estimation gas variables (REGISTRY_CONDITIONAL_OVERHEAD/REGISTRY_LOG_OVERHEAD/REGISTRY_PER_SIGNER_GAS_OVERHEAD/REGISTRY_PER_PERFORM_BYTE_GAS_OVERHEAD) by at least ' + + // estimatedGasOverhead.sub(chargedGasOverhead).toString(), + // ) + // assert.isTrue( + // estimatedGasOverhead + // .sub(actualGasOverhead) + // .lt(gasEstimationMargin), + // 'Gas overhead estimated is too high, decrease estimation gas variables (REGISTRY_CONDITIONAL_OVERHEAD/REGISTRY_LOG_OVERHEAD/REGISTRY_PER_SIGNER_GAS_OVERHEAD/REGISTRY_PER_PERFORM_BYTE_GAS_OVERHEAD) by at least ' + + // estimatedGasOverhead + // .sub(actualGasOverhead) + // .sub(gasEstimationMargin) + // .toString(), + // ) + // } + // } + // } + // }, + // ) + // }) + // }) + + // describe.only('Gas benchmarking log upkeeps [ @skip-coverage ]', function () { + // const fs = [1] + // fs.forEach(function (newF) { + // it( + // 'When f=' + + // newF + + // ' calculates gas overhead appropriately within a margin', + // async () => { + // // Perform the upkeep once to remove non-zero storage slots and have predictable gas measurement + // let tx = await getTransmitTx(registry, keeper1, [logUpkeepId]) + // await tx.wait() + // const performData = '0x' + // await mock.setCanPerform(true) + // await mock.setPerformGasToBurn(performGas) + // await registry.setConfigTypeSafe( + // signerAddresses, + // keeperAddresses, + // newF, + // config, + // offchainVersion, + // offchainBytes, + // baseConfig[6], + // baseConfig[7], + // ) + // tx = await getTransmitTx(registry, keeper1, [logUpkeepId], { + // numSigners: newF + 1, + // performDatas: [performData], + // }) + // const receipt = await tx.wait() + // const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + // // exactly 1 Upkeep Performed should be emitted + // assert.equal(upkeepPerformedLogs.length, 1) + // const upkeepPerformedLog = upkeepPerformedLogs[0] + // const chainModuleOverheads = await moduleBase.getGasOverhead() + // + // const upkeepGasUsed = upkeepPerformedLog.args.gasUsed + // const chargedGasOverhead = upkeepPerformedLog.args.gasOverhead + // const actualGasOverhead = receipt.gasUsed + // .sub(upkeepGasUsed) + // .add(500000) // the amount of pubdataGas used returned by mock gas bound caller + // const estimatedGasOverhead = registryLogOverhead + // .add(registryPerSignerGasOverhead.mul(BigNumber.from(newF + 1))) + // .add(chainModuleOverheads.chainModuleFixedOverhead) + // .add(65_400) + // + // assert.isTrue(upkeepGasUsed.gt(BigNumber.from('0'))) + // assert.isTrue(chargedGasOverhead.gt(BigNumber.from('0'))) + // assert.isTrue(actualGasOverhead.gt(BigNumber.from('0'))) + // + // console.log( + // 'Gas Benchmarking log upkeeps:', + // 'upkeepSuccess=', + // true, + // 'performGas=', + // performGas.toString(), + // 'performData length=', + // performData.length / 2 - 1, + // 'sig verification ( f =', + // newF, + // '): estimated overhead: ', + // estimatedGasOverhead.toString(), + // ' charged overhead: ', + // chargedGasOverhead.toString(), + // ' actual overhead: ', + // actualGasOverhead.toString(), + // ' calculation margin over gasUsed: ', + // chargedGasOverhead.sub(actualGasOverhead).toString(), + // ' estimation margin over gasUsed: ', + // estimatedGasOverhead.sub(actualGasOverhead).toString(), + // ' upkeepGasUsed: ', + // upkeepGasUsed, + // ' receipt.gasUsed: ', + // receipt.gasUsed, + // ) + // + // assert.isTrue( + // chargedGasOverhead.gt(actualGasOverhead), + // 'Gas overhead calculated is too low, increase account gas variables (ACCOUNTING_FIXED_GAS_OVERHEAD/ACCOUNTING_PER_UPKEEP_GAS_OVERHEAD) by at least ' + + // actualGasOverhead.sub(chargedGasOverhead).toString(), + // ) + // assert.isTrue( + // chargedGasOverhead + // .sub(actualGasOverhead) + // .lt(gasCalculationMargin), + // 'Gas overhead calculated is too high, decrease account gas variables (ACCOUNTING_FIXED_GAS_OVERHEAD/ACCOUNTING_PER_SIGNER_GAS_OVERHEAD) by at least ' + + // chargedGasOverhead + // .sub(actualGasOverhead) + // .sub(gasCalculationMargin) + // .toString(), + // ) + // + // assert.isTrue( + // estimatedGasOverhead.gt(actualGasOverhead), + // 'Gas overhead estimated in check upkeep is too low, increase estimation gas variables (REGISTRY_CONDITIONAL_OVERHEAD/REGISTRY_LOG_OVERHEAD/REGISTRY_PER_SIGNER_GAS_OVERHEAD/REGISTRY_PER_PERFORM_BYTE_GAS_OVERHEAD) by at least ' + + // estimatedGasOverhead.sub(chargedGasOverhead).toString(), + // ) + // assert.isTrue( + // estimatedGasOverhead + // .sub(actualGasOverhead) + // .lt(gasEstimationMargin), + // 'Gas overhead estimated is too high, decrease estimation gas variables (REGISTRY_CONDITIONAL_OVERHEAD/REGISTRY_LOG_OVERHEAD/REGISTRY_PER_SIGNER_GAS_OVERHEAD/REGISTRY_PER_PERFORM_BYTE_GAS_OVERHEAD) by at least ' + + // estimatedGasOverhead + // .sub(actualGasOverhead) + // .sub(gasEstimationMargin) + // .toString(), + // ) + // }, + // ) + // }) + // }) + }) + }) + + describeMaybe( + '#transmit with upkeep batches [ @skip-coverage ]', + function () { + const numPassingConditionalUpkeepsArray = [0, 1, 5] + const numPassingLogUpkeepsArray = [0, 1, 5] + const numFailingUpkeepsArray = [0, 3] + + for (let idx = 0; idx < numPassingConditionalUpkeepsArray.length; idx++) { + for (let jdx = 0; jdx < numPassingLogUpkeepsArray.length; jdx++) { + for (let kdx = 0; kdx < numFailingUpkeepsArray.length; kdx++) { + const numPassingConditionalUpkeeps = + numPassingConditionalUpkeepsArray[idx] + const numPassingLogUpkeeps = numPassingLogUpkeepsArray[jdx] + const numFailingUpkeeps = numFailingUpkeepsArray[kdx] + if ( + numPassingConditionalUpkeeps == 0 && + numPassingLogUpkeeps == 0 + ) { + continue + } + it( + '[Conditional:' + + numPassingConditionalUpkeeps + + ',Log:' + + numPassingLogUpkeeps + + ',Failures:' + + numFailingUpkeeps + + '] performs successful upkeeps and does not charge failing upkeeps', + async () => { + const allUpkeeps = await getMultipleUpkeepsDeployedAndFunded( + numPassingConditionalUpkeeps, + numPassingLogUpkeeps, + numFailingUpkeeps, + ) + const passingConditionalUpkeepIds = + allUpkeeps.passingConditionalUpkeepIds + const passingLogUpkeepIds = allUpkeeps.passingLogUpkeepIds + const failingUpkeepIds = allUpkeeps.failingUpkeepIds + + const keeperBefore = await registry.getTransmitterInfo( + await keeper1.getAddress(), + ) + const keeperLinkBefore = await linkToken.balanceOf( + await keeper1.getAddress(), + ) + const registryLinkBefore = await linkToken.balanceOf( + registry.address, + ) + const registryPremiumBefore = (await registry.getState()).state + .totalPremium + const registrationConditionalPassingBefore = await Promise.all( + passingConditionalUpkeepIds.map(async (id) => { + const reg = await registry.getUpkeep(BigNumber.from(id)) + assert.equal(reg.lastPerformedBlockNumber.toString(), '0') + return reg + }), + ) + const registrationLogPassingBefore = await Promise.all( + passingLogUpkeepIds.map(async (id) => { + const reg = await registry.getUpkeep(BigNumber.from(id)) + assert.equal(reg.lastPerformedBlockNumber.toString(), '0') + return reg + }), + ) + const registrationFailingBefore = await Promise.all( + failingUpkeepIds.map(async (id) => { + const reg = await registry.getUpkeep(BigNumber.from(id)) + assert.equal(reg.lastPerformedBlockNumber.toString(), '0') + return reg + }), + ) + + // cancel upkeeps so they will fail in the transmit process + // must call the cancel upkeep as the owner to avoid the CANCELLATION_DELAY + for (let ldx = 0; ldx < failingUpkeepIds.length; ldx++) { + await registry + .connect(owner) + .cancelUpkeep(failingUpkeepIds[ldx]) + } + + const tx = await getTransmitTx( + registry, + keeper1, + passingConditionalUpkeepIds.concat( + passingLogUpkeepIds.concat(failingUpkeepIds), + ), + ) + + const receipt = await tx.wait() + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + // exactly numPassingUpkeeps Upkeep Performed should be emitted + assert.equal( + upkeepPerformedLogs.length, + numPassingConditionalUpkeeps + numPassingLogUpkeeps, + ) + const cancelledUpkeepReportLogs = + parseCancelledUpkeepReportLogs(receipt) + // exactly numFailingUpkeeps Upkeep Performed should be emitted + assert.equal( + cancelledUpkeepReportLogs.length, + numFailingUpkeeps, + ) + + const keeperAfter = await registry.getTransmitterInfo( + await keeper1.getAddress(), + ) + const keeperLinkAfter = await linkToken.balanceOf( + await keeper1.getAddress(), + ) + const registryLinkAfter = await linkToken.balanceOf( + registry.address, + ) + const registrationConditionalPassingAfter = await Promise.all( + passingConditionalUpkeepIds.map(async (id) => { + return await registry.getUpkeep(BigNumber.from(id)) + }), + ) + const registrationLogPassingAfter = await Promise.all( + passingLogUpkeepIds.map(async (id) => { + return await registry.getUpkeep(BigNumber.from(id)) + }), + ) + const registrationFailingAfter = await Promise.all( + failingUpkeepIds.map(async (id) => { + return await registry.getUpkeep(BigNumber.from(id)) + }), + ) + const registryPremiumAfter = (await registry.getState()).state + .totalPremium + const premium = registryPremiumAfter.sub(registryPremiumBefore) + + let netPayment = BigNumber.from('0') + for (let i = 0; i < numPassingConditionalUpkeeps; i++) { + const id = upkeepPerformedLogs[i].args.id + const gasUsed = upkeepPerformedLogs[i].args.gasUsed + const gasOverhead = upkeepPerformedLogs[i].args.gasOverhead + const totalPayment = upkeepPerformedLogs[i].args.totalPayment + + expect(id).to.equal(passingConditionalUpkeepIds[i]) + assert.isTrue(gasUsed.gt(BigNumber.from('0'))) + assert.isTrue(gasOverhead.gt(BigNumber.from('0'))) + assert.isTrue(totalPayment.gt(BigNumber.from('0'))) + + // Balance should be deducted + assert.equal( + registrationConditionalPassingBefore[i].balance + .sub(totalPayment) + .toString(), + registrationConditionalPassingAfter[i].balance.toString(), + ) + + // Amount spent should be updated correctly + assert.equal( + registrationConditionalPassingAfter[i].amountSpent + .sub(totalPayment) + .toString(), + registrationConditionalPassingBefore[ + i + ].amountSpent.toString(), + ) + + // Last perform block number should be updated + assert.equal( + registrationConditionalPassingAfter[ + i + ].lastPerformedBlockNumber.toString(), + tx.blockNumber?.toString(), + ) + + netPayment = netPayment.add(totalPayment) + } + + for (let i = 0; i < numPassingLogUpkeeps; i++) { + const id = + upkeepPerformedLogs[numPassingConditionalUpkeeps + i].args + .id + const gasUsed = + upkeepPerformedLogs[numPassingConditionalUpkeeps + i].args + .gasUsed + const gasOverhead = + upkeepPerformedLogs[numPassingConditionalUpkeeps + i].args + .gasOverhead + const totalPayment = + upkeepPerformedLogs[numPassingConditionalUpkeeps + i].args + .totalPayment + + expect(id).to.equal(passingLogUpkeepIds[i]) + assert.isTrue(gasUsed.gt(BigNumber.from('0'))) + assert.isTrue(gasOverhead.gt(BigNumber.from('0'))) + assert.isTrue(totalPayment.gt(BigNumber.from('0'))) + + // Balance should be deducted + assert.equal( + registrationLogPassingBefore[i].balance + .sub(totalPayment) + .toString(), + registrationLogPassingAfter[i].balance.toString(), + ) + + // Amount spent should be updated correctly + assert.equal( + registrationLogPassingAfter[i].amountSpent + .sub(totalPayment) + .toString(), + registrationLogPassingBefore[i].amountSpent.toString(), + ) + + // Last perform block number should not be updated for log triggers + assert.equal( + registrationLogPassingAfter[ + i + ].lastPerformedBlockNumber.toString(), + '0', + ) + + netPayment = netPayment.add(totalPayment) + } + + for (let i = 0; i < numFailingUpkeeps; i++) { + // CancelledUpkeep log should be emitted + const id = cancelledUpkeepReportLogs[i].args.id + expect(id).to.equal(failingUpkeepIds[i]) + + // Balance and amount spent should be same + assert.equal( + registrationFailingBefore[i].balance.toString(), + registrationFailingAfter[i].balance.toString(), + ) + assert.equal( + registrationFailingBefore[i].amountSpent.toString(), + registrationFailingAfter[i].amountSpent.toString(), + ) + + // Last perform block number should not be updated + assert.equal( + registrationFailingAfter[ + i + ].lastPerformedBlockNumber.toString(), + '0', + ) + } + + // Keeper payment is gasPayment + premium / num keepers + const keeperPayment = netPayment + .sub(premium) + .add(premium.div(BigNumber.from(keeperAddresses.length))) + + // Keeper should be paid net payment for all passed upkeeps + assert.equal( + keeperAfter.balance.sub(keeperPayment).toString(), + keeperBefore.balance.toString(), + ) + + assert.isTrue(keeperLinkAfter.eq(keeperLinkBefore)) + assert.isTrue(registryLinkBefore.eq(registryLinkAfter)) + }, + ) + + it( + '[Conditional:' + + numPassingConditionalUpkeeps + + ',Log' + + numPassingLogUpkeeps + + ',Failures:' + + numFailingUpkeeps + + '] splits gas overhead appropriately among performed upkeeps [ @skip-coverage ]', + async () => { + const allUpkeeps = await getMultipleUpkeepsDeployedAndFunded( + numPassingConditionalUpkeeps, + numPassingLogUpkeeps, + numFailingUpkeeps, + ) + const passingConditionalUpkeepIds = + allUpkeeps.passingConditionalUpkeepIds + const passingLogUpkeepIds = allUpkeeps.passingLogUpkeepIds + const failingUpkeepIds = allUpkeeps.failingUpkeepIds + + // Perform the upkeeps once to remove non-zero storage slots and have predictable gas measurement + let tx = await getTransmitTx( + registry, + keeper1, + passingConditionalUpkeepIds.concat( + passingLogUpkeepIds.concat(failingUpkeepIds), + ), + ) + + await tx.wait() + + // cancel upkeeps so they will fail in the transmit process + // must call the cancel upkeep as the owner to avoid the CANCELLATION_DELAY + for (let ldx = 0; ldx < failingUpkeepIds.length; ldx++) { + await registry + .connect(owner) + .cancelUpkeep(failingUpkeepIds[ldx]) + } + + // Do the actual thing + + tx = await getTransmitTx( + registry, + keeper1, + passingConditionalUpkeepIds.concat( + passingLogUpkeepIds.concat(failingUpkeepIds), + ), + ) + + const receipt = await tx.wait() + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + // exactly numPassingUpkeeps Upkeep Performed should be emitted + assert.equal( + upkeepPerformedLogs.length, + numPassingConditionalUpkeeps + numPassingLogUpkeeps, + ) + + let netGasUsedPlusChargedOverhead = BigNumber.from('0') + for (let i = 0; i < numPassingConditionalUpkeeps; i++) { + const gasUsed = upkeepPerformedLogs[i].args.gasUsed + const chargedGasOverhead = + upkeepPerformedLogs[i].args.gasOverhead + + assert.isTrue(gasUsed.gt(BigNumber.from('0'))) + assert.isTrue(chargedGasOverhead.gt(BigNumber.from('0'))) + + // Overhead should be same for every upkeep + assert.isTrue( + chargedGasOverhead.eq( + upkeepPerformedLogs[0].args.gasOverhead, + ), + ) + netGasUsedPlusChargedOverhead = netGasUsedPlusChargedOverhead + .add(gasUsed) + .add(chargedGasOverhead) + } + + for (let i = 0; i < numPassingLogUpkeeps; i++) { + const gasUsed = + upkeepPerformedLogs[numPassingConditionalUpkeeps + i].args + .gasUsed + const chargedGasOverhead = + upkeepPerformedLogs[numPassingConditionalUpkeeps + i].args + .gasOverhead + + assert.isTrue(gasUsed.gt(BigNumber.from('0'))) + assert.isTrue(chargedGasOverhead.gt(BigNumber.from('0'))) + + // Overhead should be same for every upkeep + assert.isTrue( + chargedGasOverhead.eq( + upkeepPerformedLogs[numPassingConditionalUpkeeps].args + .gasOverhead, + ), + ) + netGasUsedPlusChargedOverhead = netGasUsedPlusChargedOverhead + .add(gasUsed) + .add(chargedGasOverhead) + } + + console.log( + 'Gas Benchmarking - batching (passedConditionalUpkeeps: ', + numPassingConditionalUpkeeps, + 'passedLogUpkeeps:', + numPassingLogUpkeeps, + 'failedUpkeeps:', + numFailingUpkeeps, + '): ', + numPassingConditionalUpkeeps > 0 + ? 'charged conditional overhead' + : '', + numPassingConditionalUpkeeps > 0 + ? upkeepPerformedLogs[0].args.gasOverhead.toString() + : '', + numPassingLogUpkeeps > 0 ? 'charged log overhead' : '', + numPassingLogUpkeeps > 0 + ? upkeepPerformedLogs[ + numPassingConditionalUpkeeps + ].args.gasOverhead.toString() + : '', + ' margin over gasUsed', + netGasUsedPlusChargedOverhead.sub(receipt.gasUsed).toString(), + ) + + // The total gas charged should be greater than tx gas + assert.isTrue( + netGasUsedPlusChargedOverhead.gt(receipt.gasUsed), + 'Charged gas overhead is too low for batch upkeeps, increase ACCOUNTING_PER_UPKEEP_GAS_OVERHEAD', + ) + }, + ) + } + } + } + + it('has enough perform gas overhead for large batches [ @skip-coverage ]', async () => { + const numUpkeeps = 20 + const upkeepIds: BigNumber[] = [] + let totalPerformGas = BigNumber.from('0') + for (let i = 0; i < numUpkeeps; i++) { + const mock = await upkeepMockFactory.deploy() + const tx = await registry + .connect(owner) + .registerUpkeep( + mock.address, + performGas, + await admin.getAddress(), + Trigger.CONDITION, + linkToken.address, + '0x', + '0x', + '0x', + ) + const testUpkeepId = await getUpkeepID(tx) + upkeepIds.push(testUpkeepId) + + // Add funds to passing upkeeps + await registry.connect(owner).addFunds(testUpkeepId, toWei('10')) + + await mock.setCanPerform(true) + await mock.setPerformGasToBurn(performGas) + + totalPerformGas = totalPerformGas.add(performGas) + } + + // Should revert with no overhead added + await evmRevert( + getTransmitTx(registry, keeper1, upkeepIds, { + gasLimit: totalPerformGas, + }), + ) + // Should not revert with overhead added + await getTransmitTx(registry, keeper1, upkeepIds, { + gasLimit: totalPerformGas.add(transmitGasOverhead), + }) + }) + }, + ) + + describe('#recoverFunds', () => { + const sent = toWei('7') + + beforeEach(async () => { + await linkToken.connect(admin).approve(registry.address, toWei('100')) + await linkToken + .connect(owner) + .transfer(await keeper1.getAddress(), toWei('1000')) + + // add funds to upkeep 1 and perform and withdraw some payment + const tx = await registry + .connect(owner) + .registerUpkeep( + mock.address, + performGas, + await admin.getAddress(), + Trigger.CONDITION, + linkToken.address, + '0x', + '0x', + '0x', + ) + + const id1 = await getUpkeepID(tx) + await registry.connect(admin).addFunds(id1, toWei('5')) + + await getTransmitTx(registry, keeper1, [id1]) + await getTransmitTx(registry, keeper2, [id1]) + await getTransmitTx(registry, keeper3, [id1]) + + await registry + .connect(payee1) + .withdrawPayment( + await keeper1.getAddress(), + await nonkeeper.getAddress(), + ) + + // transfer funds directly to the registry + await linkToken.connect(keeper1).transfer(registry.address, sent) + + // add funds to upkeep 2 and perform and withdraw some payment + const tx2 = await registry + .connect(owner) + .registerUpkeep( + mock.address, + performGas, + await admin.getAddress(), + Trigger.CONDITION, + linkToken.address, + '0x', + '0x', + '0x', + ) + const id2 = await getUpkeepID(tx2) + await registry.connect(admin).addFunds(id2, toWei('5')) + + await getTransmitTx(registry, keeper1, [id2]) + await getTransmitTx(registry, keeper2, [id2]) + await getTransmitTx(registry, keeper3, [id2]) + + await registry + .connect(payee2) + .withdrawPayment( + await keeper2.getAddress(), + await nonkeeper.getAddress(), + ) + + // transfer funds using onTokenTransfer + const data = ethers.utils.defaultAbiCoder.encode(['uint256'], [id2]) + await linkToken + .connect(owner) + .transferAndCall(registry.address, toWei('1'), data) + + // withdraw some funds + await registry.connect(owner).cancelUpkeep(id1) + await registry + .connect(admin) + .withdrawFunds(id1, await nonkeeper.getAddress()) + }) + }) + + describe('#getMinBalanceForUpkeep / #checkUpkeep / #transmit', () => { + it('calculates the minimum balance appropriately', async () => { + await mock.setCanCheck(true) + + const oneWei = BigNumber.from(1) + const minBalance = await registry.getMinBalanceForUpkeep(upkeepId) + const tooLow = minBalance.sub(oneWei) + + await registry.connect(admin).addFunds(upkeepId, tooLow) + let checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepId) + + assert.equal(checkUpkeepResult.upkeepNeeded, false) + assert.equal( + checkUpkeepResult.upkeepFailureReason, + UpkeepFailureReason.INSUFFICIENT_BALANCE, + ) + + await registry.connect(admin).addFunds(upkeepId, oneWei) + checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepId) + assert.equal(checkUpkeepResult.upkeepNeeded, true) + }) + + it('uses maxPerformData size in checkUpkeep but actual performDataSize in transmit', async () => { + const tx = await registry + .connect(owner) + .registerUpkeep( + mock.address, + performGas, + await admin.getAddress(), + Trigger.CONDITION, + linkToken.address, + '0x', + '0x', + '0x', + ) + const upkeepID = await getUpkeepID(tx) + await mock.setCanCheck(true) + await mock.setCanPerform(true) + + // upkeep is underfunded by 1 wei + const minBalance1 = (await registry.getMinBalanceForUpkeep(upkeepID)).sub( + 1, + ) + await registry.connect(owner).addFunds(upkeepID, minBalance1) + + // upkeep check should return false, 2 should return true + const checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepID) + assert.equal(checkUpkeepResult.upkeepNeeded, false) + assert.equal( + checkUpkeepResult.upkeepFailureReason, + UpkeepFailureReason.INSUFFICIENT_BALANCE, + ) + + // however upkeep should perform and pay all the remaining balance + let maxPerformData = '0x' + for (let i = 0; i < maxPerformDataSize.toNumber(); i++) { + maxPerformData += '11' + } + + const tx2 = await getTransmitTx(registry, keeper1, [upkeepID], { + gasPrice: gasWei.mul(gasCeilingMultiplier), + performDatas: [maxPerformData], + }) + + const receipt = await tx2.wait() + const upkeepPerformedLogs = parseUpkeepPerformedLogs(receipt) + assert.equal(upkeepPerformedLogs.length, 1) + }) + }) + + describe('#withdrawFunds', () => { + let upkeepId2: BigNumber + + beforeEach(async () => { + const tx = await registry + .connect(owner) + .registerUpkeep( + mock.address, + performGas, + await admin.getAddress(), + Trigger.CONDITION, + linkToken.address, + '0x', + '0x', + '0x', + ) + upkeepId2 = await getUpkeepID(tx) + + await registry.connect(admin).addFunds(upkeepId, toWei('100')) + await registry.connect(admin).addFunds(upkeepId2, toWei('100')) + + // Do a perform so that upkeep is charged some amount + await getTransmitTx(registry, keeper1, [upkeepId]) + await getTransmitTx(registry, keeper1, [upkeepId2]) + }) + + describe('after the registration is paused, then cancelled', () => { + it('allows the admin to withdraw', async () => { + const balance = await registry.getBalance(upkeepId) + const payee = await payee1.getAddress() + await registry.connect(admin).pauseUpkeep(upkeepId) + await registry.connect(owner).cancelUpkeep(upkeepId) + await expect(() => + registry.connect(admin).withdrawFunds(upkeepId, payee), + ).to.changeTokenBalance(linkToken, payee1, balance) + }) + }) + + describe('after the registration is cancelled', () => { + beforeEach(async () => { + await registry.connect(owner).cancelUpkeep(upkeepId) + await registry.connect(owner).cancelUpkeep(upkeepId2) + }) + + it('can be called successively on two upkeeps', async () => { + await registry + .connect(admin) + .withdrawFunds(upkeepId, await payee1.getAddress()) + await registry + .connect(admin) + .withdrawFunds(upkeepId2, await payee1.getAddress()) + }) + + it('moves the funds out and updates the balance and emits an event', async () => { + const payee1Before = await linkToken.balanceOf( + await payee1.getAddress(), + ) + const registryBefore = await linkToken.balanceOf(registry.address) + + let registration = await registry.getUpkeep(upkeepId) + const previousBalance = registration.balance + + const tx = await registry + .connect(admin) + .withdrawFunds(upkeepId, await payee1.getAddress()) + await expect(tx) + .to.emit(registry, 'FundsWithdrawn') + .withArgs(upkeepId, previousBalance, await payee1.getAddress()) + + const payee1After = await linkToken.balanceOf(await payee1.getAddress()) + const registryAfter = await linkToken.balanceOf(registry.address) + + assert.isTrue(payee1Before.add(previousBalance).eq(payee1After)) + assert.isTrue(registryBefore.sub(previousBalance).eq(registryAfter)) + + registration = await registry.getUpkeep(upkeepId) + assert.equal(registration.balance.toNumber(), 0) + }) + }) + }) + + describe('#simulatePerformUpkeep', () => { + it('reverts if called by non zero address', async () => { + await evmRevertCustomError( + registry + .connect(await owner.getAddress()) + .callStatic.simulatePerformUpkeep(upkeepId, '0x'), + registry, + 'OnlySimulatedBackend', + ) + }) + + it('reverts when registry is paused', async () => { + await registry.connect(owner).pause() + await evmRevertCustomError( + registry + .connect(zeroAddress) + .callStatic.simulatePerformUpkeep(upkeepId, '0x'), + registry, + 'RegistryPaused', + ) + }) + + it('returns false and gasUsed when perform fails', async () => { + await mock.setCanPerform(false) + + const simulatePerformResult = await registry + .connect(zeroAddress) + .callStatic.simulatePerformUpkeep(upkeepId, '0x') + + assert.equal(simulatePerformResult.success, false) + assert.isTrue(simulatePerformResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used + }) + + it('returns true, gasUsed, and performGas when perform succeeds', async () => { + await mock.setCanPerform(true) + + const simulatePerformResult = await registry + .connect(zeroAddress) + .callStatic.simulatePerformUpkeep(upkeepId, '0x') + + assert.equal(simulatePerformResult.success, true) + assert.isTrue(simulatePerformResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used + }) + + it('returns correct amount of gasUsed when perform succeeds', async () => { + await mock.setCanPerform(true) + await mock.setPerformGasToBurn(performGas) // 1,000,000 + + // increase upkeep gas limit because the mock gas bound caller will always return 500,000 as the L1 gas used + // that brings the total gas used to about 1M + 0.5M = 1.5M + await registry + .connect(admin) + .setUpkeepGasLimit(upkeepId, BigNumber.from(2000000)) + + const simulatePerformResult = await registry + .connect(zeroAddress) + .callStatic.simulatePerformUpkeep(upkeepId, '0x') + + // Full execute gas should be used, with some performGasBuffer(1000) + assert.isTrue( + simulatePerformResult.gasUsed.gt( + performGas.add(pubdataGas).sub(BigNumber.from('1000')), + ), + ) + }) + }) + + describe('#checkUpkeep', () => { + it('reverts if called by non zero address', async () => { + await evmRevertCustomError( + registry + .connect(await owner.getAddress()) + .callStatic['checkUpkeep(uint256)'](upkeepId), + registry, + 'OnlySimulatedBackend', + ) + }) + + it('returns false and error code if the upkeep is cancelled by admin', async () => { + await registry.connect(admin).cancelUpkeep(upkeepId) + + const checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepId) + + assert.equal(checkUpkeepResult.upkeepNeeded, false) + assert.equal(checkUpkeepResult.performData, '0x') + assert.equal( + checkUpkeepResult.upkeepFailureReason, + UpkeepFailureReason.UPKEEP_CANCELLED, + ) + expect(checkUpkeepResult.gasUsed).to.equal(0) + expect(checkUpkeepResult.gasLimit).to.equal(performGas) + }) + + it('returns false and error code if the upkeep is cancelled by owner', async () => { + await registry.connect(owner).cancelUpkeep(upkeepId) + + const checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepId) + + assert.equal(checkUpkeepResult.upkeepNeeded, false) + assert.equal(checkUpkeepResult.performData, '0x') + assert.equal( + checkUpkeepResult.upkeepFailureReason, + UpkeepFailureReason.UPKEEP_CANCELLED, + ) + expect(checkUpkeepResult.gasUsed).to.equal(0) + expect(checkUpkeepResult.gasLimit).to.equal(performGas) + }) + + it('returns false and error code if the registry is paused', async () => { + await registry.connect(owner).pause() + + const checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepId) + + assert.equal(checkUpkeepResult.upkeepNeeded, false) + assert.equal(checkUpkeepResult.performData, '0x') + assert.equal( + checkUpkeepResult.upkeepFailureReason, + UpkeepFailureReason.REGISTRY_PAUSED, + ) + expect(checkUpkeepResult.gasUsed).to.equal(0) + expect(checkUpkeepResult.gasLimit).to.equal(performGas) + }) + + it('returns false and error code if the upkeep is paused', async () => { + await registry.connect(admin).pauseUpkeep(upkeepId) + + const checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepId) + + assert.equal(checkUpkeepResult.upkeepNeeded, false) + assert.equal(checkUpkeepResult.performData, '0x') + assert.equal( + checkUpkeepResult.upkeepFailureReason, + UpkeepFailureReason.UPKEEP_PAUSED, + ) + expect(checkUpkeepResult.gasUsed).to.equal(0) + expect(checkUpkeepResult.gasLimit).to.equal(performGas) + }) + + it('returns false and error code if user is out of funds', async () => { + const checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepId) + + assert.equal(checkUpkeepResult.upkeepNeeded, false) + assert.equal(checkUpkeepResult.performData, '0x') + assert.equal( + checkUpkeepResult.upkeepFailureReason, + UpkeepFailureReason.INSUFFICIENT_BALANCE, + ) + expect(checkUpkeepResult.gasUsed).to.equal(0) + expect(checkUpkeepResult.gasLimit).to.equal(performGas) + }) + + context('when the registration is funded', () => { + beforeEach(async () => { + await linkToken.connect(admin).approve(registry.address, toWei('200')) + await registry.connect(admin).addFunds(upkeepId, toWei('100')) + await registry.connect(admin).addFunds(logUpkeepId, toWei('100')) + }) + + it('returns false, error code, and revert data if the target check reverts', async () => { + await mock.setShouldRevertCheck(true) + await mock.setCheckRevertReason( + 'custom revert error, clever way to insert offchain data', + ) + const checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepId) + assert.equal(checkUpkeepResult.upkeepNeeded, false) + + const revertReasonBytes = `0x${checkUpkeepResult.performData.slice(10)}` // remove sighash + assert.equal( + ethers.utils.defaultAbiCoder.decode(['string'], revertReasonBytes)[0], + 'custom revert error, clever way to insert offchain data', + ) + assert.equal( + checkUpkeepResult.upkeepFailureReason, + UpkeepFailureReason.TARGET_CHECK_REVERTED, + ) + assert.isTrue(checkUpkeepResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used + expect(checkUpkeepResult.gasLimit).to.equal(performGas) + // Feed data should be returned here + assert.isTrue(checkUpkeepResult.fastGasWei.gt(BigNumber.from('0'))) + assert.isTrue(checkUpkeepResult.linkUSD.gt(BigNumber.from('0'))) + }) + + it('returns false, error code, and no revert data if the target check revert data exceeds maxRevertDataSize', async () => { + await mock.setShouldRevertCheck(true) + let longRevertReason = '' + for (let i = 0; i <= maxRevertDataSize.toNumber(); i++) { + longRevertReason += 'x' + } + await mock.setCheckRevertReason(longRevertReason) + const checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepId) + assert.equal(checkUpkeepResult.upkeepNeeded, false) + + assert.equal(checkUpkeepResult.performData, '0x') + assert.equal( + checkUpkeepResult.upkeepFailureReason, + UpkeepFailureReason.REVERT_DATA_EXCEEDS_LIMIT, + ) + assert.isTrue(checkUpkeepResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used + expect(checkUpkeepResult.gasLimit).to.equal(performGas) + }) + + it('returns false and error code if the upkeep is not needed', async () => { + await mock.setCanCheck(false) + const checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepId) + + assert.equal(checkUpkeepResult.upkeepNeeded, false) + assert.equal(checkUpkeepResult.performData, '0x') + assert.equal( + checkUpkeepResult.upkeepFailureReason, + UpkeepFailureReason.UPKEEP_NOT_NEEDED, + ) + assert.isTrue(checkUpkeepResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used + expect(checkUpkeepResult.gasLimit).to.equal(performGas) + }) + + it('returns false and error code if the performData exceeds limit', async () => { + let longBytes = '0x' + for (let i = 0; i < 5000; i++) { + longBytes += '1' + } + await mock.setCanCheck(true) + await mock.setPerformData(longBytes) + + const checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepId) + + assert.equal(checkUpkeepResult.upkeepNeeded, false) + assert.equal(checkUpkeepResult.performData, '0x') + assert.equal( + checkUpkeepResult.upkeepFailureReason, + UpkeepFailureReason.PERFORM_DATA_EXCEEDS_LIMIT, + ) + assert.isTrue(checkUpkeepResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used + expect(checkUpkeepResult.gasLimit).to.equal(performGas) + }) + + it('returns true with gas used if the target can execute', async () => { + await mock.setCanCheck(true) + await mock.setPerformData(randomBytes) + + const latestBlock = await ethers.provider.getBlock('latest') + + const checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepId, { + blockTag: latestBlock.number, + }) + + assert.equal(checkUpkeepResult.upkeepNeeded, true) + assert.equal(checkUpkeepResult.performData, randomBytes) + assert.equal( + checkUpkeepResult.upkeepFailureReason, + UpkeepFailureReason.NONE, + ) + assert.isTrue(checkUpkeepResult.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used + expect(checkUpkeepResult.gasLimit).to.equal(performGas) + assert.isTrue(checkUpkeepResult.fastGasWei.eq(gasWei)) + assert.isTrue(checkUpkeepResult.linkUSD.eq(linkUSD)) + }) + + it('calls checkLog for log-trigger upkeeps', async () => { + const log: Log = { + index: 0, + timestamp: 0, + txHash: ethers.utils.randomBytes(32), + blockNumber: 100, + blockHash: ethers.utils.randomBytes(32), + source: randomAddress(), + topics: [ethers.utils.randomBytes(32), ethers.utils.randomBytes(32)], + data: ethers.utils.randomBytes(1000), + } + + await ltUpkeep.mock.checkLog.withArgs(log, '0x').returns(true, '0x1234') + + const checkData = encodeLog(log) + + const checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256,bytes)'](logUpkeepId, checkData) + + expect(checkUpkeepResult.upkeepNeeded).to.be.true + expect(checkUpkeepResult.performData).to.equal('0x1234') + }) + + itMaybe( + 'has a large enough gas overhead to cover upkeeps that use all their gas [ @skip-coverage ]', + async () => { + await mock.setCanCheck(true) + await mock.setCheckGasToBurn(checkGasLimit) + const gas = checkGasLimit.add(checkGasOverhead) + const checkUpkeepResult = await registry + .connect(zeroAddress) + .callStatic['checkUpkeep(uint256)'](upkeepId, { + gasLimit: gas, + }) + + assert.equal(checkUpkeepResult.upkeepNeeded, true) + }, + ) + }) + }) + + describe('#getMaxPaymentForGas', () => { + itMaybe('calculates the max fee appropriately in ZKSync', async () => { + await verifyMaxPayment(registry, moduleBase) + }) + + it('uses the fallback gas price if the feed has issues in ZKSync', async () => { + const chainModuleOverheads = await moduleBase.getGasOverhead() + const expectedFallbackMaxPayment = linkForGas( + performGas, + registryConditionalOverhead + .add(registryPerSignerGasOverhead.mul(f + 1)) + .add(chainModuleOverheads.chainModuleFixedOverhead), + gasCeilingMultiplier.mul('2'), // fallbackGasPrice is 2x gas price + paymentPremiumPPB, + flatFeeMilliCents, + ).total + + // Stale feed + let roundId = 99 + const answer = 100 + let updatedAt = 946684800 // New Years 2000 🥳 + let startedAt = 946684799 + await gasPriceFeed + .connect(owner) + .updateRoundData(roundId, answer, updatedAt, startedAt) + + assert.equal( + expectedFallbackMaxPayment.toString(), + ( + await registry.getMaxPaymentForGas( + upkeepId, + Trigger.CONDITION, + performGas, + linkToken.address, + ) + ).toString(), + ) + + // Negative feed price + roundId = 100 + updatedAt = now() + startedAt = 946684799 + await gasPriceFeed + .connect(owner) + .updateRoundData(roundId, -100, updatedAt, startedAt) + + assert.equal( + expectedFallbackMaxPayment.toString(), + ( + await registry.getMaxPaymentForGas( + upkeepId, + Trigger.CONDITION, + performGas, + linkToken.address, + ) + ).toString(), + ) + + // Zero feed price + roundId = 101 + updatedAt = now() + startedAt = 946684799 + await gasPriceFeed + .connect(owner) + .updateRoundData(roundId, 0, updatedAt, startedAt) + + assert.equal( + expectedFallbackMaxPayment.toString(), + ( + await registry.getMaxPaymentForGas( + upkeepId, + Trigger.CONDITION, + performGas, + linkToken.address, + ) + ).toString(), + ) + }) + + it('uses the fallback link price if the feed has issues in ZKSync', async () => { + const chainModuleOverheads = await moduleBase.getGasOverhead() + const expectedFallbackMaxPayment = linkForGas( + performGas, + registryConditionalOverhead + .add(registryPerSignerGasOverhead.mul(f + 1)) + .add(chainModuleOverheads.chainModuleFixedOverhead), + gasCeilingMultiplier.mul('2'), // fallbackLinkPrice is 1/2 link price, so multiply by 2 + paymentPremiumPPB, + flatFeeMilliCents, + ).total + + // Stale feed + let roundId = 99 + const answer = 100 + let updatedAt = 946684800 // New Years 2000 🥳 + let startedAt = 946684799 + await linkUSDFeed + .connect(owner) + .updateRoundData(roundId, answer, updatedAt, startedAt) + + assert.equal( + expectedFallbackMaxPayment.toString(), + ( + await registry.getMaxPaymentForGas( + upkeepId, + Trigger.CONDITION, + performGas, + linkToken.address, + ) + ).toString(), + ) + + // Negative feed price + roundId = 100 + updatedAt = now() + startedAt = 946684799 + await linkUSDFeed + .connect(owner) + .updateRoundData(roundId, -100, updatedAt, startedAt) + + assert.equal( + expectedFallbackMaxPayment.toString(), + ( + await registry.getMaxPaymentForGas( + upkeepId, + Trigger.CONDITION, + performGas, + linkToken.address, + ) + ).toString(), + ) + + // Zero feed price + roundId = 101 + updatedAt = now() + startedAt = 946684799 + await linkUSDFeed + .connect(owner) + .updateRoundData(roundId, 0, updatedAt, startedAt) + + assert.equal( + expectedFallbackMaxPayment.toString(), + ( + await registry.getMaxPaymentForGas( + upkeepId, + Trigger.CONDITION, + performGas, + linkToken.address, + ) + ).toString(), + ) + }) + }) + + describe('#typeAndVersion', () => { + it('uses the correct type and version', async () => { + const typeAndVersion = await registry.typeAndVersion() + assert.equal(typeAndVersion, 'AutomationRegistry 2.3.0') + }) + }) + + describeMaybe('#setConfig - onchain', async () => { + const maxGas = BigNumber.from(6) + const staleness = BigNumber.from(4) + const ceiling = BigNumber.from(5) + const newMaxCheckDataSize = BigNumber.from(10000) + const newMaxPerformDataSize = BigNumber.from(10000) + const newMaxRevertDataSize = BigNumber.from(10000) + const newMaxPerformGas = BigNumber.from(10000000) + const fbGasEth = BigNumber.from(7) + const fbLinkEth = BigNumber.from(8) + const fbNativeEth = BigNumber.from(100) + const newTranscoder = randomAddress() + const newRegistrars = [randomAddress(), randomAddress()] + const upkeepManager = randomAddress() + const financeAdminAddress = randomAddress() + + const newConfig: OnChainConfig = { + checkGasLimit: maxGas, + stalenessSeconds: staleness, + gasCeilingMultiplier: ceiling, + maxCheckDataSize: newMaxCheckDataSize, + maxPerformDataSize: newMaxPerformDataSize, + maxRevertDataSize: newMaxRevertDataSize, + maxPerformGas: newMaxPerformGas, + fallbackGasPrice: fbGasEth, + fallbackLinkPrice: fbLinkEth, + fallbackNativePrice: fbNativeEth, + transcoder: newTranscoder, + registrars: newRegistrars, + upkeepPrivilegeManager: upkeepManager, + chainModule: moduleBase.address, + reorgProtectionEnabled: true, + financeAdmin: financeAdminAddress, + } + + it('reverts when called by anyone but the proposed owner', async () => { + await evmRevert( + registry + .connect(payee1) + .setConfigTypeSafe( + signerAddresses, + keeperAddresses, + f, + newConfig, + offchainVersion, + offchainBytes, + baseConfig[6], + baseConfig[7], + ), + 'Only callable by owner', + ) + }) + + it('reverts if signers or transmitters are the zero address', async () => { + await evmRevertCustomError( + registry + .connect(owner) + .setConfigTypeSafe( + [randomAddress(), randomAddress(), randomAddress(), zeroAddress], + [ + randomAddress(), + randomAddress(), + randomAddress(), + randomAddress(), + ], + f, + newConfig, + offchainVersion, + offchainBytes, + baseConfig[6], + baseConfig[7], + ), + registry, + 'InvalidSigner', + ) + + await evmRevertCustomError( + registry + .connect(owner) + .setConfigTypeSafe( + [ + randomAddress(), + randomAddress(), + randomAddress(), + randomAddress(), + ], + [randomAddress(), randomAddress(), randomAddress(), zeroAddress], + f, + newConfig, + offchainVersion, + offchainBytes, + baseConfig[6], + baseConfig[7], + ), + registry, + 'InvalidTransmitter', + ) + }) + + it('updates the onchainConfig and configDigest', async () => { + const old = await registry.getState() + const oldConfig = await registry.getConfig() + const oldState = old.state + assert.isTrue(stalenessSeconds.eq(oldConfig.stalenessSeconds)) + assert.isTrue(gasCeilingMultiplier.eq(oldConfig.gasCeilingMultiplier)) + + await registry + .connect(owner) + .setConfigTypeSafe( + signerAddresses, + keeperAddresses, + f, + newConfig, + offchainVersion, + offchainBytes, + [], + [], + ) + + const updated = await registry.getState() + const updatedConfig = updated.config + const updatedState = updated.state + assert.equal(updatedConfig.stalenessSeconds, staleness.toNumber()) + assert.equal(updatedConfig.gasCeilingMultiplier, ceiling.toNumber()) + assert.equal( + updatedConfig.maxCheckDataSize, + newMaxCheckDataSize.toNumber(), + ) + assert.equal( + updatedConfig.maxPerformDataSize, + newMaxPerformDataSize.toNumber(), + ) + assert.equal( + updatedConfig.maxRevertDataSize, + newMaxRevertDataSize.toNumber(), + ) + assert.equal(updatedConfig.maxPerformGas, newMaxPerformGas.toNumber()) + assert.equal(updatedConfig.checkGasLimit, maxGas.toNumber()) + assert.equal( + updatedConfig.fallbackGasPrice.toNumber(), + fbGasEth.toNumber(), + ) + assert.equal( + updatedConfig.fallbackLinkPrice.toNumber(), + fbLinkEth.toNumber(), + ) + assert.equal(updatedState.latestEpoch, 0) + + assert(oldState.configCount + 1 == updatedState.configCount) + assert( + oldState.latestConfigBlockNumber != + updatedState.latestConfigBlockNumber, + ) + assert(oldState.latestConfigDigest != updatedState.latestConfigDigest) + + assert.equal(updatedConfig.transcoder, newTranscoder) + assert.deepEqual(updatedConfig.registrars, newRegistrars) + assert.equal(updatedConfig.upkeepPrivilegeManager, upkeepManager) + }) + + it('maintains paused state when config is changed', async () => { + await registry.pause() + const old = await registry.getState() + assert.isTrue(old.state.paused) + + await registry + .connect(owner) + .setConfigTypeSafe( + signerAddresses, + keeperAddresses, + f, + newConfig, + offchainVersion, + offchainBytes, + [], + [], + ) + + const updated = await registry.getState() + assert.isTrue(updated.state.paused) + }) + + it('emits an event', async () => { + const tx = await registry + .connect(owner) + .setConfigTypeSafe( + signerAddresses, + keeperAddresses, + f, + newConfig, + offchainVersion, + offchainBytes, + [], + [], + ) + await expect(tx).to.emit(registry, 'ConfigSet') + }) + }) + + describe('#setConfig - offchain', () => { + let newKeepers: string[] + + beforeEach(async () => { + newKeepers = [ + await personas.Eddy.getAddress(), + await personas.Nick.getAddress(), + await personas.Neil.getAddress(), + await personas.Carol.getAddress(), + ] + }) + + it('reverts when called by anyone but the owner', async () => { + await evmRevert( + registry + .connect(payee1) + .setConfigTypeSafe( + newKeepers, + newKeepers, + f, + config, + offchainVersion, + offchainBytes, + baseConfig[6], + baseConfig[7], + ), + 'Only callable by owner', + ) + }) + + it('reverts if too many keeperAddresses set', async () => { + for (let i = 0; i < 40; i++) { + newKeepers.push(randomAddress()) + } + await evmRevertCustomError( + registry + .connect(owner) + .setConfigTypeSafe( + newKeepers, + newKeepers, + f, + config, + offchainVersion, + offchainBytes, + baseConfig[6], + baseConfig[7], + ), + registry, + 'TooManyOracles', + ) + }) + + it('reverts if f=0', async () => { + await evmRevertCustomError( + registry + .connect(owner) + .setConfigTypeSafe( + newKeepers, + newKeepers, + 0, + config, + offchainVersion, + offchainBytes, + baseConfig[6], + baseConfig[7], + ), + registry, + 'IncorrectNumberOfFaultyOracles', + ) + }) + + it('reverts if signers != transmitters length', async () => { + const signers = [randomAddress()] + await evmRevertCustomError( + registry + .connect(owner) + .setConfigTypeSafe( + signers, + newKeepers, + f, + config, + offchainVersion, + offchainBytes, + baseConfig[6], + baseConfig[7], + ), + registry, + 'IncorrectNumberOfSigners', + ) + }) + + it('reverts if signers <= 3f', async () => { + newKeepers.pop() + await evmRevertCustomError( + registry + .connect(owner) + .setConfigTypeSafe( + newKeepers, + newKeepers, + f, + config, + offchainVersion, + offchainBytes, + baseConfig[6], + baseConfig[7], + ), + registry, + 'IncorrectNumberOfSigners', + ) + }) + + it('reverts on repeated signers', async () => { + const newSigners = [ + await personas.Eddy.getAddress(), + await personas.Eddy.getAddress(), + await personas.Eddy.getAddress(), + await personas.Eddy.getAddress(), + ] + await evmRevertCustomError( + registry + .connect(owner) + .setConfigTypeSafe( + newSigners, + newKeepers, + f, + config, + offchainVersion, + offchainBytes, + baseConfig[6], + baseConfig[7], + ), + registry, + 'RepeatedSigner', + ) + }) + + it('reverts on repeated transmitters', async () => { + const newTransmitters = [ + await personas.Eddy.getAddress(), + await personas.Eddy.getAddress(), + await personas.Eddy.getAddress(), + await personas.Eddy.getAddress(), + ] + await evmRevertCustomError( + registry + .connect(owner) + .setConfigTypeSafe( + newKeepers, + newTransmitters, + f, + config, + offchainVersion, + offchainBytes, + baseConfig[6], + baseConfig[7], + ), + registry, + 'RepeatedTransmitter', + ) + }) + + itMaybe('stores new config and emits event', async () => { + // Perform an upkeep so that totalPremium is updated + await registry.connect(admin).addFunds(upkeepId, toWei('100')) + let tx = await getTransmitTx(registry, keeper1, [upkeepId]) + await tx.wait() + + const newOffChainVersion = BigNumber.from('2') + const newOffChainConfig = '0x1122' + + const old = await registry.getState() + const oldState = old.state + assert(oldState.totalPremium.gt(BigNumber.from('0'))) + + const newSigners = newKeepers + tx = await registry + .connect(owner) + .setConfigTypeSafe( + newSigners, + newKeepers, + f, + config, + newOffChainVersion, + newOffChainConfig, + [], + [], + ) + + const updated = await registry.getState() + const updatedState = updated.state + assert(oldState.totalPremium.eq(updatedState.totalPremium)) + + // Old signer addresses which are not in new signers should be non active + for (let i = 0; i < signerAddresses.length; i++) { + const signer = signerAddresses[i] + if (!newSigners.includes(signer)) { + assert(!(await registry.getSignerInfo(signer)).active) + assert((await registry.getSignerInfo(signer)).index == 0) + } + } + // New signer addresses should be active + for (let i = 0; i < newSigners.length; i++) { + const signer = newSigners[i] + assert((await registry.getSignerInfo(signer)).active) + assert((await registry.getSignerInfo(signer)).index == i) + } + // Old transmitter addresses which are not in new transmitter should be non active, update lastCollected but retain other info + for (let i = 0; i < keeperAddresses.length; i++) { + const transmitter = keeperAddresses[i] + if (!newKeepers.includes(transmitter)) { + assert(!(await registry.getTransmitterInfo(transmitter)).active) + assert((await registry.getTransmitterInfo(transmitter)).index == i) + assert( + (await registry.getTransmitterInfo(transmitter)).lastCollected.eq( + oldState.totalPremium.sub( + oldState.totalPremium.mod(keeperAddresses.length), + ), + ), + ) + } + } + // New transmitter addresses should be active + for (let i = 0; i < newKeepers.length; i++) { + const transmitter = newKeepers[i] + assert((await registry.getTransmitterInfo(transmitter)).active) + assert((await registry.getTransmitterInfo(transmitter)).index == i) + assert( + (await registry.getTransmitterInfo(transmitter)).lastCollected.eq( + oldState.totalPremium, + ), + ) + } + + // config digest should be updated + assert(oldState.configCount + 1 == updatedState.configCount) + assert( + oldState.latestConfigBlockNumber != + updatedState.latestConfigBlockNumber, + ) + assert(oldState.latestConfigDigest != updatedState.latestConfigDigest) + + //New config should be updated + assert.deepEqual(updated.signers, newKeepers) + assert.deepEqual(updated.transmitters, newKeepers) + + // Event should have been emitted + await expect(tx).to.emit(registry, 'ConfigSet') + }) + }) + + describe('#cancelUpkeep', () => { + describe('when called by the admin', async () => { + describeMaybe('when an upkeep has been performed', async () => { + beforeEach(async () => { + await linkToken.connect(owner).approve(registry.address, toWei('100')) + await registry.connect(owner).addFunds(upkeepId, toWei('100')) + await getTransmitTx(registry, keeper1, [upkeepId]) + }) + + it('deducts a cancellation fee from the upkeep and adds to reserve', async () => { + const newMinUpkeepSpend = toWei('10') + const financeAdminAddress = await financeAdmin.getAddress() + + await registry.connect(owner).setConfigTypeSafe( + signerAddresses, + keeperAddresses, + f, + { + checkGasLimit, + stalenessSeconds, + gasCeilingMultiplier, + maxCheckDataSize, + maxPerformDataSize, + maxRevertDataSize, + maxPerformGas, + fallbackGasPrice, + fallbackLinkPrice, + fallbackNativePrice, + transcoder: transcoder.address, + registrars: [], + upkeepPrivilegeManager: upkeepManager, + chainModule: moduleBase.address, + reorgProtectionEnabled: true, + financeAdmin: financeAdminAddress, + }, + offchainVersion, + offchainBytes, + [linkToken.address], + [ + { + gasFeePPB: paymentPremiumPPB, + flatFeeMilliCents, + priceFeed: linkUSDFeed.address, + fallbackPrice: fallbackLinkPrice, + minSpend: newMinUpkeepSpend, + decimals: 18, + }, + ], + ) + + const payee1Before = await linkToken.balanceOf( + await payee1.getAddress(), + ) + const upkeepBefore = (await registry.getUpkeep(upkeepId)).balance + const ownerBefore = await registry.linkAvailableForPayment() + + const amountSpent = toWei('100').sub(upkeepBefore) + const cancellationFee = newMinUpkeepSpend.sub(amountSpent) + + await registry.connect(admin).cancelUpkeep(upkeepId) + + const payee1After = await linkToken.balanceOf( + await payee1.getAddress(), + ) + const upkeepAfter = (await registry.getUpkeep(upkeepId)).balance + const ownerAfter = await registry.linkAvailableForPayment() + + // post upkeep balance should be previous balance minus cancellation fee + assert.isTrue(upkeepBefore.sub(cancellationFee).eq(upkeepAfter)) + // payee balance should not change + assert.isTrue(payee1Before.eq(payee1After)) + // owner should receive the cancellation fee + assert.isTrue(ownerAfter.sub(ownerBefore).eq(cancellationFee)) + }) + + it('deducts up to balance as cancellation fee', async () => { + // Very high min spend, should deduct whole balance as cancellation fees + const newMinUpkeepSpend = toWei('1000') + const financeAdminAddress = await financeAdmin.getAddress() + + await registry.connect(owner).setConfigTypeSafe( + signerAddresses, + keeperAddresses, + f, + { + checkGasLimit, + stalenessSeconds, + gasCeilingMultiplier, + maxCheckDataSize, + maxPerformDataSize, + maxRevertDataSize, + maxPerformGas, + fallbackGasPrice, + fallbackLinkPrice, + fallbackNativePrice, + transcoder: transcoder.address, + registrars: [], + upkeepPrivilegeManager: upkeepManager, + chainModule: moduleBase.address, + reorgProtectionEnabled: true, + financeAdmin: financeAdminAddress, + }, + offchainVersion, + offchainBytes, + [linkToken.address], + [ + { + gasFeePPB: paymentPremiumPPB, + flatFeeMilliCents, + priceFeed: linkUSDFeed.address, + fallbackPrice: fallbackLinkPrice, + minSpend: newMinUpkeepSpend, + decimals: 18, + }, + ], + ) + const payee1Before = await linkToken.balanceOf( + await payee1.getAddress(), + ) + const upkeepBefore = (await registry.getUpkeep(upkeepId)).balance + const ownerBefore = await registry.linkAvailableForPayment() + + await registry.connect(admin).cancelUpkeep(upkeepId) + const payee1After = await linkToken.balanceOf( + await payee1.getAddress(), + ) + const ownerAfter = await registry.linkAvailableForPayment() + const upkeepAfter = (await registry.getUpkeep(upkeepId)).balance + + // all upkeep balance is deducted for cancellation fee + assert.equal(upkeepAfter.toNumber(), 0) + // payee balance should not change + assert.isTrue(payee1After.eq(payee1Before)) + // all upkeep balance is transferred to the owner + assert.isTrue(ownerAfter.sub(ownerBefore).eq(upkeepBefore)) + }) + + it('does not deduct cancellation fee if more than minUpkeepSpendDollars is spent', async () => { + // Very low min spend, already spent in one perform upkeep + const newMinUpkeepSpend = BigNumber.from(420) + const financeAdminAddress = await financeAdmin.getAddress() + + await registry.connect(owner).setConfigTypeSafe( + signerAddresses, + keeperAddresses, + f, + { + checkGasLimit, + stalenessSeconds, + gasCeilingMultiplier, + maxCheckDataSize, + maxPerformDataSize, + maxRevertDataSize, + maxPerformGas, + fallbackGasPrice, + fallbackLinkPrice, + fallbackNativePrice, + transcoder: transcoder.address, + registrars: [], + upkeepPrivilegeManager: upkeepManager, + chainModule: moduleBase.address, + reorgProtectionEnabled: true, + financeAdmin: financeAdminAddress, + }, + offchainVersion, + offchainBytes, + [linkToken.address], + [ + { + gasFeePPB: paymentPremiumPPB, + flatFeeMilliCents, + priceFeed: linkUSDFeed.address, + fallbackPrice: fallbackLinkPrice, + minSpend: newMinUpkeepSpend, + decimals: 18, + }, + ], + ) + const payee1Before = await linkToken.balanceOf( + await payee1.getAddress(), + ) + const upkeepBefore = (await registry.getUpkeep(upkeepId)).balance + const ownerBefore = await registry.linkAvailableForPayment() + + await registry.connect(admin).cancelUpkeep(upkeepId) + const payee1After = await linkToken.balanceOf( + await payee1.getAddress(), + ) + const ownerAfter = await registry.linkAvailableForPayment() + const upkeepAfter = (await registry.getUpkeep(upkeepId)).balance + + // upkeep does not pay cancellation fee after cancellation because minimum upkeep spent is met + assert.isTrue(upkeepBefore.eq(upkeepAfter)) + // owner balance does not change + assert.isTrue(ownerAfter.eq(ownerBefore)) + // payee balance does not change + assert.isTrue(payee1Before.eq(payee1After)) + }) + }) + }) + }) + + describe('#withdrawPayment', () => { + beforeEach(async () => { + await linkToken.connect(owner).approve(registry.address, toWei('100')) + await registry.connect(owner).addFunds(upkeepId, toWei('100')) + await getTransmitTx(registry, keeper1, [upkeepId]) + }) + + it('reverts if called by anyone but the payee', async () => { + await evmRevertCustomError( + registry + .connect(payee2) + .withdrawPayment( + await keeper1.getAddress(), + await nonkeeper.getAddress(), + ), + registry, + 'OnlyCallableByPayee', + ) + }) + + it('reverts if called with the 0 address', async () => { + await evmRevertCustomError( + registry + .connect(payee2) + .withdrawPayment(await keeper1.getAddress(), zeroAddress), + registry, + 'InvalidRecipient', + ) + }) + + it('updates the balances', async () => { + const to = await nonkeeper.getAddress() + const keeperBefore = await registry.getTransmitterInfo( + await keeper1.getAddress(), + ) + const registrationBefore = (await registry.getUpkeep(upkeepId)).balance + const toLinkBefore = await linkToken.balanceOf(to) + const registryLinkBefore = await linkToken.balanceOf(registry.address) + const registryPremiumBefore = (await registry.getState()).state + .totalPremium + const ownerBefore = await registry.linkAvailableForPayment() + + // Withdrawing for first time, last collected = 0 + assert.equal(keeperBefore.lastCollected.toString(), '0') + + //// Do the thing + await registry + .connect(payee1) + .withdrawPayment(await keeper1.getAddress(), to) + + const keeperAfter = await registry.getTransmitterInfo( + await keeper1.getAddress(), + ) + const registrationAfter = (await registry.getUpkeep(upkeepId)).balance + const toLinkAfter = await linkToken.balanceOf(to) + const registryLinkAfter = await linkToken.balanceOf(registry.address) + const registryPremiumAfter = (await registry.getState()).state + .totalPremium + const ownerAfter = await registry.linkAvailableForPayment() + + // registry total premium should not change + assert.isTrue(registryPremiumBefore.eq(registryPremiumAfter)) + + // Last collected should be updated to premium-change + assert.isTrue( + keeperAfter.lastCollected.eq( + registryPremiumBefore.sub( + registryPremiumBefore.mod(keeperAddresses.length), + ), + ), + ) + + // owner balance should remain unchanged + assert.isTrue(ownerAfter.eq(ownerBefore)) + + assert.isTrue(keeperAfter.balance.eq(BigNumber.from(0))) + assert.isTrue(registrationBefore.eq(registrationAfter)) + assert.isTrue(toLinkBefore.add(keeperBefore.balance).eq(toLinkAfter)) + assert.isTrue( + registryLinkBefore.sub(keeperBefore.balance).eq(registryLinkAfter), + ) + }) + + it('emits a log announcing the withdrawal', async () => { + const balance = ( + await registry.getTransmitterInfo(await keeper1.getAddress()) + ).balance + const tx = await registry + .connect(payee1) + .withdrawPayment( + await keeper1.getAddress(), + await nonkeeper.getAddress(), + ) + await expect(tx) + .to.emit(registry, 'PaymentWithdrawn') + .withArgs( + await keeper1.getAddress(), + balance, + await nonkeeper.getAddress(), + await payee1.getAddress(), + ) + }) + }) + + describe('#checkCallback', () => { + it('returns false with appropriate failure reason when target callback reverts', async () => { + await streamsLookupUpkeep.setShouldRevertCallback(true) + + const values: any[] = ['0x1234', '0xabcd'] + const res = await registry + .connect(zeroAddress) + .callStatic.checkCallback(streamsLookupUpkeepId, values, '0x') + + assert.isFalse(res.upkeepNeeded) + assert.equal(res.performData, '0x') + assert.equal( + res.upkeepFailureReason, + UpkeepFailureReason.CHECK_CALLBACK_REVERTED, + ) + assert.isTrue(res.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used + }) + + it('returns false with appropriate failure reason when target callback returns big performData', async () => { + let longBytes = '0x' + for (let i = 0; i <= maxPerformDataSize.toNumber(); i++) { + longBytes += '11' + } + const values: any[] = [longBytes, longBytes] + const res = await registry + .connect(zeroAddress) + .callStatic.checkCallback(streamsLookupUpkeepId, values, '0x') + + assert.isFalse(res.upkeepNeeded) + assert.equal(res.performData, '0x') + assert.equal( + res.upkeepFailureReason, + UpkeepFailureReason.PERFORM_DATA_EXCEEDS_LIMIT, + ) + assert.isTrue(res.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used + }) + + it('returns false with appropriate failure reason when target callback returns false', async () => { + await streamsLookupUpkeep.setCallbackReturnBool(false) + const values: any[] = ['0x1234', '0xabcd'] + const res = await registry + .connect(zeroAddress) + .callStatic.checkCallback(streamsLookupUpkeepId, values, '0x') + + assert.isFalse(res.upkeepNeeded) + assert.equal(res.performData, '0x') + assert.equal( + res.upkeepFailureReason, + UpkeepFailureReason.UPKEEP_NOT_NEEDED, + ) + assert.isTrue(res.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used + }) + + it('succeeds with upkeep needed', async () => { + const values: any[] = ['0x1234', '0xabcd'] + + const res = await registry + .connect(zeroAddress) + .callStatic.checkCallback(streamsLookupUpkeepId, values, '0x') + const expectedPerformData = ethers.utils.defaultAbiCoder.encode( + ['bytes[]', 'bytes'], + [values, '0x'], + ) + + assert.isTrue(res.upkeepNeeded) + assert.equal(res.performData, expectedPerformData) + assert.equal(res.upkeepFailureReason, UpkeepFailureReason.NONE) + assert.isTrue(res.gasUsed.gt(BigNumber.from('0'))) // Some gas should be used + }) + }) + + describe('transmitterPremiumSplit [ @skip-coverage ]', () => { + beforeEach(async () => { + await linkToken.connect(owner).approve(registry.address, toWei('100')) + await registry.connect(owner).addFunds(upkeepId, toWei('100')) + }) + + it('splits premium evenly across transmitters', async () => { + // Do a transmit from keeper1 + await getTransmitTx(registry, keeper1, [upkeepId]) + + const registryPremium = (await registry.getState()).state.totalPremium + assert.isTrue(registryPremium.gt(BigNumber.from(0))) + + const premiumPerTransmitter = registryPremium.div( + BigNumber.from(keeperAddresses.length), + ) + const k1Balance = ( + await registry.getTransmitterInfo(await keeper1.getAddress()) + ).balance + // transmitter should be reimbursed for gas and get the premium + assert.isTrue(k1Balance.gt(premiumPerTransmitter)) + const k1GasReimbursement = k1Balance.sub(premiumPerTransmitter) + + const k2Balance = ( + await registry.getTransmitterInfo(await keeper2.getAddress()) + ).balance + // non transmitter should get its share of premium + assert.isTrue(k2Balance.eq(premiumPerTransmitter)) + + // Now do a transmit from keeper 2 + await getTransmitTx(registry, keeper2, [upkeepId]) + const registryPremiumNew = (await registry.getState()).state.totalPremium + assert.isTrue(registryPremiumNew.gt(registryPremium)) + const premiumPerTransmitterNew = registryPremiumNew.div( + BigNumber.from(keeperAddresses.length), + ) + const additionalPremium = premiumPerTransmitterNew.sub( + premiumPerTransmitter, + ) + + const k1BalanceNew = ( + await registry.getTransmitterInfo(await keeper1.getAddress()) + ).balance + // k1 should get the new premium + assert.isTrue( + k1BalanceNew.eq(k1GasReimbursement.add(premiumPerTransmitterNew)), + ) + + const k2BalanceNew = ( + await registry.getTransmitterInfo(await keeper2.getAddress()) + ).balance + // k2 should get gas reimbursement in addition to new premium + assert.isTrue(k2BalanceNew.gt(k2Balance.add(additionalPremium))) + }) + + it('updates last collected upon payment withdrawn', async () => { + // Do a transmit from keeper1 + await getTransmitTx(registry, keeper1, [upkeepId]) + + const registryPremium = (await registry.getState()).state.totalPremium + const k1 = await registry.getTransmitterInfo(await keeper1.getAddress()) + const k2 = await registry.getTransmitterInfo(await keeper2.getAddress()) + + // Withdrawing for first time, last collected = 0 + assert.isTrue(k1.lastCollected.eq(BigNumber.from(0))) + assert.isTrue(k2.lastCollected.eq(BigNumber.from(0))) + + //// Do the thing + await registry + .connect(payee1) + .withdrawPayment( + await keeper1.getAddress(), + await nonkeeper.getAddress(), + ) + + const k1New = await registry.getTransmitterInfo( + await keeper1.getAddress(), + ) + const k2New = await registry.getTransmitterInfo( + await keeper2.getAddress(), + ) + + // transmitter info lastCollected should be updated for k1, not for k2 + assert.isTrue( + k1New.lastCollected.eq( + registryPremium.sub(registryPremium.mod(keeperAddresses.length)), + ), + ) + assert.isTrue(k2New.lastCollected.eq(BigNumber.from(0))) + }) + + // itMaybe( + it('maintains consistent balance information across all parties', async () => { + // throughout transmits, withdrawals, setConfigs total claim on balances should remain less than expected balance + // some spare change can get lost but it should be less than maxAllowedSpareChange + + let maxAllowedSpareChange = BigNumber.from('0') + await verifyConsistentAccounting(maxAllowedSpareChange) + + await getTransmitTx(registry, keeper1, [upkeepId]) + maxAllowedSpareChange = maxAllowedSpareChange.add(BigNumber.from('31')) + await verifyConsistentAccounting(maxAllowedSpareChange) + + await registry + .connect(payee1) + .withdrawPayment( + await keeper1.getAddress(), + await nonkeeper.getAddress(), + ) + await verifyConsistentAccounting(maxAllowedSpareChange) + + await registry + .connect(payee2) + .withdrawPayment( + await keeper2.getAddress(), + await nonkeeper.getAddress(), + ) + await verifyConsistentAccounting(maxAllowedSpareChange) + + await getTransmitTx(registry, keeper1, [upkeepId]) + maxAllowedSpareChange = maxAllowedSpareChange.add(BigNumber.from('31')) + await verifyConsistentAccounting(maxAllowedSpareChange) + + await registry.connect(owner).setConfigTypeSafe( + signerAddresses.slice(2, 15), // only use 2-14th index keepers + keeperAddresses.slice(2, 15), + f, + config, + offchainVersion, + offchainBytes, + baseConfig[6], + baseConfig[7], + ) + await verifyConsistentAccounting(maxAllowedSpareChange) + + await getTransmitTx(registry, keeper3, [upkeepId], { + startingSignerIndex: 2, + }) + maxAllowedSpareChange = maxAllowedSpareChange.add(BigNumber.from('13')) + await verifyConsistentAccounting(maxAllowedSpareChange) + + await registry + .connect(payee1) + .withdrawPayment( + await keeper1.getAddress(), + await nonkeeper.getAddress(), + ) + await verifyConsistentAccounting(maxAllowedSpareChange) + + await registry + .connect(payee3) + .withdrawPayment( + await keeper3.getAddress(), + await nonkeeper.getAddress(), + ) + await verifyConsistentAccounting(maxAllowedSpareChange) + + await registry.connect(owner).setConfigTypeSafe( + signerAddresses.slice(0, 4), // only use 0-3rd index keepers + keeperAddresses.slice(0, 4), + f, + config, + offchainVersion, + offchainBytes, + baseConfig[6], + baseConfig[7], + ) + await verifyConsistentAccounting(maxAllowedSpareChange) + await getTransmitTx(registry, keeper1, [upkeepId]) + maxAllowedSpareChange = maxAllowedSpareChange.add(BigNumber.from('4')) + await getTransmitTx(registry, keeper3, [upkeepId]) + maxAllowedSpareChange = maxAllowedSpareChange.add(BigNumber.from('4')) + + await verifyConsistentAccounting(maxAllowedSpareChange) + await registry + .connect(payee5) + .withdrawPayment( + await keeper5.getAddress(), + await nonkeeper.getAddress(), + ) + await verifyConsistentAccounting(maxAllowedSpareChange) + + await registry + .connect(payee1) + .withdrawPayment( + await keeper1.getAddress(), + await nonkeeper.getAddress(), + ) + await verifyConsistentAccounting(maxAllowedSpareChange) + }) + }) +}) diff --git a/contracts/test/v0.8/automation/helpers.ts b/contracts/test/v0.8/automation/helpers.ts index 5a95fb482c..99f2cef9b8 100644 --- a/contracts/test/v0.8/automation/helpers.ts +++ b/contracts/test/v0.8/automation/helpers.ts @@ -9,6 +9,7 @@ import { IAutomationRegistryMaster__factory as IAutomationRegistryMasterFactory import { assert } from 'chai' import { FunctionFragment } from '@ethersproject/abi' import { AutomationRegistryLogicC2_3__factory as AutomationRegistryLogicC2_3Factory } from '../../../typechain/factories/AutomationRegistryLogicC2_3__factory' +import { ZKSyncAutomationRegistryLogicC2_3__factory as ZKSyncAutomationRegistryLogicC2_3Factory } from '../../../typechain/factories/ZKSyncAutomationRegistryLogicC2_3__factory' import { IAutomationRegistryMaster2_3 as IAutomationRegistry2_3 } from '../../../typechain/IAutomationRegistryMaster2_3' import { IAutomationRegistryMaster2_3__factory as IAutomationRegistryMaster2_3Factory } from '../../../typechain/factories/IAutomationRegistryMaster2_3__factory' @@ -170,10 +171,10 @@ export const deployRegistry23 = async ( link: Parameters[0], linkUSD: Parameters[1], nativeUSD: Parameters[2], - fastgas: Parameters[2], + fastgas: Parameters[3], allowedReadOnlyAddress: Parameters< AutomationRegistryLogicC2_3Factory['deploy'] - >[3], + >[5], payoutMode: Parameters[6], wrappedNativeTokenAddress: Parameters< AutomationRegistryLogicC2_3Factory['deploy'] @@ -212,3 +213,51 @@ export const deployRegistry23 = async ( const master = await registryFactory.connect(from).deploy(logicA.address) return IAutomationRegistryMaster2_3Factory.connect(master.address, from) } + +export const deployZKSyncRegistry23 = async ( + from: Signer, + link: Parameters[0], + linkUSD: Parameters[1], + nativeUSD: Parameters[2], + fastgas: Parameters[3], + allowedReadOnlyAddress: Parameters< + AutomationRegistryLogicC2_3Factory['deploy'] + >[5], + payoutMode: Parameters[6], + wrappedNativeTokenAddress: Parameters< + ZKSyncAutomationRegistryLogicC2_3Factory['deploy'] + >[7], +): Promise => { + const logicCFactory = await ethers.getContractFactory( + 'ZKSyncAutomationRegistryLogicC2_3', + ) + const logicBFactory = await ethers.getContractFactory( + 'ZKSyncAutomationRegistryLogicB2_3', + ) + const logicAFactory = await ethers.getContractFactory( + 'ZKSyncAutomationRegistryLogicA2_3', + ) + const registryFactory = await ethers.getContractFactory( + 'ZKSyncAutomationRegistry2_3', + ) + const forwarderLogicFactory = await ethers.getContractFactory( + 'AutomationForwarderLogic', + ) + const forwarderLogic = await forwarderLogicFactory.connect(from).deploy() + const logicC = await logicCFactory + .connect(from) + .deploy( + link, + linkUSD, + nativeUSD, + fastgas, + forwarderLogic.address, + allowedReadOnlyAddress, + payoutMode, + wrappedNativeTokenAddress, + ) + const logicB = await logicBFactory.connect(from).deploy(logicC.address) + const logicA = await logicAFactory.connect(from).deploy(logicB.address) + const master = await registryFactory.connect(from).deploy(logicA.address) + return IAutomationRegistryMaster2_3Factory.connect(master.address, from) +} diff --git a/core/bridges/cache.go b/core/bridges/cache.go index 4b5a655244..e97874a35e 100644 --- a/core/bridges/cache.go +++ b/core/bridges/cache.go @@ -10,11 +10,9 @@ import ( "golang.org/x/exp/maps" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" - - "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) const ( @@ -25,13 +23,11 @@ const ( type Cache struct { // dependencies and configurations ORM - lggr logger.Logger interval time.Duration // service state - services.StateMachine - wg sync.WaitGroup - chStop services.StopChan + services.Service + eng *services.Engine // data state bridgeTypesCache sync.Map @@ -43,17 +39,20 @@ var _ ORM = (*Cache)(nil) var _ services.Service = (*Cache)(nil) func NewCache(base ORM, lggr logger.Logger, upsertInterval time.Duration) *Cache { - return &Cache{ + c := &Cache{ ORM: base, - lggr: lggr.Named(CacheServiceName), interval: upsertInterval, - chStop: make(chan struct{}), bridgeLastValueCache: make(map[string]BridgeResponse), } + c.Service, c.eng = services.Config{ + Name: CacheServiceName, + Start: c.start, + }.NewServiceEngine(lggr) + return c } func (c *Cache) WithDataSource(ds sqlutil.DataSource) ORM { - return NewCache(NewORM(ds), c.lggr, c.interval) + return NewCache(NewORM(ds), c.eng, c.interval) } func (c *Cache) FindBridge(ctx context.Context, name BridgeName) (BridgeType, error) { @@ -190,51 +189,17 @@ func (c *Cache) UpsertBridgeResponse(ctx context.Context, dotId string, specId i return nil } -func (c *Cache) Start(_ context.Context) error { - return c.StartOnce(CacheServiceName, func() error { - c.wg.Add(1) - - go c.run() - - return nil - }) -} - -func (c *Cache) Close() error { - return c.StopOnce(CacheServiceName, func() error { - close(c.chStop) - c.wg.Wait() - - return nil - }) -} - -func (c *Cache) HealthReport() map[string]error { - return map[string]error{c.Name(): c.Healthy()} -} - -func (c *Cache) Name() string { - return c.lggr.Name() -} - -func (c *Cache) run() { - defer c.wg.Done() - - for { - timer := time.NewTimer(utils.WithJitter(c.interval)) +func (c *Cache) start(_ context.Context) error { + ticker := services.TickerConfig{ + Initial: c.interval, + JitterPct: services.DefaultJitter, + }.NewTicker(c.interval) + c.eng.GoTick(ticker, c.doBulkUpsert) - select { - case <-timer.C: - c.doBulkUpsert() - case <-c.chStop: - timer.Stop() - - return - } - } + return nil } -func (c *Cache) doBulkUpsert() { +func (c *Cache) doBulkUpsert(ctx context.Context) { c.mu.RLock() values := maps.Values(c.bridgeLastValueCache) c.mu.RUnlock() @@ -243,11 +208,8 @@ func (c *Cache) doBulkUpsert() { return } - ctx, cancel := c.chStop.NewCtx() - defer cancel() - if err := c.ORM.BulkUpsertBridgeResponse(ctx, values); err != nil { - c.lggr.Warnf("bulk upsert of bridge responses failed: %s", err.Error()) + c.eng.Warnf("bulk upsert of bridge responses failed: %s", err.Error()) } } diff --git a/core/capabilities/integration_tests/keystone_contracts_setup.go b/core/capabilities/integration_tests/keystone_contracts_setup.go index 42269d1bd4..fbd3da5cc5 100644 --- a/core/capabilities/integration_tests/keystone_contracts_setup.go +++ b/core/capabilities/integration_tests/keystone_contracts_setup.go @@ -26,7 +26,6 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" "github.com/smartcontractkit/chainlink-common/pkg/values" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" @@ -40,6 +39,13 @@ import ( kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" ) +const ( + CapabilityTypeTrigger = 0 + CapabilityTypeAction = 1 + CapabilityTypeConsensus = 2 + CapabilityTypeTarget = 3 +) + type peer struct { PeerID string Signer string @@ -91,8 +97,8 @@ func peerToNode(nopID uint32, p peer) (kcr.CapabilitiesRegistryNodeParams, error }, nil } -func setupCapabilitiesRegistryContract(ctx context.Context, t *testing.T, workflowDonPeers []peer, triggerDonPeers []peer, - targetDonPeerIDs []peer, +func setupCapabilitiesRegistryContract(ctx context.Context, t *testing.T, workflowDon donInfo, triggerDon donInfo, + targetDon donInfo, transactOpts *bind.TransactOpts, backend *ethBackend) common.Address { addr, _, reg, err := kcr.DeployCapabilitiesRegistry(transactOpts, backend) require.NoError(t, err) @@ -102,15 +108,16 @@ func setupCapabilitiesRegistryContract(ctx context.Context, t *testing.T, workfl streamsTrigger := kcr.CapabilitiesRegistryCapability{ LabelledName: "streams-trigger", Version: "1.0.0", - CapabilityType: uint8(capabilities.CapabilityTypeTrigger), + CapabilityType: CapabilityTypeTrigger, } sid, err := reg.GetHashedCapabilityId(&bind.CallOpts{}, streamsTrigger.LabelledName, streamsTrigger.Version) require.NoError(t, err) writeChain := kcr.CapabilitiesRegistryCapability{ - LabelledName: "write_geth-testnet", - Version: "1.0.0", - CapabilityType: uint8(capabilities.CapabilityTypeTarget), + LabelledName: "write_geth-testnet", + Version: "1.0.0", + + CapabilityType: CapabilityTypeTarget, } wid, err := reg.GetHashedCapabilityId(&bind.CallOpts{}, writeChain.LabelledName, writeChain.Version) if err != nil { @@ -120,7 +127,7 @@ func setupCapabilitiesRegistryContract(ctx context.Context, t *testing.T, workfl ocr := kcr.CapabilitiesRegistryCapability{ LabelledName: "offchain_reporting", Version: "1.0.0", - CapabilityType: uint8(capabilities.CapabilityTypeConsensus), + CapabilityType: CapabilityTypeConsensus, } ocrid, err := reg.GetHashedCapabilityId(&bind.CallOpts{}, ocr.LabelledName, ocr.Version) require.NoError(t, err) @@ -157,7 +164,7 @@ func setupCapabilitiesRegistryContract(ctx context.Context, t *testing.T, workfl nopID := recLog.NodeOperatorId nodes := []kcr.CapabilitiesRegistryNodeParams{} - for _, wfPeer := range workflowDonPeers { + for _, wfPeer := range workflowDon.peerIDs { n, innerErr := peerToNode(nopID, wfPeer) require.NoError(t, innerErr) @@ -165,7 +172,7 @@ func setupCapabilitiesRegistryContract(ctx context.Context, t *testing.T, workfl nodes = append(nodes, n) } - for _, triggerPeer := range triggerDonPeers { + for _, triggerPeer := range triggerDon.peerIDs { n, innerErr := peerToNode(nopID, triggerPeer) require.NoError(t, innerErr) @@ -173,7 +180,7 @@ func setupCapabilitiesRegistryContract(ctx context.Context, t *testing.T, workfl nodes = append(nodes, n) } - for _, targetPeer := range targetDonPeerIDs { + for _, targetPeer := range targetDon.peerIDs { n, innerErr := peerToNode(nopID, targetPeer) require.NoError(t, innerErr) @@ -185,7 +192,7 @@ func setupCapabilitiesRegistryContract(ctx context.Context, t *testing.T, workfl require.NoError(t, err) // workflow DON - ps, err := peers(workflowDonPeers) + ps, err := peers(workflowDon.peerIDs) require.NoError(t, err) cc := newCapabilityConfig() @@ -199,22 +206,24 @@ func setupCapabilitiesRegistryContract(ctx context.Context, t *testing.T, workfl }, } - workflowDonF := uint8(2) - _, err = reg.AddDON(transactOpts, ps, cfgs, false, true, workflowDonF) + _, err = reg.AddDON(transactOpts, ps, cfgs, false, true, workflowDon.F) require.NoError(t, err) // trigger DON - ps, err = peers(triggerDonPeers) + ps, err = peers(triggerDon.peerIDs) require.NoError(t, err) - triggerDonF := 1 - config := &pb.RemoteTriggerConfig{ - RegistrationRefresh: durationpb.New(20000 * time.Millisecond), - RegistrationExpiry: durationpb.New(60000 * time.Millisecond), - // F + 1 - MinResponsesToAggregate: uint32(triggerDonF) + 1, + triggerCapabilityConfig := newCapabilityConfig() + triggerCapabilityConfig.RemoteConfig = &pb.CapabilityConfig_RemoteTriggerConfig{ + RemoteTriggerConfig: &pb.RemoteTriggerConfig{ + RegistrationRefresh: durationpb.New(1000 * time.Millisecond), + RegistrationExpiry: durationpb.New(60000 * time.Millisecond), + // F + 1 + MinResponsesToAggregate: uint32(triggerDon.F) + 1, + }, } - configb, err := proto.Marshal(config) + + configb, err := proto.Marshal(triggerCapabilityConfig) require.NoError(t, err) cfgs = []kcr.CapabilitiesRegistryCapabilityConfiguration{ @@ -224,22 +233,37 @@ func setupCapabilitiesRegistryContract(ctx context.Context, t *testing.T, workfl }, } - _, err = reg.AddDON(transactOpts, ps, cfgs, true, false, uint8(triggerDonF)) + _, err = reg.AddDON(transactOpts, ps, cfgs, true, false, triggerDon.F) require.NoError(t, err) // target DON - ps, err = peers(targetDonPeerIDs) + ps, err = peers(targetDon.peerIDs) + require.NoError(t, err) + + targetCapabilityConfig := newCapabilityConfig() + + configWithLimit, err := values.WrapMap(map[string]any{"gasLimit": 500000}) + require.NoError(t, err) + + targetCapabilityConfig.DefaultConfig = values.Proto(configWithLimit).GetMapValue() + + targetCapabilityConfig.RemoteConfig = &pb.CapabilityConfig_RemoteTargetConfig{ + RemoteTargetConfig: &pb.RemoteTargetConfig{ + RequestHashExcludedAttributes: []string{"signed_report.Signatures"}, + }, + } + + remoteTargetConfigBytes, err := proto.Marshal(targetCapabilityConfig) require.NoError(t, err) cfgs = []kcr.CapabilitiesRegistryCapabilityConfiguration{ { CapabilityId: wid, - Config: ccb, + Config: remoteTargetConfigBytes, }, } - targetDonF := uint8(1) - _, err = reg.AddDON(transactOpts, ps, cfgs, true, false, targetDonF) + _, err = reg.AddDON(transactOpts, ps, cfgs, true, false, targetDon.F) require.NoError(t, err) backend.Commit() @@ -253,19 +277,18 @@ func newCapabilityConfig() *pb.CapabilityConfig { } } -func setupForwarderContract(t *testing.T, workflowDonPeers []peer, workflowDonId uint32, - configVersion uint32, f uint8, +func setupForwarderContract(t *testing.T, workflowDon donInfo, transactOpts *bind.TransactOpts, backend *ethBackend) (common.Address, *forwarder.KeystoneForwarder) { addr, _, fwd, err := forwarder.DeployKeystoneForwarder(transactOpts, backend) require.NoError(t, err) backend.Commit() var signers []common.Address - for _, p := range workflowDonPeers { + for _, p := range workflowDon.peerIDs { signers = append(signers, common.HexToAddress(p.Signer)) } - _, err = fwd.SetConfig(transactOpts, workflowDonId, configVersion, f, signers) + _, err = fwd.SetConfig(transactOpts, workflowDon.ID, workflowDon.ConfigVersion, workflowDon.F, signers) require.NoError(t, err) backend.Commit() diff --git a/core/capabilities/integration_tests/mock_dispatcher.go b/core/capabilities/integration_tests/mock_dispatcher.go index f685f0ad2e..1230e59427 100644 --- a/core/capabilities/integration_tests/mock_dispatcher.go +++ b/core/capabilities/integration_tests/mock_dispatcher.go @@ -9,6 +9,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote" remotetypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/types" p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" @@ -58,6 +59,7 @@ func (a *testAsyncMessageBroker) NewDispatcherForNode(nodePeerID p2ptypes.PeerID return &brokerDispatcher{ callerPeerID: nodePeerID, broker: a, + receivers: map[key]remotetypes.Receiver{}, } } @@ -158,6 +160,14 @@ type broker interface { type brokerDispatcher struct { callerPeerID p2ptypes.PeerID broker broker + + receivers map[key]remotetypes.Receiver + mu sync.Mutex +} + +type key struct { + capId string + donId uint32 } func (t *brokerDispatcher) Send(peerID p2ptypes.PeerID, msgBody *remotetypes.MessageBody) error { @@ -171,6 +181,15 @@ func (t *brokerDispatcher) Send(peerID p2ptypes.PeerID, msgBody *remotetypes.Mes } func (t *brokerDispatcher) SetReceiver(capabilityId string, donId uint32, receiver remotetypes.Receiver) error { + t.mu.Lock() + defer t.mu.Unlock() + k := key{capabilityId, donId} + _, ok := t.receivers[k] + if ok { + return fmt.Errorf("%w: receiver already exists for capability %s and don %d", remote.ErrReceiverExists, capabilityId, donId) + } + t.receivers[k] = receiver + t.broker.(*testAsyncMessageBroker).registerReceiverNode(t.callerPeerID, capabilityId, donId, receiver) return nil } diff --git a/core/capabilities/integration_tests/mock_libocr.go b/core/capabilities/integration_tests/mock_libocr.go index 39c53d48af..14ccdce600 100644 --- a/core/capabilities/integration_tests/mock_libocr.go +++ b/core/capabilities/integration_tests/mock_libocr.go @@ -157,10 +157,6 @@ func (m *mockLibOCR) simulateProtocolRound(ctx context.Context) error { Signer: commontypes.OracleID(i), Signature: sig, }) - - if uint8(len(signatures)) == m.f+1 { - break - } } for _, node := range m.nodes { @@ -181,7 +177,16 @@ func (m *mockLibOCR) simulateProtocolRound(ctx context.Context) error { continue } - err = node.Transmit(ctx, types.ConfigDigest{}, 0, report, signatures) + // For each node select a random set of f+1 signatures to mimic libocr behaviour + s := rand.NewSource(time.Now().UnixNano()) + r := rand.New(s) + indices := r.Perm(len(signatures)) + selectedSignatures := make([]types.AttributedOnchainSignature, m.f+1) + for i := 0; i < int(m.f+1); i++ { + selectedSignatures[i] = signatures[indices[i]] + } + + err = node.Transmit(ctx, types.ConfigDigest{}, 0, report, selectedSignatures) if err != nil { return fmt.Errorf("failed to transmit report: %w", err) } diff --git a/core/capabilities/integration_tests/mock_trigger.go b/core/capabilities/integration_tests/mock_trigger.go index cb673f54ff..35b05b054c 100644 --- a/core/capabilities/integration_tests/mock_trigger.go +++ b/core/capabilities/integration_tests/mock_trigger.go @@ -2,8 +2,6 @@ package integration_tests import ( "context" - "fmt" - "strconv" "sync" "testing" @@ -45,7 +43,7 @@ func (r *reportsSink) Close() error { func (r *reportsSink) sendReports(reportList []*datastreams.FeedReport) { for _, trigger := range r.triggers { - resp, err := wrapReports(reportList, "1", 12, datastreams.SignersMetadata{}) + resp, err := wrapReports(reportList, "1", 12, datastreams.Metadata{}) if err != nil { panic(err) } @@ -54,7 +52,7 @@ func (r *reportsSink) sendReports(reportList []*datastreams.FeedReport) { } func (r *reportsSink) getNewTrigger(t *testing.T) *streamsTrigger { - trigger := streamsTrigger{t: t, toSend: make(chan capabilities.CapabilityResponse, 1000), + trigger := streamsTrigger{t: t, toSend: make(chan capabilities.TriggerResponse, 1000), wg: &r.wg, stopCh: r.stopCh} r.triggers = append(r.triggers, trigger) return &trigger @@ -63,13 +61,13 @@ func (r *reportsSink) getNewTrigger(t *testing.T) *streamsTrigger { type streamsTrigger struct { t *testing.T cancel context.CancelFunc - toSend chan capabilities.CapabilityResponse + toSend chan capabilities.TriggerResponse wg *sync.WaitGroup stopCh services.StopChan } -func (s *streamsTrigger) sendResponse(resp capabilities.CapabilityResponse) { +func (s *streamsTrigger) sendResponse(resp capabilities.TriggerResponse) { s.toSend <- resp } @@ -81,32 +79,34 @@ func (s *streamsTrigger) Info(ctx context.Context) (capabilities.CapabilityInfo, ), nil } -func (s *streamsTrigger) RegisterTrigger(ctx context.Context, request capabilities.CapabilityRequest) (<-chan capabilities.CapabilityResponse, error) { +func (s *streamsTrigger) RegisterTrigger(ctx context.Context, request capabilities.TriggerRegistrationRequest) (<-chan capabilities.TriggerResponse, error) { if s.cancel != nil { s.t.Fatal("trigger already registered") } - responseCh := make(chan capabilities.CapabilityResponse) + responseCh := make(chan capabilities.TriggerResponse) - ctxWithCancel, cancel := context.WithCancel(ctx) + ctxWithCancel, cancel := context.WithCancel(context.Background()) s.cancel = cancel s.wg.Add(1) go func() { defer s.wg.Done() - select { - case <-s.stopCh: - return - case <-ctxWithCancel.Done(): - return - case resp := <-s.toSend: - responseCh <- resp + for { + select { + case <-s.stopCh: + return + case <-ctxWithCancel.Done(): + return + case resp := <-s.toSend: + responseCh <- resp + } } }() return responseCh, nil } -func (s *streamsTrigger) UnregisterTrigger(ctx context.Context, request capabilities.CapabilityRequest) error { +func (s *streamsTrigger) UnregisterTrigger(ctx context.Context, request capabilities.TriggerRegistrationRequest) error { if s.cancel == nil { s.t.Fatal("trigger not registered") } @@ -116,32 +116,28 @@ func (s *streamsTrigger) UnregisterTrigger(ctx context.Context, request capabili return nil } -func wrapReports(reportList []*datastreams.FeedReport, eventID string, timestamp int64, meta datastreams.SignersMetadata) (capabilities.CapabilityResponse, error) { - val, err := values.Wrap(reportList) - if err != nil { - return capabilities.CapabilityResponse{}, err +func wrapReports(reportList []*datastreams.FeedReport, eventID string, timestamp int64, meta datastreams.Metadata) (capabilities.TriggerResponse, error) { + rl := []datastreams.FeedReport{} + for _, r := range reportList { + rl = append(rl, *r) } - - metaVal, err := values.Wrap(meta) + outputs, err := values.WrapMap(datastreams.StreamsTriggerEvent{ + Payload: rl, + Metadata: meta, + Timestamp: timestamp, + }) if err != nil { - return capabilities.CapabilityResponse{}, err + return capabilities.TriggerResponse{}, err } triggerEvent := capabilities.TriggerEvent{ TriggerType: triggerID, ID: eventID, - Timestamp: strconv.FormatInt(timestamp, 10), - Metadata: metaVal, - Payload: val, - } - - triggerEventMapValue, err := values.WrapMap(triggerEvent) - if err != nil { - return capabilities.CapabilityResponse{}, fmt.Errorf("failed to wrap trigger event: %w", err) + Outputs: outputs, } - // Create a new CapabilityResponse with the MercuryTriggerEvent - return capabilities.CapabilityResponse{ - Value: triggerEventMapValue, + // Create a new TriggerResponse with the MercuryTriggerEvent + return capabilities.TriggerResponse{ + Event: triggerEvent, }, nil } diff --git a/core/capabilities/integration_tests/setup.go b/core/capabilities/integration_tests/setup.go index 6347d55878..8e31ce5ba5 100644 --- a/core/capabilities/integration_tests/setup.go +++ b/core/capabilities/integration_tests/setup.go @@ -69,8 +69,8 @@ func setupStreamDonsWithTransmissionSchedule(ctx context.Context, t *testing.T, lggr.SetLogLevel(TestLogLevel) ethBlockchain, transactor := setupBlockchain(t, 1000, 1*time.Second) - capabilitiesRegistryAddr := setupCapabilitiesRegistryContract(ctx, t, workflowDonInfo.peerIDs, triggerDonInfo.peerIDs, targetDonInfo.peerIDs, transactor, ethBlockchain) - forwarderAddr, _ := setupForwarderContract(t, workflowDonInfo.peerIDs, workflowDonInfo.ID, 1, workflowDonInfo.F, transactor, ethBlockchain) + capabilitiesRegistryAddr := setupCapabilitiesRegistryContract(ctx, t, workflowDonInfo, triggerDonInfo, targetDonInfo, transactor, ethBlockchain) + forwarderAddr, _ := setupForwarderContract(t, workflowDonInfo, transactor, ethBlockchain) consumerAddr, consumer := setupConsumerContract(t, transactor, ethBlockchain, forwarderAddr, workflowOwnerID, workflowName) var feedIDs []string @@ -260,9 +260,10 @@ func createDonInfo(t *testing.T, don don) donInfo { triggerDonInfo := donInfo{ DON: commoncap.DON{ - ID: don.id, - Members: donPeers, - F: don.f, + ID: don.id, + Members: donPeers, + F: don.f, + ConfigVersion: 1, }, peerIDs: peerIDs, keys: donKeys, diff --git a/core/capabilities/integration_tests/streams_test.go b/core/capabilities/integration_tests/streams_test.go index 6216e36c85..8c8f51914c 100644 --- a/core/capabilities/integration_tests/streams_test.go +++ b/core/capabilities/integration_tests/streams_test.go @@ -22,8 +22,8 @@ func Test_AllAtOnceTransmissionSchedule(t *testing.T) { // The don IDs set in the below calls are inferred from the order in which the dons are added to the capabilities registry // in the setupCapabilitiesRegistryContract function, should this order change the don IDs will need updating. - workflowDonInfo := createDonInfo(t, don{id: 1, numNodes: 5, f: 1}) - triggerDonInfo := createDonInfo(t, don{id: 2, numNodes: 7, f: 1}) + workflowDonInfo := createDonInfo(t, don{id: 1, numNodes: 7, f: 2}) + triggerDonInfo := createDonInfo(t, don{id: 2, numNodes: 7, f: 2}) targetDonInfo := createDonInfo(t, don{id: 3, numNodes: 4, f: 1}) consumer, feedIDs, triggerSink := setupStreamDonsWithTransmissionSchedule(ctx, t, workflowDonInfo, triggerDonInfo, targetDonInfo, 3, @@ -45,8 +45,8 @@ func Test_OneAtATimeTransmissionSchedule(t *testing.T) { // The don IDs set in the below calls are inferred from the order in which the dons are added to the capabilities registry // in the setupCapabilitiesRegistryContract function, should this order change the don IDs will need updating. - workflowDonInfo := createDonInfo(t, don{id: 1, numNodes: 5, f: 1}) - triggerDonInfo := createDonInfo(t, don{id: 2, numNodes: 7, f: 1}) + workflowDonInfo := createDonInfo(t, don{id: 1, numNodes: 7, f: 2}) + triggerDonInfo := createDonInfo(t, don{id: 2, numNodes: 7, f: 2}) targetDonInfo := createDonInfo(t, don{id: 3, numNodes: 4, f: 1}) consumer, feedIDs, triggerSink := setupStreamDonsWithTransmissionSchedule(ctx, t, workflowDonInfo, triggerDonInfo, targetDonInfo, 3, diff --git a/core/capabilities/launcher.go b/core/capabilities/launcher.go index b4ade04127..03a1dd54f0 100644 --- a/core/capabilities/launcher.go +++ b/core/capabilities/launcher.go @@ -4,16 +4,21 @@ import ( "context" "errors" "fmt" + "slices" "strings" "time" + "google.golang.org/protobuf/proto" + "github.com/smartcontractkit/chainlink-common/pkg/capabilities" "github.com/smartcontractkit/chainlink-common/pkg/capabilities/triggers" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/values" "github.com/smartcontractkit/libocr/ragep2p" ragetypes "github.com/smartcontractkit/libocr/ragep2p/types" + capabilitiespb "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote" "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/target" remotetypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/types" @@ -24,16 +29,16 @@ import ( ) var defaultStreamConfig = p2ptypes.StreamConfig{ - IncomingMessageBufferSize: 1000000, - OutgoingMessageBufferSize: 1000000, - MaxMessageLenBytes: 100000, + IncomingMessageBufferSize: 500, + OutgoingMessageBufferSize: 500, + MaxMessageLenBytes: 500000, // 500 KB; max capacity = 500 * 500000 = 250 MB MessageRateLimiter: ragep2p.TokenBucketParams{ Rate: 100.0, - Capacity: 1000, + Capacity: 500, }, BytesRateLimiter: ragep2p.TokenBucketParams{ - Rate: 100000.0, - Capacity: 1000000, + Rate: 5000000.0, // 5 MB/s + Capacity: 10000000, // 10 MB }, } @@ -46,6 +51,42 @@ type launcher struct { subServices []services.Service } +func unmarshalCapabilityConfig(data []byte) (capabilities.CapabilityConfiguration, error) { + cconf := &capabilitiespb.CapabilityConfig{} + err := proto.Unmarshal(data, cconf) + if err != nil { + return capabilities.CapabilityConfiguration{}, err + } + + var remoteTriggerConfig *capabilities.RemoteTriggerConfig + var remoteTargetConfig *capabilities.RemoteTargetConfig + + switch cconf.GetRemoteConfig().(type) { + case *capabilitiespb.CapabilityConfig_RemoteTriggerConfig: + prtc := cconf.GetRemoteTriggerConfig() + remoteTriggerConfig = &capabilities.RemoteTriggerConfig{} + remoteTriggerConfig.RegistrationRefresh = prtc.RegistrationRefresh.AsDuration() + remoteTriggerConfig.RegistrationExpiry = prtc.RegistrationExpiry.AsDuration() + remoteTriggerConfig.MinResponsesToAggregate = prtc.MinResponsesToAggregate + remoteTriggerConfig.MessageExpiry = prtc.MessageExpiry.AsDuration() + case *capabilitiespb.CapabilityConfig_RemoteTargetConfig: + prtc := cconf.GetRemoteTargetConfig() + remoteTargetConfig = &capabilities.RemoteTargetConfig{} + remoteTargetConfig.RequestHashExcludedAttributes = prtc.RequestHashExcludedAttributes + } + + dc, err := values.FromMapValueProto(cconf.DefaultConfig) + if err != nil { + return capabilities.CapabilityConfiguration{}, err + } + + return capabilities.CapabilityConfiguration{ + DefaultConfig: dc, + RemoteTriggerConfig: remoteTriggerConfig, + RemoteTargetConfig: remoteTargetConfig, + }, nil +} + func NewLauncher( lggr logger.Logger, peerWrapper p2ptypes.PeerWrapper, @@ -90,6 +131,12 @@ func (w *launcher) Name() string { func (w *launcher) Launch(ctx context.Context, state *registrysyncer.LocalRegistry) error { w.registry.SetLocalRegistry(state) + allDONIDs := []registrysyncer.DonID{} + for id := range state.IDsToDONs { + allDONIDs = append(allDONIDs, id) + } + slices.Sort(allDONIDs) // ensure deterministic order + // Let's start by updating the list of Peers // We do this by creating a new entry for each node belonging // to a public DON. @@ -97,7 +144,8 @@ func (w *launcher) Launch(ctx context.Context, state *registrysyncer.LocalRegist allPeers := make(map[ragetypes.PeerID]p2ptypes.StreamConfig) publicDONs := []registrysyncer.DON{} - for _, d := range state.IDsToDONs { + for _, id := range allDONIDs { + d := state.IDsToDONs[id] if !d.DON.IsPublic { continue } @@ -127,7 +175,8 @@ func (w *launcher) Launch(ctx context.Context, state *registrysyncer.LocalRegist myWorkflowDONs := []registrysyncer.DON{} remoteWorkflowDONs := []registrysyncer.DON{} myDONs := map[uint32]bool{} - for _, d := range state.IDsToDONs { + for _, id := range allDONIDs { + d := state.IDsToDONs[id] for _, peerID := range d.Members { if peerID == myID { myDONs[d.ID] = true @@ -164,7 +213,7 @@ func (w *launcher) Launch(ctx context.Context, state *registrysyncer.LocalRegist // NOTE: this is enforced on-chain and so should never happen. if len(myWorkflowDONs) > 1 { - w.lggr.Error("invariant violation: node is part of more than one workflowDON: this shouldn't happen.") + return errors.New("invariant violation: node is part of more than one workflowDON") } for _, rcd := range remoteCapabilityDONs { @@ -196,6 +245,11 @@ func (w *launcher) addRemoteCapabilities(ctx context.Context, myDON registrysync return fmt.Errorf("could not find capability matching id %s", cid) } + capabilityConfig, err := unmarshalCapabilityConfig(c.Config) + if err != nil { + return fmt.Errorf("could not unmarshal capability config for id %s", cid) + } + switch capability.CapabilityType { case capabilities.CapabilityTypeTrigger: newTriggerFn := func(info capabilities.CapabilityInfo) (capabilityService, error) { @@ -216,6 +270,7 @@ func (w *launcher) addRemoteCapabilities(ctx context.Context, myDON registrysync int(remoteDON.F+1), w.lggr, ) + // TODO: We need to implement a custom, Mercury-specific // aggregator here, because there is no guarantee that // all trigger events in the workflow will have the same @@ -223,7 +278,7 @@ func (w *launcher) addRemoteCapabilities(ctx context.Context, myDON registrysync // When this is solved, we can move to a generic aggregator // and remove this. triggerCap := remote.NewTriggerSubscriber( - c.RemoteTriggerConfig, + capabilityConfig.RemoteTriggerConfig, info, remoteDON.DON, myDON.DON, @@ -332,11 +387,16 @@ func (w *launcher) exposeCapabilities(ctx context.Context, myPeerID p2ptypes.Pee return fmt.Errorf("could not find capability matching id %s", cid) } + capabilityConfig, err := unmarshalCapabilityConfig(c.Config) + if err != nil { + return fmt.Errorf("could not unmarshal capability config for id %s", cid) + } + switch capability.CapabilityType { case capabilities.CapabilityTypeTrigger: newTriggerPublisher := func(capability capabilities.BaseCapability, info capabilities.CapabilityInfo) (receiverService, error) { publisher := remote.NewTriggerPublisher( - c.RemoteTriggerConfig, + capabilityConfig.RemoteTriggerConfig, capability.(capabilities.TriggerCapability), info, don.DON, @@ -358,6 +418,7 @@ func (w *launcher) exposeCapabilities(ctx context.Context, myPeerID p2ptypes.Pee case capabilities.CapabilityTypeTarget: newTargetServer := func(capability capabilities.BaseCapability, info capabilities.CapabilityInfo) (receiverService, error) { return target.NewServer( + capabilityConfig.RemoteTargetConfig, myPeerID, capability.(capabilities.TargetCapability), info, diff --git a/core/capabilities/launcher_test.go b/core/capabilities/launcher_test.go index fb3e6837d0..220a7838db 100644 --- a/core/capabilities/launcher_test.go +++ b/core/capabilities/launcher_test.go @@ -4,14 +4,18 @@ import ( "context" "crypto/rand" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/durationpb" ragetypes "github.com/smartcontractkit/libocr/ragep2p/types" "github.com/smartcontractkit/chainlink-common/pkg/capabilities" + capabilitiespb "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote" remoteMocks "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/types/mocks" @@ -26,11 +30,11 @@ type mockTrigger struct { capabilities.CapabilityInfo } -func (m *mockTrigger) RegisterTrigger(ctx context.Context, request capabilities.CapabilityRequest) (<-chan capabilities.CapabilityResponse, error) { +func (m *mockTrigger) RegisterTrigger(ctx context.Context, request capabilities.TriggerRegistrationRequest) (<-chan capabilities.TriggerResponse, error) { return nil, nil } -func (m *mockTrigger) UnregisterTrigger(ctx context.Context, request capabilities.CapabilityRequest) error { +func (m *mockTrigger) UnregisterTrigger(ctx context.Context, request capabilities.TriggerRegistrationRequest) error { return nil } @@ -121,7 +125,7 @@ func TestLauncher_WiresUpExternalCapabilities(t *testing.T) { AcceptsWorkflows: true, Members: nodes, }, - CapabilityConfigurations: map[string]capabilities.CapabilityConfiguration{ + CapabilityConfigurations: map[string]registrysyncer.CapabilityConfiguration{ fullTriggerCapID: {}, fullTargetID: {}, }, @@ -223,7 +227,7 @@ func TestSyncer_IgnoresCapabilitiesForPrivateDON(t *testing.T) { AcceptsWorkflows: true, Members: nodes, }, - CapabilityConfigurations: map[string]capabilities.CapabilityConfiguration{ + CapabilityConfigurations: map[string]registrysyncer.CapabilityConfiguration{ triggerID: {}, targetID: {}, }, @@ -323,9 +327,18 @@ func TestLauncher_WiresUpClientsForPublicWorkflowDON(t *testing.T) { // The below state describes a Workflow DON (AcceptsWorkflows = true), // which exposes the streams-trigger and write_chain capabilities. // We expect receivers to be wired up and both capabilities to be added to the registry. - var rtc capabilities.RemoteTriggerConfig + rtc := &capabilities.RemoteTriggerConfig{} rtc.ApplyDefaults() + cfg, err := proto.Marshal(&capabilitiespb.CapabilityConfig{ + RemoteConfig: &capabilitiespb.CapabilityConfig_RemoteTriggerConfig{ + RemoteTriggerConfig: &capabilitiespb.RemoteTriggerConfig{ + RegistrationRefresh: durationpb.New(1 * time.Second), + }, + }, + }) + require.NoError(t, err) + state := ®istrysyncer.LocalRegistry{ IDsToDONs: map[registrysyncer.DonID]registrysyncer.DON{ registrysyncer.DonID(dID): { @@ -347,12 +360,12 @@ func TestLauncher_WiresUpClientsForPublicWorkflowDON(t *testing.T) { AcceptsWorkflows: false, Members: capabilityDonNodes, }, - CapabilityConfigurations: map[string]capabilities.CapabilityConfiguration{ + CapabilityConfigurations: map[string]registrysyncer.CapabilityConfiguration{ fullTriggerCapID: { - RemoteTriggerConfig: rtc, + Config: cfg, }, fullTargetID: { - RemoteTriggerConfig: rtc, + Config: cfg, }, }, }, @@ -496,7 +509,7 @@ func TestLauncher_WiresUpClientsForPublicWorkflowDONButIgnoresPrivateCapabilitie AcceptsWorkflows: false, Members: capabilityDonNodes, }, - CapabilityConfigurations: map[string]capabilities.CapabilityConfiguration{ + CapabilityConfigurations: map[string]registrysyncer.CapabilityConfiguration{ fullTriggerCapID: {}, }, }, @@ -509,7 +522,7 @@ func TestLauncher_WiresUpClientsForPublicWorkflowDONButIgnoresPrivateCapabilitie AcceptsWorkflows: false, Members: capabilityDonNodes, }, - CapabilityConfigurations: map[string]capabilities.CapabilityConfiguration{ + CapabilityConfigurations: map[string]registrysyncer.CapabilityConfiguration{ fullTargetID: {}, }, }, @@ -653,7 +666,7 @@ func TestLauncher_SucceedsEvenIfDispatcherAlreadyHasReceiver(t *testing.T) { AcceptsWorkflows: false, Members: capabilityDonNodes, }, - CapabilityConfigurations: map[string]capabilities.CapabilityConfiguration{ + CapabilityConfigurations: map[string]registrysyncer.CapabilityConfiguration{ fullTriggerCapID: {}, }, }, diff --git a/core/capabilities/registry.go b/core/capabilities/registry.go index 8a99450c09..4728550580 100644 --- a/core/capabilities/registry.go +++ b/core/capabilities/registry.go @@ -8,6 +8,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/capabilities" "github.com/smartcontractkit/chainlink/v2/core/logger" + p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" + "github.com/smartcontractkit/chainlink/v2/core/services/registrysyncer" ) var ( @@ -16,7 +18,7 @@ var ( type metadataRegistry interface { LocalNode(ctx context.Context) (capabilities.Node, error) - ConfigForCapability(ctx context.Context, capabilityID string, donID uint32) (capabilities.CapabilityConfiguration, error) + ConfigForCapability(ctx context.Context, capabilityID string, donID uint32) (registrysyncer.CapabilityConfiguration, error) } // Registry is a struct for the registry of capabilities. @@ -37,11 +39,18 @@ func (r *Registry) LocalNode(ctx context.Context) (capabilities.Node, error) { } func (r *Registry) ConfigForCapability(ctx context.Context, capabilityID string, donID uint32) (capabilities.CapabilityConfiguration, error) { + r.mu.RLock() + defer r.mu.RUnlock() if r.metadataRegistry == nil { return capabilities.CapabilityConfiguration{}, errors.New("metadataRegistry information not available") } - return r.metadataRegistry.ConfigForCapability(ctx, capabilityID, donID) + cfc, err := r.metadataRegistry.ConfigForCapability(ctx, capabilityID, donID) + if err != nil { + return capabilities.CapabilityConfiguration{}, err + } + + return unmarshalCapabilityConfig(cfc.Config) } // SetLocalRegistry sets a local copy of the offchain registry for the registry to use. @@ -191,3 +200,30 @@ func NewRegistry(lggr logger.Logger) *Registry { lggr: lggr.Named("CapabilitiesRegistry"), } } + +// TestMetadataRegistry is a test implementation of the metadataRegistry +// interface. It is used when ExternalCapabilitiesRegistry is not available. +type TestMetadataRegistry struct{} + +func (t *TestMetadataRegistry) LocalNode(ctx context.Context) (capabilities.Node, error) { + peerID := p2ptypes.PeerID{} + workflowDON := capabilities.DON{ + ID: 1, + ConfigVersion: 1, + Members: []p2ptypes.PeerID{ + peerID, + }, + F: 0, + IsPublic: false, + AcceptsWorkflows: true, + } + return capabilities.Node{ + PeerID: &peerID, + WorkflowDON: workflowDON, + CapabilityDONs: []capabilities.DON{}, + }, nil +} + +func (t *TestMetadataRegistry) ConfigForCapability(ctx context.Context, capabilityID string, donID uint32) (registrysyncer.CapabilityConfiguration, error) { + return registrysyncer.CapabilityConfiguration{}, nil +} diff --git a/core/capabilities/remote/dispatcher.go b/core/capabilities/remote/dispatcher.go index c1ee5db294..bed485c286 100644 --- a/core/capabilities/remote/dispatcher.go +++ b/core/capabilities/remote/dispatcher.go @@ -13,7 +13,6 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/types/core" - "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/types" remotetypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/types" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -180,7 +179,7 @@ func (d *dispatcher) receive() { receiver, ok := d.receivers[k] d.mu.RUnlock() if !ok { - d.lggr.Debugw("received message for unregistered capability", "capabilityId", k.capId, "donId", k.donId) + d.lggr.Debugw("received message for unregistered capability", "capabilityId", SanitizeLogString(k.capId), "donId", k.donId) d.tryRespondWithError(msg.Sender, body, types.Error_CAPABILITY_NOT_FOUND) continue } diff --git a/core/capabilities/remote/message_cache.go b/core/capabilities/remote/message_cache.go index 27f909c516..f3a3a79b2c 100644 --- a/core/capabilities/remote/message_cache.go +++ b/core/capabilities/remote/message_cache.go @@ -60,12 +60,12 @@ func (c *messageCache[EventID, PeerID]) Ready(eventID EventID, minCount uint32, if msg.timestamp >= minTimestamp { countAboveMinTimestamp++ accPayloads = append(accPayloads, msg.payload) - if countAboveMinTimestamp >= minCount { - ev.wasReady = true - return true, accPayloads - } } } + if countAboveMinTimestamp >= minCount { + ev.wasReady = true + return true, accPayloads + } return false, nil } diff --git a/core/capabilities/remote/target/client.go b/core/capabilities/remote/target/client.go index 5b65bf63e4..8572efed15 100644 --- a/core/capabilities/remote/target/client.go +++ b/core/capabilities/remote/target/client.go @@ -9,8 +9,10 @@ import ( commoncap "github.com/smartcontractkit/chainlink-common/pkg/capabilities" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote" "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/target/request" "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/types" + "github.com/smartcontractkit/chainlink/v2/core/capabilities/validation" "github.com/smartcontractkit/chainlink/v2/core/logger" ) @@ -151,7 +153,11 @@ func (c *client) Receive(ctx context.Context, msg *types.MessageBody) { c.mutex.Lock() defer c.mutex.Unlock() - messageID := GetMessageID(msg) + messageID, err := GetMessageID(msg) + if err != nil { + c.lggr.Errorw("invalid message ID", "err", err, "id", remote.SanitizeLogString(string(msg.MessageId))) + return + } c.lggr.Debugw("Remote client target receiving message", "messageID", messageID) @@ -167,8 +173,12 @@ func (c *client) Receive(ctx context.Context, msg *types.MessageBody) { } func GetMessageIDForRequest(req commoncap.CapabilityRequest) (string, error) { - if req.Metadata.WorkflowID == "" || req.Metadata.WorkflowExecutionID == "" { - return "", errors.New("workflow ID and workflow execution ID must be set in request metadata") + if err := validation.ValidateWorkflowOrExecutionID(req.Metadata.WorkflowID); err != nil { + return "", fmt.Errorf("workflow ID is invalid: %w", err) + } + + if err := validation.ValidateWorkflowOrExecutionID(req.Metadata.WorkflowExecutionID); err != nil { + return "", fmt.Errorf("workflow execution ID is invalid: %w", err) } return req.Metadata.WorkflowID + req.Metadata.WorkflowExecutionID, nil diff --git a/core/capabilities/remote/target/client_test.go b/core/capabilities/remote/target/client_test.go index 6d26b51b8a..2198636a7a 100644 --- a/core/capabilities/remote/target/client_test.go +++ b/core/capabilities/remote/target/client_test.go @@ -21,6 +21,11 @@ import ( p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" ) +const ( + workflowID1 = "15c631d295ef5e32deb99a10ee6804bc4af13855687559d7ff6552ac6dbb2ce0" + workflowExecutionID1 = "95ef5e32deb99a10ee6804bc4af13855687559d7ff6552ac6dbb2ce0abbadeed" +) + func Test_Client_DonTopologies(t *testing.T) { ctx := testutils.Context(t) @@ -192,8 +197,8 @@ func testClient(ctx context.Context, t *testing.T, numWorkflowPeers int, workflo responseCh, err := caller.Execute(ctx, commoncap.CapabilityRequest{ Metadata: commoncap.RequestMetadata{ - WorkflowID: "workflowID", - WorkflowExecutionID: "workflowExecutionID", + WorkflowID: workflowID1, + WorkflowExecutionID: workflowExecutionID1, }, Config: transmissionSchedule, Inputs: executeInputs, @@ -234,7 +239,10 @@ func (t *clientTestServer) Receive(_ context.Context, msg *remotetypes.MessageBo defer t.mux.Unlock() sender := toPeerID(msg.Sender) - messageID := target.GetMessageID(msg) + messageID, err := target.GetMessageID(msg) + if err != nil { + panic(err) + } if t.messageIDToSenders[messageID] == nil { t.messageIDToSenders[messageID] = make(map[p2ptypes.PeerID]bool) diff --git a/core/capabilities/remote/target/endtoend_test.go b/core/capabilities/remote/target/endtoend_test.go index 9bbb53d4f6..9abb6e99d5 100644 --- a/core/capabilities/remote/target/endtoend_test.go +++ b/core/capabilities/remote/target/endtoend_test.go @@ -226,7 +226,7 @@ func testRemoteTarget(ctx context.Context, t *testing.T, underlying commoncap.Ta for i := 0; i < numCapabilityPeers; i++ { capabilityPeer := capabilityPeers[i] capabilityDispatcher := broker.NewDispatcherForNode(capabilityPeer) - capabilityNode := target.NewServer(capabilityPeer, underlying, capInfo, capDonInfo, workflowDONs, capabilityDispatcher, + capabilityNode := target.NewServer(&commoncap.RemoteTargetConfig{RequestHashExcludedAttributes: []string{}}, capabilityPeer, underlying, capInfo, capDonInfo, workflowDONs, capabilityDispatcher, capabilityNodeResponseTimeout, lggr) servicetest.Run(t, capabilityNode) broker.RegisterReceiverNode(capabilityPeer, capabilityNode) @@ -261,8 +261,8 @@ func testRemoteTarget(ctx context.Context, t *testing.T, underlying commoncap.Ta responseCh, err := caller.Execute(ctx, commoncap.CapabilityRequest{ Metadata: commoncap.RequestMetadata{ - WorkflowID: "workflowID", - WorkflowExecutionID: "workflowExecutionID", + WorkflowID: workflowID1, + WorkflowExecutionID: workflowExecutionID1, }, Config: transmissionSchedule, Inputs: executeInputs, @@ -276,67 +276,47 @@ func testRemoteTarget(ctx context.Context, t *testing.T, underlying commoncap.Ta } type testAsyncMessageBroker struct { - services.StateMachine - t *testing.T + services.Service + eng *services.Engine + t *testing.T nodes map[p2ptypes.PeerID]remotetypes.Receiver sendCh chan *remotetypes.MessageBody - - stopCh services.StopChan - wg sync.WaitGroup -} - -func (a *testAsyncMessageBroker) HealthReport() map[string]error { - return nil -} - -func (a *testAsyncMessageBroker) Name() string { - return "testAsyncMessageBroker" } func newTestAsyncMessageBroker(t *testing.T, sendChBufferSize int) *testAsyncMessageBroker { - return &testAsyncMessageBroker{ + b := &testAsyncMessageBroker{ t: t, nodes: make(map[p2ptypes.PeerID]remotetypes.Receiver), - stopCh: make(services.StopChan), sendCh: make(chan *remotetypes.MessageBody, sendChBufferSize), } -} - -func (a *testAsyncMessageBroker) Start(ctx context.Context) error { - return a.StartOnce("testAsyncMessageBroker", func() error { - a.wg.Add(1) - go func() { - defer a.wg.Done() - - for { - select { - case <-a.stopCh: - return - case msg := <-a.sendCh: - receiverId := toPeerID(msg.Receiver) - - receiver, ok := a.nodes[receiverId] - if !ok { - panic("server not found for peer id") - } - - receiver.Receive(tests.Context(a.t), msg) + b.Service, b.eng = services.Config{ + Name: "testAsyncMessageBroker", + Start: b.start, + }.NewServiceEngine(logger.TestLogger(t)) + return b +} + +func (a *testAsyncMessageBroker) start(ctx context.Context) error { + a.eng.Go(func(ctx context.Context) { + for { + select { + case <-ctx.Done(): + return + case msg := <-a.sendCh: + receiverId := toPeerID(msg.Receiver) + + receiver, ok := a.nodes[receiverId] + if !ok { + panic("server not found for peer id") } - } - }() - return nil - }) -} - -func (a *testAsyncMessageBroker) Close() error { - return a.StopOnce("testAsyncMessageBroker", func() error { - close(a.stopCh) - a.wg.Wait() - return nil + receiver.Receive(tests.Context(a.t), msg) + } + } }) + return nil } func (a *testAsyncMessageBroker) NewDispatcherForNode(nodePeerID p2ptypes.PeerID) remotetypes.Dispatcher { diff --git a/core/capabilities/remote/target/request/client_request.go b/core/capabilities/remote/target/request/client_request.go index 50a742c218..c9f76fb25a 100644 --- a/core/capabilities/remote/target/request/client_request.go +++ b/core/capabilities/remote/target/request/client_request.go @@ -140,7 +140,10 @@ func (c *ClientRequest) OnMessage(_ context.Context, msg *types.MessageBody) err c.lggr.Debugw("OnMessage called for client request", "messageID", msg.MessageId) - sender := remote.ToPeerID(msg.Sender) + sender, err := remote.ToPeerID(msg.Sender) + if err != nil { + return fmt.Errorf("failed to convert message sender to PeerID: %w", err) + } received, expected := c.responseReceived[sender] if !expected { @@ -170,7 +173,7 @@ func (c *ClientRequest) OnMessage(_ context.Context, msg *types.MessageBody) err } } } else { - c.lggr.Warnw("received error response", "error", msg.ErrorMsg) + c.lggr.Warnw("received error response", "error", remote.SanitizeLogString(msg.ErrorMsg)) c.errorCount[msg.ErrorMsg]++ if c.errorCount[msg.ErrorMsg] == c.requiredIdenticalResponses { c.sendResponse(commoncap.CapabilityResponse{Err: errors.New(msg.ErrorMsg)}) diff --git a/core/capabilities/remote/target/request/client_request_test.go b/core/capabilities/remote/target/request/client_request_test.go index 07f43dbc71..7edb2f5e53 100644 --- a/core/capabilities/remote/target/request/client_request_test.go +++ b/core/capabilities/remote/target/request/client_request_test.go @@ -20,6 +20,11 @@ import ( p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" ) +const ( + workflowID1 = "15c631d295ef5e32deb99a10ee6804bc4af13855687559d7ff6552ac6dbb2ce0" + workflowExecutionID1 = "95ef5e32deb99a10ee6804bc4af13855687559d7ff6552ac6dbb2ce0abbadeed" +) + func Test_ClientRequest_MessageValidation(t *testing.T) { lggr := logger.TestLogger(t) @@ -68,8 +73,8 @@ func Test_ClientRequest_MessageValidation(t *testing.T) { capabilityRequest := commoncap.CapabilityRequest{ Metadata: commoncap.RequestMetadata{ - WorkflowID: "workflowID", - WorkflowExecutionID: "workflowExecutionID", + WorkflowID: workflowID1, + WorkflowExecutionID: workflowExecutionID1, }, Inputs: executeInputs, Config: transmissionSchedule, diff --git a/core/capabilities/remote/target/request/server_request.go b/core/capabilities/remote/target/request/server_request.go index b8ae05bc31..e16742a246 100644 --- a/core/capabilities/remote/target/request/server_request.go +++ b/core/capabilities/remote/target/request/server_request.go @@ -74,7 +74,11 @@ func (e *ServerRequest) OnMessage(ctx context.Context, msg *types.MessageBody) e return fmt.Errorf("sender missing from message") } - requester := remote.ToPeerID(msg.Sender) + requester, err := remote.ToPeerID(msg.Sender) + if err != nil { + return fmt.Errorf("failed to convert message sender to PeerID: %w", err) + } + if err := e.addRequester(requester); err != nil { return fmt.Errorf("failed to add requester to request: %w", err) } @@ -134,7 +138,7 @@ func (e *ServerRequest) executeRequest(ctx context.Context, payload []byte) erro return fmt.Errorf("failed to marshal capability response: %w", err) } - e.lggr.Debugw("received execution results", "metadata", capabilityRequest.Metadata, "error", capResponse.Err) + e.lggr.Debugw("received execution results", "workflowExecutionID", capabilityRequest.Metadata.WorkflowExecutionID, "error", capResponse.Err) e.setResult(responsePayload) return nil } diff --git a/core/capabilities/remote/target/server.go b/core/capabilities/remote/target/server.go index ea9caf81ef..5324475b19 100644 --- a/core/capabilities/remote/target/server.go +++ b/core/capabilities/remote/target/server.go @@ -4,13 +4,17 @@ import ( "context" "crypto/sha256" "encoding/hex" + "fmt" "sync" "time" commoncap "github.com/smartcontractkit/chainlink-common/pkg/capabilities" + "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote" "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/target/request" "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/types" + "github.com/smartcontractkit/chainlink/v2/core/capabilities/validation" p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -24,7 +28,9 @@ import ( // server communicates with corresponding client on remote nodes. type server struct { services.StateMachine - lggr logger.Logger + lggr logger.Logger + + config *commoncap.RemoteTargetConfig peerID p2ptypes.PeerID underlying commoncap.TargetCapability capInfo commoncap.CapabilityInfo @@ -51,9 +57,14 @@ type requestAndMsgID struct { messageID string } -func NewServer(peerID p2ptypes.PeerID, underlying commoncap.TargetCapability, capInfo commoncap.CapabilityInfo, localDonInfo commoncap.DON, +func NewServer(config *commoncap.RemoteTargetConfig, peerID p2ptypes.PeerID, underlying commoncap.TargetCapability, capInfo commoncap.CapabilityInfo, localDonInfo commoncap.DON, workflowDONs map[uint32]commoncap.DON, dispatcher types.Dispatcher, requestTimeout time.Duration, lggr logger.Logger) *server { + if config == nil { + lggr.Info("no config provided, using default values") + config = &commoncap.RemoteTargetConfig{} + } return &server{ + config: config, underlying: underlying, peerID: peerID, capInfo: capInfo, @@ -120,17 +131,28 @@ func (r *server) Receive(ctx context.Context, msg *types.MessageBody) { r.receiveLock.Lock() defer r.receiveLock.Unlock() - r.lggr.Debugw("received request for msg", "msgId", msg.MessageId) if msg.Method != types.MethodExecute { - r.lggr.Errorw("received request for unsupported method type", "method", msg.Method) + r.lggr.Errorw("received request for unsupported method type", "method", remote.SanitizeLogString(msg.Method)) + return + } + + messageId, err := GetMessageID(msg) + if err != nil { + r.lggr.Errorw("invalid message id", "err", err, "id", remote.SanitizeLogString(string(msg.MessageId))) + return + } + + msgHash, err := r.getMessageHash(msg) + if err != nil { + r.lggr.Errorw("failed to get message hash", "err", err) return } // A request is uniquely identified by the message id and the hash of the payload to prevent a malicious // actor from sending a different payload with the same message id - messageId := GetMessageID(msg) - hash := sha256.Sum256(msg.Payload) - requestID := messageId + hex.EncodeToString(hash[:]) + requestID := messageId + hex.EncodeToString(msgHash[:]) + + r.lggr.Debugw("received request", "msgId", msg.MessageId, "requestID", requestID) if requestIDs, ok := r.messageIDToRequestIDsCount[messageId]; ok { requestIDs[requestID] = requestIDs[requestID] + 1 @@ -142,7 +164,7 @@ func (r *server) Receive(ctx context.Context, msg *types.MessageBody) { if len(requestIDs) > 1 { // This is a potential attack vector as well as a situation that will occur if the client is sending non-deterministic payloads // so a warning is logged - r.lggr.Warnw("received messages with the same id and different payloads", "messageID", messageId, "requestIDToCount", requestIDs) + r.lggr.Warnw("received messages with the same id and different payloads", "messageID", messageId, "lenRequestIDs", len(requestIDs)) } if _, ok := r.requestIDToRequest[requestID]; !ok { @@ -161,14 +183,38 @@ func (r *server) Receive(ctx context.Context, msg *types.MessageBody) { reqAndMsgID := r.requestIDToRequest[requestID] - err := reqAndMsgID.request.OnMessage(ctx, msg) + err = reqAndMsgID.request.OnMessage(ctx, msg) if err != nil { - r.lggr.Errorw("request failed to OnMessage new message", "request", reqAndMsgID, "err", err) + r.lggr.Errorw("request failed to OnMessage new message", "messageID", reqAndMsgID.messageID, "err", err) } } -func GetMessageID(msg *types.MessageBody) string { - return string(msg.MessageId) +func (r *server) getMessageHash(msg *types.MessageBody) ([32]byte, error) { + req, err := pb.UnmarshalCapabilityRequest(msg.Payload) + if err != nil { + return [32]byte{}, fmt.Errorf("failed to unmarshal capability request: %w", err) + } + + for _, path := range r.config.RequestHashExcludedAttributes { + if !req.Inputs.DeleteAtPath(path) { + return [32]byte{}, fmt.Errorf("failed to delete attribute from map at path: %s", path) + } + } + + reqBytes, err := pb.MarshalCapabilityRequest(req) + if err != nil { + return [32]byte{}, fmt.Errorf("failed to marshal capability request: %w", err) + } + hash := sha256.Sum256(reqBytes) + return hash, nil +} + +func GetMessageID(msg *types.MessageBody) (string, error) { + idStr := string(msg.MessageId) + if !validation.IsValidID(idStr) { + return "", fmt.Errorf("invalid message id") + } + return idStr, nil } func (r *server) Ready() error { diff --git a/core/capabilities/remote/target/server_test.go b/core/capabilities/remote/target/server_test.go index a5aa45efd0..505a2dcce5 100644 --- a/core/capabilities/remote/target/server_test.go +++ b/core/capabilities/remote/target/server_test.go @@ -2,6 +2,7 @@ package target_test import ( "context" + "strconv" "testing" "time" @@ -11,6 +12,7 @@ import ( commoncap "github.com/smartcontractkit/chainlink-common/pkg/capabilities" "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/values" "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/target" remotetypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/types" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -18,19 +20,55 @@ import ( p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" ) +func Test_Server_ExcludesNonDeterministicInputAttributes(t *testing.T) { + ctx := testutils.Context(t) + + numCapabilityPeers := 4 + + callers, srvcs := testRemoteTargetServer(ctx, t, &commoncap.RemoteTargetConfig{RequestHashExcludedAttributes: []string{"signed_report.Signatures"}}, + &TestCapability{}, 10, 9, numCapabilityPeers, 3, 10*time.Minute) + + for idx, caller := range callers { + rawInputs := map[string]any{ + "signed_report": map[string]any{"Signatures": "sig" + strconv.Itoa(idx), "Price": 20}, + } + + inputs, err := values.NewMap(rawInputs) + require.NoError(t, err) + + _, err = caller.Execute(context.Background(), + commoncap.CapabilityRequest{ + Metadata: commoncap.RequestMetadata{ + WorkflowID: workflowID1, + WorkflowExecutionID: workflowExecutionID1, + }, + Inputs: inputs, + }) + require.NoError(t, err) + } + + for _, caller := range callers { + for i := 0; i < numCapabilityPeers; i++ { + msg := <-caller.receivedMessages + assert.Equal(t, remotetypes.Error_OK, msg.Error) + } + } + closeServices(t, srvcs) +} + func Test_Server_RespondsAfterSufficientRequests(t *testing.T) { ctx := testutils.Context(t) numCapabilityPeers := 4 - callers, srvcs := testRemoteTargetServer(ctx, t, &TestCapability{}, 10, 9, numCapabilityPeers, 3, 10*time.Minute) + callers, srvcs := testRemoteTargetServer(ctx, t, &commoncap.RemoteTargetConfig{}, &TestCapability{}, 10, 9, numCapabilityPeers, 3, 10*time.Minute) for _, caller := range callers { _, err := caller.Execute(context.Background(), commoncap.CapabilityRequest{ Metadata: commoncap.RequestMetadata{ - WorkflowID: "workflowID", - WorkflowExecutionID: "workflowExecutionID", + WorkflowID: workflowID1, + WorkflowExecutionID: workflowExecutionID1, }, }) require.NoError(t, err) @@ -50,14 +88,14 @@ func Test_Server_InsufficientCallers(t *testing.T) { numCapabilityPeers := 4 - callers, srvcs := testRemoteTargetServer(ctx, t, &TestCapability{}, 10, 10, numCapabilityPeers, 3, 100*time.Millisecond) + callers, srvcs := testRemoteTargetServer(ctx, t, &commoncap.RemoteTargetConfig{}, &TestCapability{}, 10, 10, numCapabilityPeers, 3, 100*time.Millisecond) for _, caller := range callers { _, err := caller.Execute(context.Background(), commoncap.CapabilityRequest{ Metadata: commoncap.RequestMetadata{ - WorkflowID: "workflowID", - WorkflowExecutionID: "workflowExecutionID", + WorkflowID: workflowID1, + WorkflowExecutionID: workflowExecutionID1, }, }) require.NoError(t, err) @@ -77,14 +115,14 @@ func Test_Server_CapabilityError(t *testing.T) { numCapabilityPeers := 4 - callers, srvcs := testRemoteTargetServer(ctx, t, &TestErrorCapability{}, 10, 9, numCapabilityPeers, 3, 100*time.Millisecond) + callers, srvcs := testRemoteTargetServer(ctx, t, &commoncap.RemoteTargetConfig{}, &TestErrorCapability{}, 10, 9, numCapabilityPeers, 3, 100*time.Millisecond) for _, caller := range callers { _, err := caller.Execute(context.Background(), commoncap.CapabilityRequest{ Metadata: commoncap.RequestMetadata{ - WorkflowID: "workflowID", - WorkflowExecutionID: "workflowExecutionID", + WorkflowID: workflowID1, + WorkflowExecutionID: workflowExecutionID1, }, }) require.NoError(t, err) @@ -100,6 +138,7 @@ func Test_Server_CapabilityError(t *testing.T) { } func testRemoteTargetServer(ctx context.Context, t *testing.T, + config *commoncap.RemoteTargetConfig, underlying commoncap.TargetCapability, numWorkflowPeers int, workflowDonF uint8, numCapabilityPeers int, capabilityDonF uint8, capabilityNodeResponseTimeout time.Duration) ([]*serverTestClient, []services.Service) { @@ -150,7 +189,7 @@ func testRemoteTargetServer(ctx context.Context, t *testing.T, for i := 0; i < numCapabilityPeers; i++ { capabilityPeer := capabilityPeers[i] capabilityDispatcher := broker.NewDispatcherForNode(capabilityPeer) - capabilityNode := target.NewServer(capabilityPeer, underlying, capInfo, capDonInfo, workflowDONs, capabilityDispatcher, + capabilityNode := target.NewServer(config, capabilityPeer, underlying, capInfo, capDonInfo, workflowDONs, capabilityDispatcher, capabilityNodeResponseTimeout, lggr) require.NoError(t, capabilityNode.Start(ctx)) broker.RegisterReceiverNode(capabilityPeer, capabilityNode) diff --git a/core/capabilities/remote/trigger_publisher.go b/core/capabilities/remote/trigger_publisher.go index 35ce41118f..b46d8bcb72 100644 --- a/core/capabilities/remote/trigger_publisher.go +++ b/core/capabilities/remote/trigger_publisher.go @@ -10,6 +10,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/types" + "github.com/smartcontractkit/chainlink/v2/core/capabilities/validation" "github.com/smartcontractkit/chainlink/v2/core/logger" p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" ) @@ -21,11 +22,12 @@ import ( // // TriggerPublisher communicates with corresponding TriggerSubscribers on remote nodes. type triggerPublisher struct { - config capabilities.RemoteTriggerConfig + config *capabilities.RemoteTriggerConfig underlying commoncap.TriggerCapability capInfo commoncap.CapabilityInfo capDonInfo commoncap.DON workflowDONs map[uint32]commoncap.DON + membersCache map[uint32]map[p2ptypes.PeerID]bool dispatcher types.Dispatcher messageCache *messageCache[registrationKey, p2ptypes.PeerID] registrations map[registrationKey]*pubRegState @@ -41,21 +43,34 @@ type registrationKey struct { } type pubRegState struct { - callback <-chan commoncap.CapabilityResponse - request commoncap.CapabilityRequest + callback <-chan commoncap.TriggerResponse + request commoncap.TriggerRegistrationRequest } var _ types.Receiver = &triggerPublisher{} var _ services.Service = &triggerPublisher{} -func NewTriggerPublisher(config capabilities.RemoteTriggerConfig, underlying commoncap.TriggerCapability, capInfo commoncap.CapabilityInfo, capDonInfo commoncap.DON, workflowDONs map[uint32]commoncap.DON, dispatcher types.Dispatcher, lggr logger.Logger) *triggerPublisher { +func NewTriggerPublisher(config *capabilities.RemoteTriggerConfig, underlying commoncap.TriggerCapability, capInfo commoncap.CapabilityInfo, capDonInfo commoncap.DON, workflowDONs map[uint32]commoncap.DON, dispatcher types.Dispatcher, lggr logger.Logger) *triggerPublisher { + if config == nil { + lggr.Info("no config provided, using default values") + config = &capabilities.RemoteTriggerConfig{} + } config.ApplyDefaults() + membersCache := make(map[uint32]map[p2ptypes.PeerID]bool) + for id, don := range workflowDONs { + cache := make(map[p2ptypes.PeerID]bool) + for _, member := range don.Members { + cache[member] = true + } + membersCache[id] = cache + } return &triggerPublisher{ config: config, underlying: underlying, capInfo: capInfo, capDonInfo: capDonInfo, workflowDONs: workflowDONs, + membersCache: membersCache, dispatcher: dispatcher, messageCache: NewMessageCache[registrationKey, p2ptypes.PeerID](), registrations: make(map[registrationKey]*pubRegState), @@ -72,11 +87,16 @@ func (p *triggerPublisher) Start(ctx context.Context) error { } func (p *triggerPublisher) Receive(_ context.Context, msg *types.MessageBody) { - sender := ToPeerID(msg.Sender) + sender, err := ToPeerID(msg.Sender) + if err != nil { + p.lggr.Errorw("failed to convert message sender to PeerID", "err", err) + return + } + if msg.Method == types.MethodRegisterTrigger { - req, err := pb.UnmarshalCapabilityRequest(msg.Payload) + req, err := pb.UnmarshalTriggerRegistrationRequest(msg.Payload) if err != nil { - p.lggr.Errorw("failed to unmarshal capability request", "capabilityId", p.capInfo.ID, "err", err) + p.lggr.Errorw("failed to unmarshal trigger registration request", "capabilityId", p.capInfo.ID, "err", err) return } callerDon, ok := p.workflowDONs[msg.CallerDonId] @@ -84,6 +104,14 @@ func (p *triggerPublisher) Receive(_ context.Context, msg *types.MessageBody) { p.lggr.Errorw("received a message from unsupported workflow DON", "capabilityId", p.capInfo.ID, "callerDonId", msg.CallerDonId) return } + if !p.membersCache[msg.CallerDonId][sender] { + p.lggr.Errorw("sender not a member of its workflow DON", "capabilityId", p.capInfo.ID, "callerDonId", msg.CallerDonId, "sender", sender) + return + } + if err = validation.ValidateWorkflowOrExecutionID(req.Metadata.WorkflowID); err != nil { + p.lggr.Errorw("received trigger request with invalid workflow ID", "capabilityId", p.capInfo.ID, "workflowId", SanitizeLogString(req.Metadata.WorkflowID), "err", err) + return + } p.lggr.Debugw("received trigger registration", "capabilityId", p.capInfo.ID, "workflowId", req.Metadata.WorkflowID, "sender", sender) key := registrationKey{msg.CallerDonId, req.Metadata.WorkflowID} nowMs := time.Now().UnixMilli() @@ -107,7 +135,7 @@ func (p *triggerPublisher) Receive(_ context.Context, msg *types.MessageBody) { p.lggr.Errorw("failed to aggregate trigger registrations", "capabilityId", p.capInfo.ID, "workflowId", req.Metadata.WorkflowID, "err", err) return } - unmarshaled, err := pb.UnmarshalCapabilityRequest(aggregated) + unmarshaled, err := pb.UnmarshalTriggerRegistrationRequest(aggregated) if err != nil { p.lggr.Errorw("failed to unmarshal request", "capabilityId", p.capInfo.ID, "err", err) return @@ -127,7 +155,7 @@ func (p *triggerPublisher) Receive(_ context.Context, msg *types.MessageBody) { p.lggr.Errorw("failed to register trigger", "capabilityId", p.capInfo.ID, "workflowId", req.Metadata.WorkflowID, "err", err) } } else { - p.lggr.Errorw("received trigger request with unknown method", "method", msg.Method, "sender", sender) + p.lggr.Errorw("received trigger request with unknown method", "method", SanitizeLogString(msg.Method), "sender", sender) } } @@ -141,7 +169,7 @@ func (p *triggerPublisher) registrationCleanupLoop() { return case <-ticker.C: now := time.Now().UnixMilli() - p.mu.RLock() + p.mu.Lock() for key, req := range p.registrations { callerDon := p.workflowDONs[key.callerDonId] ready, _ := p.messageCache.Ready(key, uint32(2*callerDon.F+1), now-p.config.RegistrationExpiry.Milliseconds(), false) @@ -156,12 +184,12 @@ func (p *triggerPublisher) registrationCleanupLoop() { p.messageCache.Delete(key) } } - p.mu.RUnlock() + p.mu.Unlock() } } } -func (p *triggerPublisher) triggerEventLoop(callbackCh <-chan commoncap.CapabilityResponse, key registrationKey) { +func (p *triggerPublisher) triggerEventLoop(callbackCh <-chan commoncap.TriggerResponse, key registrationKey) { defer p.wg.Done() for { select { @@ -172,14 +200,9 @@ func (p *triggerPublisher) triggerEventLoop(callbackCh <-chan commoncap.Capabili p.lggr.Infow("triggerEventLoop channel closed", "capabilityId", p.capInfo.ID, "workflowId", key.workflowId) return } - triggerEvent := capabilities.TriggerEvent{} - err := response.Value.UnwrapTo(&triggerEvent) - if err != nil { - p.lggr.Errorw("can't unwrap trigger event", "capabilityId", p.capInfo.ID, "workflowId", key.workflowId, "err", err) - break - } + triggerEvent := response.Event p.lggr.Debugw("received trigger event", "capabilityId", p.capInfo.ID, "workflowId", key.workflowId, "triggerEventID", triggerEvent.ID) - marshaled, err := pb.MarshalCapabilityResponse(response) + marshaled, err := pb.MarshalTriggerResponse(response) if err != nil { p.lggr.Debugw("can't marshal trigger event", "err", err) break diff --git a/core/capabilities/remote/trigger_publisher_test.go b/core/capabilities/remote/trigger_publisher_test.go index 1e3000d20c..bcc79b4fbb 100644 --- a/core/capabilities/remote/trigger_publisher_test.go +++ b/core/capabilities/remote/trigger_publisher_test.go @@ -7,7 +7,6 @@ import ( "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities" commoncap "github.com/smartcontractkit/chainlink-common/pkg/capabilities" "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote" @@ -42,7 +41,7 @@ func TestTriggerPublisher_Register(t *testing.T) { } dispatcher := remoteMocks.NewDispatcher(t) - config := capabilities.RemoteTriggerConfig{ + config := &commoncap.RemoteTriggerConfig{ RegistrationRefresh: 100 * time.Millisecond, RegistrationExpiry: 100 * time.Second, MinResponsesToAggregate: 1, @@ -53,18 +52,18 @@ func TestTriggerPublisher_Register(t *testing.T) { } underlying := &testTrigger{ info: capInfo, - registrationsCh: make(chan commoncap.CapabilityRequest, 2), + registrationsCh: make(chan commoncap.TriggerRegistrationRequest, 2), } publisher := remote.NewTriggerPublisher(config, underlying, capInfo, capDonInfo, workflowDONs, dispatcher, lggr) require.NoError(t, publisher.Start(ctx)) // trigger registration event - capRequest := commoncap.CapabilityRequest{ + triggerRequest := commoncap.TriggerRegistrationRequest{ Metadata: commoncap.RequestMetadata{ WorkflowID: workflowID1, }, } - marshaled, err := pb.MarshalCapabilityRequest(capRequest) + marshaled, err := pb.MarshalTriggerRegistrationRequest(triggerRequest) require.NoError(t, err) regEvent := &remotetypes.MessageBody{ Sender: p1[:], @@ -73,26 +72,32 @@ func TestTriggerPublisher_Register(t *testing.T) { Payload: marshaled, } publisher.Receive(ctx, regEvent) + // node p1 is not a member of the workflow DON so registration shoudn't happen + require.Empty(t, underlying.registrationsCh) + + regEvent.Sender = p2[:] + publisher.Receive(ctx, regEvent) + require.NotEmpty(t, underlying.registrationsCh) forwarded := <-underlying.registrationsCh - require.Equal(t, capRequest.Metadata.WorkflowID, forwarded.Metadata.WorkflowID) + require.Equal(t, triggerRequest.Metadata.WorkflowID, forwarded.Metadata.WorkflowID) require.NoError(t, publisher.Close()) } type testTrigger struct { info commoncap.CapabilityInfo - registrationsCh chan commoncap.CapabilityRequest + registrationsCh chan commoncap.TriggerRegistrationRequest } func (t *testTrigger) Info(_ context.Context) (commoncap.CapabilityInfo, error) { return t.info, nil } -func (t *testTrigger) RegisterTrigger(_ context.Context, request commoncap.CapabilityRequest) (<-chan commoncap.CapabilityResponse, error) { +func (t *testTrigger) RegisterTrigger(_ context.Context, request commoncap.TriggerRegistrationRequest) (<-chan commoncap.TriggerResponse, error) { t.registrationsCh <- request return nil, nil } -func (t *testTrigger) UnregisterTrigger(_ context.Context, request commoncap.CapabilityRequest) error { +func (t *testTrigger) UnregisterTrigger(_ context.Context, request commoncap.TriggerRegistrationRequest) error { return nil } diff --git a/core/capabilities/remote/trigger_subscriber.go b/core/capabilities/remote/trigger_subscriber.go index 0ccbf37c61..967b59258a 100644 --- a/core/capabilities/remote/trigger_subscriber.go +++ b/core/capabilities/remote/trigger_subscriber.go @@ -6,7 +6,6 @@ import ( sync "sync" "time" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities" commoncap "github.com/smartcontractkit/chainlink-common/pkg/capabilities" "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" "github.com/smartcontractkit/chainlink-common/pkg/services" @@ -23,11 +22,11 @@ import ( // // TriggerSubscriber communicates with corresponding TriggerReceivers on remote nodes. type triggerSubscriber struct { - config capabilities.RemoteTriggerConfig + config *commoncap.RemoteTriggerConfig capInfo commoncap.CapabilityInfo - capDonInfo capabilities.DON + capDonInfo commoncap.DON capDonMembers map[p2ptypes.PeerID]struct{} - localDonInfo capabilities.DON + localDonInfo commoncap.DON dispatcher types.Dispatcher aggregator types.Aggregator messageCache *messageCache[triggerEventKey, p2ptypes.PeerID] @@ -44,7 +43,7 @@ type triggerEventKey struct { } type subRegState struct { - callback chan commoncap.CapabilityResponse + callback chan commoncap.TriggerResponse rawRequest []byte } @@ -53,13 +52,20 @@ var _ types.Receiver = &triggerSubscriber{} var _ services.Service = &triggerSubscriber{} // TODO makes this configurable with a default -const defaultSendChannelBufferSize = 1000 +const ( + defaultSendChannelBufferSize = 1000 + maxBatchedWorkflowIDs = 1000 +) -func NewTriggerSubscriber(config capabilities.RemoteTriggerConfig, capInfo commoncap.CapabilityInfo, capDonInfo capabilities.DON, localDonInfo capabilities.DON, dispatcher types.Dispatcher, aggregator types.Aggregator, lggr logger.Logger) *triggerSubscriber { +func NewTriggerSubscriber(config *commoncap.RemoteTriggerConfig, capInfo commoncap.CapabilityInfo, capDonInfo commoncap.DON, localDonInfo commoncap.DON, dispatcher types.Dispatcher, aggregator types.Aggregator, lggr logger.Logger) *triggerSubscriber { if aggregator == nil { lggr.Warnw("no aggregator provided, using default MODE aggregator", "capabilityId", capInfo.ID) aggregator = NewDefaultModeAggregator(uint32(capDonInfo.F + 1)) } + if config == nil { + lggr.Info("no config provided, using default values") + config = &commoncap.RemoteTriggerConfig{} + } config.ApplyDefaults() capDonMembers := make(map[p2ptypes.PeerID]struct{}) for _, member := range capDonInfo.Members { @@ -92,8 +98,8 @@ func (s *triggerSubscriber) Info(ctx context.Context) (commoncap.CapabilityInfo, return s.capInfo, nil } -func (s *triggerSubscriber) RegisterTrigger(ctx context.Context, request commoncap.CapabilityRequest) (<-chan commoncap.CapabilityResponse, error) { - rawRequest, err := pb.MarshalCapabilityRequest(request) +func (s *triggerSubscriber) RegisterTrigger(ctx context.Context, request commoncap.TriggerRegistrationRequest) (<-chan commoncap.TriggerResponse, error) { + rawRequest, err := pb.MarshalTriggerRegistrationRequest(request) if err != nil { return nil, err } @@ -107,7 +113,7 @@ func (s *triggerSubscriber) RegisterTrigger(ctx context.Context, request commonc regState, ok := s.registeredWorkflows[request.Metadata.WorkflowID] if !ok { regState = &subRegState{ - callback: make(chan commoncap.CapabilityResponse, defaultSendChannelBufferSize), + callback: make(chan commoncap.TriggerResponse, defaultSendChannelBufferSize), rawRequest: rawRequest, } s.registeredWorkflows[request.Metadata.WorkflowID] = regState @@ -154,7 +160,7 @@ func (s *triggerSubscriber) registrationLoop() { } } -func (s *triggerSubscriber) UnregisterTrigger(ctx context.Context, request commoncap.CapabilityRequest) error { +func (s *triggerSubscriber) UnregisterTrigger(ctx context.Context, request commoncap.TriggerRegistrationRequest) error { s.mu.Lock() defer s.mu.Unlock() @@ -169,7 +175,12 @@ func (s *triggerSubscriber) UnregisterTrigger(ctx context.Context, request commo } func (s *triggerSubscriber) Receive(_ context.Context, msg *types.MessageBody) { - sender := ToPeerID(msg.Sender) + sender, err := ToPeerID(msg.Sender) + if err != nil { + s.lggr.Errorw("failed to convert message sender to PeerID", "err", err) + return + } + if _, found := s.capDonMembers[sender]; !found { s.lggr.Errorw("received message from unexpected node", "capabilityId", s.capInfo.ID, "sender", sender) return @@ -180,12 +191,16 @@ func (s *triggerSubscriber) Receive(_ context.Context, msg *types.MessageBody) { s.lggr.Errorw("received message with invalid trigger metadata", "capabilityId", s.capInfo.ID, "sender", sender) return } + if len(meta.WorkflowIds) > maxBatchedWorkflowIDs { + s.lggr.Errorw("received message with too many workflow IDs - truncating", "capabilityId", s.capInfo.ID, "nWorkflows", len(meta.WorkflowIds), "sender", sender) + meta.WorkflowIds = meta.WorkflowIds[:maxBatchedWorkflowIDs] + } for _, workflowId := range meta.WorkflowIds { s.mu.RLock() registration, found := s.registeredWorkflows[workflowId] s.mu.RUnlock() if !found { - s.lggr.Errorw("received message for unregistered workflow", "capabilityId", s.capInfo.ID, "workflowID", workflowId, "sender", sender) + s.lggr.Errorw("received message for unregistered workflow", "capabilityId", s.capInfo.ID, "workflowID", SanitizeLogString(workflowId), "sender", sender) continue } key := triggerEventKey{ @@ -193,10 +208,10 @@ func (s *triggerSubscriber) Receive(_ context.Context, msg *types.MessageBody) { workflowId: workflowId, } nowMs := time.Now().UnixMilli() - s.mu.RLock() + s.mu.Lock() creationTs := s.messageCache.Insert(key, sender, nowMs, msg.Payload) ready, payloads := s.messageCache.Ready(key, s.config.MinResponsesToAggregate, nowMs-s.config.MessageExpiry.Milliseconds(), true) - s.mu.RUnlock() + s.mu.Unlock() if nowMs-creationTs > s.config.RegistrationExpiry.Milliseconds() { s.lggr.Warnw("received trigger event for an expired ID", "triggerEventID", meta.TriggerEventId, "capabilityId", s.capInfo.ID, "workflowId", workflowId, "sender", sender) continue @@ -213,7 +228,7 @@ func (s *triggerSubscriber) Receive(_ context.Context, msg *types.MessageBody) { } } } else { - s.lggr.Errorw("received trigger event with unknown method", "method", msg.Method, "sender", sender) + s.lggr.Errorw("received trigger event with unknown method", "method", SanitizeLogString(msg.Method), "sender", sender) } } diff --git a/core/capabilities/remote/trigger_subscriber_test.go b/core/capabilities/remote/trigger_subscriber_test.go index 93e962215a..b8cc3ddc7b 100644 --- a/core/capabilities/remote/trigger_subscriber_test.go +++ b/core/capabilities/remote/trigger_subscriber_test.go @@ -7,7 +7,6 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities" commoncap "github.com/smartcontractkit/chainlink-common/pkg/capabilities" "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" "github.com/smartcontractkit/chainlink-common/pkg/values" @@ -22,7 +21,7 @@ import ( const ( peerID1 = "12D3KooWF3dVeJ6YoT5HFnYhmwQWWMoEwVFzJQ5kKCMX3ZityxMC" peerID2 = "12D3KooWQsmok6aD8PZqt3RnJhQRrNzKHLficq7zYFRp7kZ1hHP8" - workflowID1 = "workflowID1" + workflowID1 = "15c631d295ef5e32deb99a10ee6804bc4af13855687559d7ff6552ac6dbb2ce0" ) var ( @@ -63,7 +62,7 @@ func TestTriggerSubscriber_RegisterAndReceive(t *testing.T) { }) // register trigger - config := capabilities.RemoteTriggerConfig{ + config := &commoncap.RemoteTriggerConfig{ RegistrationRefresh: 100 * time.Millisecond, RegistrationExpiry: 100 * time.Second, MinResponsesToAggregate: 1, @@ -72,7 +71,7 @@ func TestTriggerSubscriber_RegisterAndReceive(t *testing.T) { subscriber := remote.NewTriggerSubscriber(config, capInfo, capDonInfo, workflowDonInfo, dispatcher, nil, lggr) require.NoError(t, subscriber.Start(ctx)) - req := commoncap.CapabilityRequest{ + req := commoncap.TriggerRegistrationRequest{ Metadata: commoncap.RequestMetadata{ WorkflowID: workflowID1, }, @@ -84,11 +83,13 @@ func TestTriggerSubscriber_RegisterAndReceive(t *testing.T) { // receive trigger event triggerEventValue, err := values.NewMap(triggerEvent1) require.NoError(t, err) - capResponse := commoncap.CapabilityResponse{ - Value: triggerEventValue, - Err: nil, + capResponse := commoncap.TriggerResponse{ + Event: commoncap.TriggerEvent{ + Outputs: triggerEventValue, + }, + Err: nil, } - marshaled, err := pb.MarshalCapabilityResponse(capResponse) + marshaled, err := pb.MarshalTriggerResponse(capResponse) require.NoError(t, err) triggerEvent := &remotetypes.MessageBody{ Sender: p1[:], @@ -102,7 +103,7 @@ func TestTriggerSubscriber_RegisterAndReceive(t *testing.T) { } subscriber.Receive(ctx, triggerEvent) response := <-triggerEventCallbackCh - require.Equal(t, response.Value, triggerEventValue) + require.Equal(t, response.Event.Outputs, triggerEventValue) require.NoError(t, subscriber.UnregisterTrigger(ctx, req)) require.NoError(t, subscriber.UnregisterTrigger(ctx, req)) diff --git a/core/capabilities/remote/types/messages.pb.go b/core/capabilities/remote/types/messages.pb.go index f5b77b5c15..d6c1876859 100644 --- a/core/capabilities/remote/types/messages.pb.go +++ b/core/capabilities/remote/types/messages.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.34.2 -// protoc v4.25.1 +// protoc v5.28.2 // source: core/capabilities/remote/types/messages.proto package types diff --git a/core/capabilities/remote/types/types.go b/core/capabilities/remote/types/types.go index 3629fc06fe..7f3868486a 100644 --- a/core/capabilities/remote/types/types.go +++ b/core/capabilities/remote/types/types.go @@ -31,7 +31,7 @@ type Receiver interface { } type Aggregator interface { - Aggregate(eventID string, responses [][]byte) (commoncap.CapabilityResponse, error) + Aggregate(eventID string, responses [][]byte) (commoncap.TriggerResponse, error) } // NOTE: this type will become part of the Registry (KS-108) diff --git a/core/capabilities/remote/utils.go b/core/capabilities/remote/utils.go index dba24b843c..ea6a3efb18 100644 --- a/core/capabilities/remote/utils.go +++ b/core/capabilities/remote/utils.go @@ -7,6 +7,7 @@ import ( "encoding/hex" "errors" "fmt" + "unicode" "google.golang.org/protobuf/proto" @@ -16,6 +17,10 @@ import ( p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" ) +const ( + maxLoggedStringLen = 256 +) + func ValidateMessage(msg p2ptypes.Message, expectedReceiver p2ptypes.PeerID) (*remotetypes.MessageBody, error) { var topLevelMessage remotetypes.Message err := proto.Unmarshal(msg.Payload, &topLevelMessage) @@ -43,10 +48,14 @@ func ValidateMessage(msg p2ptypes.Message, expectedReceiver p2ptypes.PeerID) (*r return &body, nil } -func ToPeerID(peerID []byte) p2ptypes.PeerID { +func ToPeerID(peerID []byte) (p2ptypes.PeerID, error) { + if len(peerID) != p2ptypes.PeerIDLength { + return p2ptypes.PeerID{}, fmt.Errorf("invalid peer ID length: %d", len(peerID)) + } + var id p2ptypes.PeerID copy(id[:], peerID) - return id + return id, nil } // Default MODE Aggregator needs a configurable number of identical responses for aggregation to succeed @@ -62,15 +71,15 @@ func NewDefaultModeAggregator(minIdenticalResponses uint32) *defaultModeAggregat } } -func (a *defaultModeAggregator) Aggregate(_ string, responses [][]byte) (commoncap.CapabilityResponse, error) { +func (a *defaultModeAggregator) Aggregate(_ string, responses [][]byte) (commoncap.TriggerResponse, error) { found, err := AggregateModeRaw(responses, a.minIdenticalResponses) if err != nil { - return commoncap.CapabilityResponse{}, fmt.Errorf("failed to aggregate responses, err: %w", err) + return commoncap.TriggerResponse{}, fmt.Errorf("failed to aggregate responses, err: %w", err) } - unmarshaled, err := pb.UnmarshalCapabilityResponse(found) + unmarshaled, err := pb.UnmarshalTriggerResponse(found) if err != nil { - return commoncap.CapabilityResponse{}, fmt.Errorf("failed to unmarshal aggregated responses, err: %w", err) + return commoncap.TriggerResponse{}, fmt.Errorf("failed to unmarshal aggregated responses, err: %w", err) } return unmarshaled, nil } @@ -85,7 +94,8 @@ func AggregateModeRaw(elemList [][]byte, minIdenticalResponses uint32) ([]byte, hashToCount[sha]++ if hashToCount[sha] >= minIdenticalResponses { found = elem - break + // update in case we find another elem with an even higher count + minIdenticalResponses = hashToCount[sha] } } if found == nil { @@ -93,3 +103,17 @@ func AggregateModeRaw(elemList [][]byte, minIdenticalResponses uint32) ([]byte, } return found, nil } + +func SanitizeLogString(s string) string { + tooLongSuffix := "" + if len(s) > maxLoggedStringLen { + s = s[:maxLoggedStringLen] + tooLongSuffix = " [TRUNCATED]" + } + for i := 0; i < len(s); i++ { + if !unicode.IsPrint(rune(s[i])) { + return "[UNPRINTABLE] " + hex.EncodeToString([]byte(s)) + tooLongSuffix + } + } + return s + tooLongSuffix +} diff --git a/core/capabilities/remote/utils_test.go b/core/capabilities/remote/utils_test.go index 8bebf71fb6..6707e6ffb2 100644 --- a/core/capabilities/remote/utils_test.go +++ b/core/capabilities/remote/utils_test.go @@ -84,27 +84,32 @@ func encodeAndSign(t *testing.T, senderPrivKey ed25519.PrivateKey, senderId p2pt } func TestToPeerID(t *testing.T) { - id := remote.ToPeerID([]byte("12345678901234567890123456789012")) + id, err := remote.ToPeerID([]byte("12345678901234567890123456789012")) + require.NoError(t, err) require.Equal(t, "12D3KooWD8QYTQVYjB6oog4Ej8PcPpqTrPRnxLQap8yY8KUQRVvq", id.String()) } func TestDefaultModeAggregator_Aggregate(t *testing.T) { val, err := values.NewMap(triggerEvent1) require.NoError(t, err) - capResponse1 := commoncap.CapabilityResponse{ - Value: val, - Err: nil, + capResponse1 := commoncap.TriggerResponse{ + Event: commoncap.TriggerEvent{ + Outputs: val, + }, + Err: nil, } - marshaled1, err := pb.MarshalCapabilityResponse(capResponse1) + marshaled1, err := pb.MarshalTriggerResponse(capResponse1) require.NoError(t, err) val2, err := values.NewMap(triggerEvent2) require.NoError(t, err) - capResponse2 := commoncap.CapabilityResponse{ - Value: val2, - Err: nil, + capResponse2 := commoncap.TriggerResponse{ + Event: commoncap.TriggerEvent{ + Outputs: val2, + }, + Err: nil, } - marshaled2, err := pb.MarshalCapabilityResponse(capResponse2) + marshaled2, err := pb.MarshalTriggerResponse(capResponse2) require.NoError(t, err) agg := remote.NewDefaultModeAggregator(2) @@ -118,3 +123,14 @@ func TestDefaultModeAggregator_Aggregate(t *testing.T) { require.NoError(t, err) require.Equal(t, res, capResponse1) } + +func TestSanitizeLogString(t *testing.T) { + require.Equal(t, "hello", remote.SanitizeLogString("hello")) + require.Equal(t, "[UNPRINTABLE] 0a", remote.SanitizeLogString("\n")) + + longString := "" + for i := 0; i < 100; i++ { + longString += "aa-aa-aa-" + } + require.Equal(t, longString[:256]+" [TRUNCATED]", remote.SanitizeLogString(longString)) +} diff --git a/core/capabilities/streams/codec.go b/core/capabilities/streams/codec.go index d2bc451a39..d6918f0c73 100644 --- a/core/capabilities/streams/codec.go +++ b/core/capabilities/streams/codec.go @@ -1,6 +1,7 @@ package streams import ( + "encoding/hex" "fmt" "github.com/ethereum/go-ethereum/common" @@ -19,7 +20,7 @@ type codec struct { var _ datastreams.ReportCodec = &codec{} func (c *codec) Unwrap(wrapped values.Value) ([]datastreams.FeedReport, error) { - dest, err := datastreams.UnwrapFeedReportList(wrapped) + dest, err := datastreams.UnwrapStreamsTriggerEventToFeedReportList(wrapped) if err != nil { return nil, fmt.Errorf("failed to unwrap: %v", err) } @@ -34,6 +35,9 @@ func (c *codec) Unwrap(wrapped values.Value) ([]datastreams.FeedReport, error) { if err2 != nil { return nil, fmt.Errorf("failed to decode: %v", err2) } + if decoded.FeedId != id.Bytes() { + return nil, fmt.Errorf("feed ID mismatch: FeedID: %s, FullReport.FeedId: %s", id, hex.EncodeToString(decoded.FeedId[:])) + } dest[i].BenchmarkPrice = decoded.BenchmarkPrice.Bytes() dest[i].ObservationTimestamp = int64(decoded.ObservationsTimestamp) } @@ -41,7 +45,9 @@ func (c *codec) Unwrap(wrapped values.Value) ([]datastreams.FeedReport, error) { } func (c *codec) Wrap(reports []datastreams.FeedReport) (values.Value, error) { - return values.Wrap(reports) + return values.Wrap(&datastreams.StreamsTriggerEvent{ + Payload: reports, + }) } func (c *codec) Validate(report datastreams.FeedReport, allowedSigners [][]byte, minRequiredSignatures int) error { diff --git a/core/capabilities/streams/codec_test.go b/core/capabilities/streams/codec_test.go index e3ada731e4..02ec474fec 100644 --- a/core/capabilities/streams/codec_test.go +++ b/core/capabilities/streams/codec_test.go @@ -69,7 +69,7 @@ func TestCodec_WrapUnwrap(t *testing.T) { _, err = codec.Unwrap(values.NewBool(true)) require.Error(t, err) - // correct reports byt wrong signatures + // correct reports but wrong signatures unwrapped, err := codec.Unwrap(wrapped) require.NoError(t, err) require.Equal(t, 2, len(unwrapped)) @@ -85,6 +85,20 @@ func TestCodec_WrapUnwrap(t *testing.T) { for _, report := range unwrapped { require.NoError(t, codec.Validate(report, allowedSigners, 2)) } + + // invalid FeedID + wrappedInvalid, err := codec.Wrap([]datastreams.FeedReport{ + { + FeedID: id2Str, // ID #2 doesn't match what's in report #1 + FullReport: report1, + ReportContext: rawCtx, + Signatures: [][]byte{signatureK1R1, signatureK2R1}, + }, + }) + require.NoError(t, err) + _, err = codec.Unwrap(wrappedInvalid) + require.Error(t, err) + require.Contains(t, err.Error(), "feed ID mismatch") } func newFeedID(t *testing.T) ([32]byte, string) { diff --git a/core/capabilities/streams/consensus_agg_test.go b/core/capabilities/streams/consensus_agg_test.go index 506ad26f86..04396a38ba 100644 --- a/core/capabilities/streams/consensus_agg_test.go +++ b/core/capabilities/streams/consensus_agg_test.go @@ -9,7 +9,6 @@ import ( "github.com/smartcontractkit/libocr/commontypes" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities" "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3/datafeeds" "github.com/smartcontractkit/chainlink-common/pkg/capabilities/datastreams" "github.com/smartcontractkit/chainlink-common/pkg/values" @@ -98,26 +97,18 @@ func newObservations(t *testing.T, nNodes int, feeds []feed, minRequiredSignatur reportList = append(reportList, signedStreamsReport) } - payloadVal, err := values.Wrap(reportList) - require.NoError(t, err) - - meta := datastreams.SignersMetadata{ + meta := datastreams.Metadata{ Signers: allowedSigners, MinRequiredSignatures: minRequiredSignatures, } - metaVal, err := values.Wrap(meta) - require.NoError(t, err) - - triggerEvent := capabilities.TriggerEvent{ - TriggerType: triggerID, - ID: "unused", - Timestamp: "1234", - Metadata: metaVal, - Payload: payloadVal, + p := datastreams.StreamsTriggerEvent{ + Payload: reportList, + Metadata: meta, } - wrappedEvent, err := values.Wrap(triggerEvent) + outputs, err := values.WrapMap(p) require.NoError(t, err) - observations[commontypes.OracleID(i)] = []values.Value{wrappedEvent} + + observations[commontypes.OracleID(i)] = []values.Value{outputs} } return observations } diff --git a/core/capabilities/streams/trigger_test.go b/core/capabilities/streams/trigger_test.go index cb4cfaa36b..3db6f2445e 100644 --- a/core/capabilities/streams/trigger_test.go +++ b/core/capabilities/streams/trigger_test.go @@ -87,13 +87,13 @@ func TestStreamsTrigger(t *testing.T) { Members: capMembers, F: uint8(F), } - config := capabilities.RemoteTriggerConfig{ + config := &capabilities.RemoteTriggerConfig{ MinResponsesToAggregate: uint32(F + 1), } subscriber := remote.NewTriggerSubscriber(config, capInfo, capDonInfo, capabilities.DON{}, nil, agg, lggr) // register trigger - req := capabilities.CapabilityRequest{ + req := capabilities.TriggerRegistrationRequest{ Metadata: capabilities.RequestMetadata{ WorkflowID: workflowID, }, @@ -131,7 +131,7 @@ func TestStreamsTrigger(t *testing.T) { } response := <-triggerEventCallbackCh - validateLatestReports(t, response.Value, P, basePrice+R-1, baseTimestamp+R-1) + validateLatestReports(t, response.Event.Outputs, P, basePrice+R-1, baseTimestamp+R-1) } totalTime := time.Now().UnixMilli() - startTs lggr.Infow("elapsed", "totalMs", totalTime, "processingMs", processingTime) @@ -180,24 +180,20 @@ func newFeedsWithSignedReports(t *testing.T, nodes []node, N, P, R int) []feed { } func newTriggerEvent(t *testing.T, reportList []datastreams.FeedReport, triggerEventID string, sender ragetypes.PeerID) *remotetypes.MessageBody { - val, err := values.Wrap(reportList) + outputs, err := values.WrapMap(&datastreams.StreamsTriggerEvent{ + Timestamp: 10, + Payload: reportList, + }) require.NoError(t, err) triggerEvent := capabilities.TriggerEvent{ TriggerType: triggerID, ID: triggerEventID, - Timestamp: strconv.FormatInt(1000, 10), - Metadata: nil, - Payload: val, + Outputs: outputs, } - eventVal, err := values.WrapMap(triggerEvent) - require.NoError(t, err) + marshaled, err := pb.MarshalTriggerResponse(capabilities.TriggerResponse{Event: triggerEvent}) - marshaled, err := pb.MarshalCapabilityResponse( - capabilities.CapabilityResponse{ - Value: eventVal, - }) require.NoError(t, err) msg := &remotetypes.MessageBody{ Sender: sender[:], @@ -214,13 +210,11 @@ func newTriggerEvent(t *testing.T, reportList []datastreams.FeedReport, triggerE } func validateLatestReports(t *testing.T, wrapped values.Value, expectedFeedsLen int, expectedPrice int, expectedTimestamp int) { - triggerEvent := capabilities.TriggerEvent{} + triggerEvent := datastreams.StreamsTriggerEvent{} require.NoError(t, wrapped.UnwrapTo(&triggerEvent)) - reports := []datastreams.FeedReport{} - require.NoError(t, triggerEvent.Payload.UnwrapTo(&reports)) - require.Equal(t, expectedFeedsLen, len(reports)) + require.Equal(t, expectedFeedsLen, len(triggerEvent.Payload)) priceBig := big.NewInt(int64(expectedPrice)) - for _, report := range reports { + for _, report := range triggerEvent.Payload { require.Equal(t, priceBig.Bytes(), report.BenchmarkPrice) require.Equal(t, int64(expectedTimestamp), report.ObservationTimestamp) } diff --git a/core/capabilities/targets/write_target.go b/core/capabilities/targets/write_target.go index 330f15872d..eb3a390de4 100644 --- a/core/capabilities/targets/write_target.go +++ b/core/capabilities/targets/write_target.go @@ -1,7 +1,9 @@ package targets import ( + "bytes" "context" + "encoding/binary" "encoding/hex" "fmt" "math/big" @@ -11,23 +13,21 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/capabilities" "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/types/query/primitives" - "github.com/smartcontractkit/chainlink-common/pkg/values" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) var ( _ capabilities.ActionCapability = &WriteTarget{} ) -// required field of target's config in the workflow spec -const signedReportField = "signed_report" - type WriteTarget struct { cr commontypes.ContractReader cw commontypes.ChainWriter forwarderAddress string + // The minimum amount of gas that the receiver contract must get to process the forwarder report + receiverGasMinimum uint64 capabilities.CapabilityInfo lggr logger.Logger @@ -35,37 +35,139 @@ type WriteTarget struct { bound bool } -func NewWriteTarget(lggr logger.Logger, id string, cr commontypes.ContractReader, cw commontypes.ChainWriter, forwarderAddress string) *WriteTarget { +type TransmissionInfo struct { + GasLimit *big.Int + InvalidReceiver bool + State uint8 + Success bool + TransmissionId [32]byte + Transmitter common.Address +} + +// The gas cost of the forwarder contract logic, including state updates and event emission. +// This is a rough estimate and should be updated if the forwarder contract logic changes. +// TODO: Make this part of the on-chain capability configuration +const FORWARDER_CONTRACT_LOGIC_GAS_COST = 100_000 + +func NewWriteTarget(lggr logger.Logger, id string, cr commontypes.ContractReader, cw commontypes.ChainWriter, forwarderAddress string, txGasLimit uint64) *WriteTarget { info := capabilities.MustNewCapabilityInfo( id, capabilities.CapabilityTypeTarget, "Write target.", ) - logger := lggr.Named("WriteTarget") - return &WriteTarget{ cr, cw, forwarderAddress, + txGasLimit - FORWARDER_CONTRACT_LOGIC_GAS_COST, info, - logger, + logger.Named(lggr, "WriteTarget"), false, } } -type EvmConfig struct { +// Note: This should be a shared type that the OCR3 package validates as well +type ReportV1Metadata struct { + Version uint8 + WorkflowExecutionID [32]byte + Timestamp uint32 + DonID uint32 + DonConfigVersion uint32 + WorkflowCID [32]byte + WorkflowName [10]byte + WorkflowOwner [20]byte + ReportID [2]byte +} + +func (rm ReportV1Metadata) Encode() ([]byte, error) { + buf := new(bytes.Buffer) + err := binary.Write(buf, binary.BigEndian, rm) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func (rm ReportV1Metadata) Length() int { + bytes, err := rm.Encode() + if err != nil { + return 0 + } + return len(bytes) +} + +func decodeReportMetadata(data []byte) (metadata ReportV1Metadata, err error) { + if len(data) < metadata.Length() { + return metadata, fmt.Errorf("data too short: %d bytes", len(data)) + } + return metadata, binary.Read(bytes.NewReader(data[:metadata.Length()]), binary.BigEndian, &metadata) +} + +type Config struct { + // Address of the contract that will get the forwarded report Address string + // Optional gas limit that overrides the default limit sent to the chain writer + GasLimit *uint64 +} + +type Inputs struct { + SignedReport types.SignedReport +} + +type Request struct { + Metadata capabilities.RequestMetadata + Config Config + Inputs Inputs } -func parseConfig(rawConfig *values.Map) (config EvmConfig, err error) { - if err := rawConfig.UnwrapTo(&config); err != nil { - return config, err +func evaluate(rawRequest capabilities.CapabilityRequest) (r Request, err error) { + r.Metadata = rawRequest.Metadata + + if rawRequest.Config == nil { + return r, fmt.Errorf("missing config field") } - if !common.IsHexAddress(config.Address) { - return config, fmt.Errorf("'%v' is not a valid address", config.Address) + + if err = rawRequest.Config.UnwrapTo(&r.Config); err != nil { + return r, err + } + + if !common.IsHexAddress(r.Config.Address) { + return r, fmt.Errorf("'%v' is not a valid address", r.Config.Address) } - return config, nil + + if rawRequest.Inputs == nil { + return r, fmt.Errorf("missing inputs field") + } + + // required field of target's config in the workflow spec + const signedReportField = "signed_report" + signedReport, ok := rawRequest.Inputs.Underlying[signedReportField] + if !ok { + return r, fmt.Errorf("missing required field %s", signedReportField) + } + + if err = signedReport.UnwrapTo(&r.Inputs.SignedReport); err != nil { + return r, err + } + + reportMetadata, err := decodeReportMetadata(r.Inputs.SignedReport.Report) + if err != nil { + return r, err + } + + if reportMetadata.Version != 1 { + return r, fmt.Errorf("unsupported report version: %d", reportMetadata.Version) + } + + if hex.EncodeToString(reportMetadata.WorkflowExecutionID[:]) != rawRequest.Metadata.WorkflowExecutionID || + hex.EncodeToString(reportMetadata.WorkflowOwner[:]) != rawRequest.Metadata.WorkflowOwner || + hex.EncodeToString(reportMetadata.WorkflowName[:]) != rawRequest.Metadata.WorkflowName || + hex.EncodeToString(reportMetadata.WorkflowCID[:]) != rawRequest.Metadata.WorkflowID { + return r, fmt.Errorf("report metadata does not match request metadata. reportMetadata: %+v, requestMetadata: %+v", reportMetadata, rawRequest.Metadata) + } + + return r, nil } func success() <-chan capabilities.CapabilityResponse { @@ -77,7 +179,7 @@ func success() <-chan capabilities.CapabilityResponse { return callback } -func (cap *WriteTarget) Execute(ctx context.Context, request capabilities.CapabilityRequest) (<-chan capabilities.CapabilityResponse, error) { +func (cap *WriteTarget) Execute(ctx context.Context, rawRequest capabilities.CapabilityRequest) (<-chan capabilities.CapabilityResponse, error) { // Bind to the contract address on the write path. // Bind() requires a connection to the node's RPCs and // cannot be run during initialization. @@ -93,54 +195,57 @@ func (cap *WriteTarget) Execute(ctx context.Context, request capabilities.Capabi cap.bound = true } - cap.lggr.Debugw("Execute", "request", request) + cap.lggr.Debugw("Execute", "rawRequest", rawRequest) - reqConfig, err := parseConfig(request.Config) + request, err := evaluate(rawRequest) if err != nil { return nil, err } - signedReport, ok := request.Inputs.Underlying[signedReportField] - if !ok { - return nil, fmt.Errorf("missing required field %s", signedReportField) - } - - inputs := types.SignedReport{} - if err = signedReport.UnwrapTo(&inputs); err != nil { - return nil, err - } - - if len(inputs.Report) == 0 { - // We received any empty report -- this means we should skip transmission. - cap.lggr.Debugw("Skipping empty report", "request", request) - return success(), nil - } - // TODO: validate encoded report is prefixed with workflowID and executionID that match the request meta - rawExecutionID, err := hex.DecodeString(request.Metadata.WorkflowExecutionID) if err != nil { return nil, err } + // Check whether value was already transmitted on chain queryInputs := struct { Receiver string WorkflowExecutionID []byte ReportId []byte }{ - Receiver: reqConfig.Address, + Receiver: request.Config.Address, WorkflowExecutionID: rawExecutionID, - ReportId: inputs.ID, + ReportId: request.Inputs.SignedReport.ID, } - var transmitter common.Address - if err = cap.cr.GetLatestValue(ctx, "forwarder", "getTransmitter", primitives.Unconfirmed, queryInputs, &transmitter); err != nil { - return nil, fmt.Errorf("failed to getTransmitter latest value: %w", err) + var transmissionInfo TransmissionInfo + if err = cap.cr.GetLatestValue(ctx, "forwarder", "getTransmissionInfo", primitives.Unconfirmed, queryInputs, &transmissionInfo); err != nil { + return nil, fmt.Errorf("failed to getTransmissionInfo latest value: %w", err) } - if transmitter != common.HexToAddress("0x0") { - cap.lggr.Infow("WriteTarget report already onchain - returning without a tranmission attempt", "executionID", request.Metadata.WorkflowExecutionID) + + switch { + case transmissionInfo.State == 0: // NOT_ATTEMPTED + cap.lggr.Infow("non-empty report - transmission not attempted - attempting to push to txmgr", "request", request, "reportLen", len(request.Inputs.SignedReport.Report), "reportContextLen", len(request.Inputs.SignedReport.Context), "nSignatures", len(request.Inputs.SignedReport.Signatures), "executionID", request.Metadata.WorkflowExecutionID) + case transmissionInfo.State == 1: // SUCCEEDED + cap.lggr.Infow("returning without a transmission attempt - report already onchain ", "executionID", request.Metadata.WorkflowExecutionID) + return success(), nil + case transmissionInfo.State == 2: // INVALID_RECEIVER + cap.lggr.Infow("returning without a transmission attempt - transmission already attempted, receiver was marked as invalid", "executionID", request.Metadata.WorkflowExecutionID) return success(), nil + case transmissionInfo.State == 3: // FAILED + receiverGasMinimum := cap.receiverGasMinimum + if request.Config.GasLimit != nil { + receiverGasMinimum = *request.Config.GasLimit - FORWARDER_CONTRACT_LOGIC_GAS_COST + } + if transmissionInfo.GasLimit.Uint64() > receiverGasMinimum { + cap.lggr.Infow("returning without a transmission attempt - transmission already attempted and failed, sufficient gas was provided", "executionID", request.Metadata.WorkflowExecutionID, "receiverGasMinimum", receiverGasMinimum, "transmissionGasLimit", transmissionInfo.GasLimit) + return success(), nil + } else { + cap.lggr.Infow("non-empty report - retrying a failed transmission - attempting to push to txmgr", "request", request, "reportLen", len(request.Inputs.SignedReport.Report), "reportContextLen", len(request.Inputs.SignedReport.Context), "nSignatures", len(request.Inputs.SignedReport.Signatures), "executionID", request.Metadata.WorkflowExecutionID, "receiverGasMinimum", receiverGasMinimum, "transmissionGasLimit", transmissionInfo.GasLimit) + } + default: + return nil, fmt.Errorf("unexpected transmission state: %v", transmissionInfo.State) } - cap.lggr.Infow("WriteTarget non-empty report - attempting to push to txmgr", "request", request, "reportLen", len(inputs.Report), "reportContextLen", len(inputs.Context), "nSignatures", len(inputs.Signatures), "executionID", request.Metadata.WorkflowExecutionID) txID, err := uuid.NewUUID() // NOTE: CW expects us to generate an ID, rather than return one if err != nil { return nil, err @@ -154,7 +259,7 @@ func (cap *WriteTarget) Execute(ctx context.Context, request capabilities.Capabi RawReport []byte ReportContext []byte Signatures [][]byte - }{reqConfig.Address, inputs.Report, inputs.Context, inputs.Signatures} + }{request.Config.Address, request.Inputs.SignedReport.Report, request.Inputs.SignedReport.Context, request.Inputs.SignedReport.Signatures} if req.RawReport == nil { req.RawReport = make([]byte, 0) @@ -170,10 +275,22 @@ func (cap *WriteTarget) Execute(ctx context.Context, request capabilities.Capabi cap.lggr.Debugw("Transaction raw report", "report", hex.EncodeToString(req.RawReport)) meta := commontypes.TxMeta{WorkflowExecutionID: &request.Metadata.WorkflowExecutionID} + if request.Config.GasLimit != nil { + meta.GasLimit = new(big.Int).SetUint64(*request.Config.GasLimit) + } + value := big.NewInt(0) if err := cap.cw.SubmitTransaction(ctx, "forwarder", "report", req, txID.String(), cap.forwarderAddress, &meta, value); err != nil { - return nil, err + if commontypes.ErrSettingTransactionGasLimitNotSupported.Is(err) { + meta.GasLimit = nil + if err := cap.cw.SubmitTransaction(ctx, "forwarder", "report", req, txID.String(), cap.forwarderAddress, &meta, value); err != nil { + return nil, fmt.Errorf("failed to submit transaction: %w", err) + } + } else { + return nil, fmt.Errorf("failed to submit transaction: %w", err) + } } + cap.lggr.Debugw("Transaction submitted", "request", request, "transaction", txID) return success(), nil } diff --git a/core/capabilities/targets/write_target_test.go b/core/capabilities/targets/write_target_test.go index e118433177..9f7b6a2de2 100644 --- a/core/capabilities/targets/write_target_test.go +++ b/core/capabilities/targets/write_target_test.go @@ -2,7 +2,9 @@ package targets_test import ( "context" + "encoding/hex" "errors" + "math/big" "testing" "github.com/ethereum/go-ethereum/common" @@ -29,7 +31,7 @@ func TestWriteTarget(t *testing.T) { forwarderA := testutils.NewAddress() forwarderAddr := forwarderA.Hex() - writeTarget := targets.NewWriteTarget(lggr, "test-write-target@1.0.0", cr, cw, forwarderAddr) + writeTarget := targets.NewWriteTarget(lggr, "test-write-target@1.0.0", cr, cw, forwarderAddr, 400_000) require.NotNil(t, writeTarget) config, err := values.NewMap(map[string]any{ @@ -37,14 +39,36 @@ func TestWriteTarget(t *testing.T) { }) require.NoError(t, err) + reportMetadata := targets.ReportV1Metadata{ + Version: 1, + WorkflowExecutionID: [32]byte{}, + Timestamp: 0, + DonID: 0, + DonConfigVersion: 0, + WorkflowCID: [32]byte{}, + WorkflowName: [10]byte{}, + WorkflowOwner: [20]byte{}, + ReportID: [2]byte{}, + } + + reportMetadataBytes, err := reportMetadata.Encode() + require.NoError(t, err) + validInputs, err := values.NewMap(map[string]any{ "signed_report": map[string]any{ - "report": []byte{1, 2, 3}, + "report": reportMetadataBytes, "signatures": [][]byte{}, }, }) require.NoError(t, err) + validMetadata := capabilities.RequestMetadata{ + WorkflowID: hex.EncodeToString(reportMetadata.WorkflowCID[:]), + WorkflowOwner: hex.EncodeToString(reportMetadata.WorkflowOwner[:]), + WorkflowName: hex.EncodeToString(reportMetadata.WorkflowName[:]), + WorkflowExecutionID: hex.EncodeToString(reportMetadata.WorkflowExecutionID[:]), + } + cr.On("Bind", mock.Anything, []types.BoundContract{ { Address: forwarderAddr, @@ -52,20 +76,25 @@ func TestWriteTarget(t *testing.T) { }, }).Return(nil) - cr.On("GetLatestValue", mock.Anything, "forwarder", "getTransmitter", mock.Anything, mock.Anything, mock.Anything).Return(nil).Run(func(args mock.Arguments) { - transmitter := args.Get(5).(*common.Address) - *transmitter = common.HexToAddress("0x0") - }).Once() + cr.On("GetLatestValue", mock.Anything, "forwarder", "getTransmissionInfo", mock.Anything, mock.Anything, mock.Anything).Return(nil).Run(func(args mock.Arguments) { + transmissionInfo := args.Get(5).(*targets.TransmissionInfo) + *transmissionInfo = targets.TransmissionInfo{ + GasLimit: big.NewInt(0), + InvalidReceiver: false, + State: 0, + Success: false, + TransmissionId: [32]byte{}, + Transmitter: common.HexToAddress("0x0"), + } + }) cw.On("SubmitTransaction", mock.Anything, "forwarder", "report", mock.Anything, mock.Anything, forwarderAddr, mock.Anything, mock.Anything).Return(nil).Once() t.Run("succeeds with valid report", func(t *testing.T) { req := capabilities.CapabilityRequest{ - Metadata: capabilities.RequestMetadata{ - WorkflowID: "test-id", - }, - Config: config, - Inputs: validInputs, + Metadata: validMetadata, + Config: config, + Inputs: validInputs, } ch, err2 := writeTarget.Execute(ctx, req) @@ -74,62 +103,84 @@ func TestWriteTarget(t *testing.T) { require.NotNil(t, response) }) - t.Run("succeeds with empty report", func(t *testing.T) { - emptyInputs, err2 := values.NewMap(map[string]any{ - "signed_report": map[string]any{ - "report": []byte{}, - }, - "signatures": [][]byte{}, - }) + t.Run("fails when ChainWriter's SubmitTransaction returns error", func(t *testing.T) { + req := capabilities.CapabilityRequest{ + Metadata: validMetadata, + Config: config, + Inputs: validInputs, + } + cw.On("SubmitTransaction", mock.Anything, "forwarder", "report", mock.Anything, mock.Anything, forwarderAddr, mock.Anything, mock.Anything).Return(errors.New("writer error")) - require.NoError(t, err2) + _, err = writeTarget.Execute(ctx, req) + require.Error(t, err) + }) + + t.Run("passes gas limit set on config to the chain writer", func(t *testing.T) { + configGasLimit, err := values.NewMap(map[string]any{ + "Address": forwarderAddr, + "GasLimit": 500000, + }) + require.NoError(t, err) req := capabilities.CapabilityRequest{ - Metadata: capabilities.RequestMetadata{ - WorkflowExecutionID: "test-id", - }, - Config: config, - Inputs: emptyInputs, + Metadata: validMetadata, + Config: configGasLimit, + Inputs: validInputs, } - ch, err2 := writeTarget.Execute(ctx, req) - require.NoError(t, err2) - response := <-ch - require.Nil(t, response.Value) + meta := types.TxMeta{WorkflowExecutionID: &req.Metadata.WorkflowExecutionID, GasLimit: big.NewInt(500000)} + cw.On("SubmitTransaction", mock.Anything, "forwarder", "report", mock.Anything, mock.Anything, forwarderAddr, &meta, mock.Anything).Return(types.ErrSettingTransactionGasLimitNotSupported) + + _, err2 := writeTarget.Execute(ctx, req) + require.Error(t, err2) }) - t.Run("fails when ChainReader's GetLatestValue returns error", func(t *testing.T) { + t.Run("retries without gas limit when ChainWriter's SubmitTransaction returns error due to gas limit not supported", func(t *testing.T) { + configGasLimit, err := values.NewMap(map[string]any{ + "Address": forwarderAddr, + "GasLimit": 500000, + }) + require.NoError(t, err) req := capabilities.CapabilityRequest{ - Metadata: capabilities.RequestMetadata{ - WorkflowID: "test-id", - }, - Config: config, - Inputs: validInputs, + Metadata: validMetadata, + Config: configGasLimit, + Inputs: validInputs, } - cr.On("GetLatestValue", mock.Anything, "forwarder", "getTransmitter", mock.Anything, mock.Anything, mock.Anything).Return(errors.New("reader error")) - _, err = writeTarget.Execute(ctx, req) - require.Error(t, err) + meta := types.TxMeta{WorkflowExecutionID: &req.Metadata.WorkflowExecutionID, GasLimit: big.NewInt(500000)} + cw.On("SubmitTransaction", mock.Anything, "forwarder", "report", mock.Anything, mock.Anything, forwarderAddr, &meta, mock.Anything).Return(types.ErrSettingTransactionGasLimitNotSupported) + meta = types.TxMeta{WorkflowExecutionID: &req.Metadata.WorkflowExecutionID} + cw.On("SubmitTransaction", mock.Anything, "forwarder", "report", mock.Anything, mock.Anything, forwarderAddr, &meta, mock.Anything).Return(nil) + + configGasLimit, err = values.NewMap(map[string]any{ + "Address": forwarderAddr, + }) + req = capabilities.CapabilityRequest{ + Metadata: validMetadata, + Config: configGasLimit, + Inputs: validInputs, + } + + _, err2 := writeTarget.Execute(ctx, req) + require.Error(t, err2) }) - t.Run("fails when ChainWriter's SubmitTransaction returns error", func(t *testing.T) { + t.Run("fails when ChainReader's GetLatestValue returns error", func(t *testing.T) { req := capabilities.CapabilityRequest{ - Metadata: capabilities.RequestMetadata{ - WorkflowID: "test-id", - }, - Config: config, - Inputs: validInputs, + Metadata: validMetadata, + Config: config, + Inputs: validInputs, } - cw.On("SubmitTransaction", mock.Anything, "forwarder", "report", mock.Anything, mock.Anything, forwarderAddr, mock.Anything, mock.Anything).Return(errors.New("writer error")) + cr.On("GetLatestValue", mock.Anything, "forwarder", "getTransmissionInfo", mock.Anything, mock.Anything, mock.Anything).Return(errors.New("reader error")) _, err = writeTarget.Execute(ctx, req) require.Error(t, err) }) t.Run("fails with invalid config", func(t *testing.T) { - invalidConfig, err := values.NewMap(map[string]any{ + invalidConfig, err2 := values.NewMap(map[string]any{ "Address": "invalid-address", }) - require.NoError(t, err) + require.NoError(t, err2) req := capabilities.CapabilityRequest{ Metadata: capabilities.RequestMetadata{ @@ -138,7 +189,27 @@ func TestWriteTarget(t *testing.T) { Config: invalidConfig, Inputs: validInputs, } - _, err = writeTarget.Execute(ctx, req) - require.Error(t, err) + _, err2 = writeTarget.Execute(ctx, req) + require.Error(t, err2) + }) + + t.Run("fails with nil config", func(t *testing.T) { + req := capabilities.CapabilityRequest{ + Metadata: validMetadata, + Config: nil, + Inputs: validInputs, + } + _, err2 := writeTarget.Execute(ctx, req) + require.Error(t, err2) + }) + + t.Run("fails with nil inputs", func(t *testing.T) { + req := capabilities.CapabilityRequest{ + Metadata: validMetadata, + Config: config, + Inputs: nil, + } + _, err2 := writeTarget.Execute(ctx, req) + require.Error(t, err2) }) } diff --git a/core/capabilities/transmission/local_target_capability_test.go b/core/capabilities/transmission/local_target_capability_test.go index 93bf708cce..cdca854986 100644 --- a/core/capabilities/transmission/local_target_capability_test.go +++ b/core/capabilities/transmission/local_target_capability_test.go @@ -54,8 +54,8 @@ func TestScheduledExecutionStrategy_LocalDON(t *testing.T) { name: "position 0; oneAtATime", position: 0, schedule: "oneAtATime", - low: 300 * time.Millisecond, - high: 400 * time.Millisecond, + low: 200 * time.Millisecond, + high: 300 * time.Millisecond, }, { name: "position 1; oneAtATime", @@ -68,15 +68,15 @@ func TestScheduledExecutionStrategy_LocalDON(t *testing.T) { name: "position 2; oneAtATime", position: 2, schedule: "oneAtATime", - low: 0 * time.Millisecond, - high: 100 * time.Millisecond, + low: 300 * time.Millisecond, + high: 400 * time.Millisecond, }, { name: "position 3; oneAtATime", position: 3, schedule: "oneAtATime", - low: 100 * time.Millisecond, - high: 300 * time.Millisecond, + low: 0 * time.Millisecond, + high: 100 * time.Millisecond, }, { name: "position 0; allAtOnce", @@ -121,8 +121,8 @@ func TestScheduledExecutionStrategy_LocalDON(t *testing.T) { req := capabilities.CapabilityRequest{ Config: m, Metadata: capabilities.RequestMetadata{ - WorkflowID: "mock-workflow-id", - WorkflowExecutionID: "mock-execution-id-1", + WorkflowID: "15c631d295ef5e32deb99a10ee6804bc4af13855687559d7ff6552ac6dbb2ce0", + WorkflowExecutionID: "32c631d295ef5e32deb99a10ee6804bc4af13855687559d7ff6552ac6dbb2ce1", }, } diff --git a/core/capabilities/transmission/transmission.go b/core/capabilities/transmission/transmission.go index b41be5bcaa..88ce0fa3ed 100644 --- a/core/capabilities/transmission/transmission.go +++ b/core/capabilities/transmission/transmission.go @@ -4,10 +4,10 @@ import ( "fmt" "time" - "github.com/pkg/errors" - "github.com/smartcontractkit/libocr/permutation" + "github.com/smartcontractkit/chainlink/v2/core/capabilities/validation" + "github.com/smartcontractkit/chainlink-common/pkg/capabilities" "github.com/smartcontractkit/chainlink-common/pkg/values" "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" @@ -56,8 +56,12 @@ func GetPeerIDToTransmissionDelay(donPeerIDs []types.PeerID, req capabilities.Ca return nil, fmt.Errorf("failed to extract transmission config from request: %w", err) } - if req.Metadata.WorkflowID == "" || req.Metadata.WorkflowExecutionID == "" { - return nil, errors.New("workflow ID and workflow execution ID must be set in request metadata") + if err = validation.ValidateWorkflowOrExecutionID(req.Metadata.WorkflowID); err != nil { + return nil, fmt.Errorf("workflow ID is invalid: %w", err) + } + + if err = validation.ValidateWorkflowOrExecutionID(req.Metadata.WorkflowExecutionID); err != nil { + return nil, fmt.Errorf("workflow execution ID is invalid: %w", err) } transmissionID := req.Metadata.WorkflowID + req.Metadata.WorkflowExecutionID diff --git a/core/capabilities/transmission/transmission_test.go b/core/capabilities/transmission/transmission_test.go index fba233eadb..aaa367e78c 100644 --- a/core/capabilities/transmission/transmission_test.go +++ b/core/capabilities/transmission/transmission_test.go @@ -36,20 +36,21 @@ func Test_GetPeerIDToTransmissionDelay(t *testing.T) { "one", "oneAtATime", "100ms", - "mock-execution-id", + "15c631d295ef5e32deb99a10ee6804bc4af13855687559d7ff6552ac6dbb2ce0", map[string]time.Duration{ "one": 300 * time.Millisecond, - "two": 100 * time.Millisecond, - "three": 0 * time.Millisecond, + "two": 0 * time.Millisecond, + "three": 100 * time.Millisecond, "four": 200 * time.Millisecond, }, }, + { "TestAllAtOnce", "one", "allAtOnce", "100ms", - "mock-execution-id", + "15c631d295ef5e32deb99a10ee6804bc4af13855687559d7ff6552ac6dbb2ce0", map[string]time.Duration{ "one": 0 * time.Millisecond, "two": 0 * time.Millisecond, @@ -57,17 +58,18 @@ func Test_GetPeerIDToTransmissionDelay(t *testing.T) { "four": 0 * time.Millisecond, }, }, + { "TestOneAtATimeWithDifferentExecutionID", "one", "oneAtATime", "100ms", - "mock-execution-id2", + "16c631d295ef5e32deb99a10ee6804bc4af13855687559d7ff6552ac6dbb2ce1", map[string]time.Duration{ - "one": 0 * time.Millisecond, - "two": 200 * time.Millisecond, - "three": 100 * time.Millisecond, - "four": 300 * time.Millisecond, + "one": 300 * time.Millisecond, + "two": 100 * time.Millisecond, + "three": 200 * time.Millisecond, + "four": 0 * time.Millisecond, }, }, } @@ -83,7 +85,7 @@ func Test_GetPeerIDToTransmissionDelay(t *testing.T) { capabilityRequest := capabilities.CapabilityRequest{ Config: transmissionCfg, Metadata: capabilities.RequestMetadata{ - WorkflowID: "mock-workflow-id", + WorkflowID: "17c631d295ef5e32deb99a10ee6804bc4af13855687559d7ff6552ac6dbb2ce0", WorkflowExecutionID: tc.workflowExecutionID, }, } diff --git a/core/capabilities/validation/validation.go b/core/capabilities/validation/validation.go new file mode 100644 index 0000000000..67ee3a504c --- /dev/null +++ b/core/capabilities/validation/validation.go @@ -0,0 +1,38 @@ +package validation + +import ( + "encoding/hex" + "errors" + "unicode" +) + +const ( + validWorkflowIDLen = 64 + maxIDLen = 128 +) + +// Workflow IDs and Execution IDs are 32-byte hex-encoded strings +func ValidateWorkflowOrExecutionID(id string) error { + if len(id) != validWorkflowIDLen { + return errors.New("must be 32 bytes long") + } + _, err := hex.DecodeString(id) + if err != nil { + return errors.New("must be a hex-encoded string") + } + + return nil +} + +// Trigger event IDs and message IDs can only contain printable characters and must be non-empty +func IsValidID(id string) bool { + if len(id) == 0 || len(id) > maxIDLen { + return false + } + for i := 0; i < len(id); i++ { + if !unicode.IsPrint(rune(id[i])) { + return false + } + } + return true +} diff --git a/core/capabilities/validation/validation_test.go b/core/capabilities/validation/validation_test.go new file mode 100644 index 0000000000..205898652f --- /dev/null +++ b/core/capabilities/validation/validation_test.go @@ -0,0 +1,19 @@ +package validation + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestIsValidWorkflowID(t *testing.T) { + require.NotNil(t, ValidateWorkflowOrExecutionID("too_short")) + require.NotNil(t, ValidateWorkflowOrExecutionID("nothex--95ef5e32deb99a10ee6804bc4af13855687559d7ff6552ac6dbb2ce0")) + require.NoError(t, ValidateWorkflowOrExecutionID("15c631d295ef5e32deb99a10ee6804bc4af13855687559d7ff6552ac6dbb2ce0")) +} + +func TestIsValidTriggerEventID(t *testing.T) { + require.False(t, IsValidID("")) + require.False(t, IsValidID("\n\n")) + require.True(t, IsValidID("id_id_2")) +} diff --git a/core/chainlink.goreleaser.Dockerfile b/core/chainlink.goreleaser.Dockerfile index 5d172fd77e..c35fe015cb 100644 --- a/core/chainlink.goreleaser.Dockerfile +++ b/core/chainlink.goreleaser.Dockerfile @@ -20,8 +20,9 @@ COPY ./chainlink /usr/local/bin/ # Copy native libs if cgo is enabled COPY ./tmp/linux_${TARGETARCH}/libs /usr/local/bin/libs -# Copy plugins and enable them -COPY ./tmp/linux_${TARGETARCH}/plugins/* /usr/local/bin/ +# Copy plugins if exist and enable them +# https://stackoverflow.com/questions/70096208/dockerfile-copy-folder-if-it-exists-conditional-copy/70096420#70096420 +COPY ./tmp/linux_${TARGETARCH}/plugin[s] /usr/local/bin/ # Allow individual plugins to be enabled by supplying their path ARG CL_MEDIAN_CMD ARG CL_MERCURY_CMD diff --git a/core/chains/evm/client/errors.go b/core/chains/evm/client/errors.go index ff4bd15a7b..6882cca524 100644 --- a/core/chains/evm/client/errors.go +++ b/core/chains/evm/client/errors.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rpc" pkgerrors "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -62,6 +63,7 @@ const ( Fatal ServiceUnavailable TerminallyStuck + TooManyResults ) type ClientErrors map[int]*regexp.Regexp @@ -298,6 +300,7 @@ func ClientErrorRegexes(errsRegex config.ClientErrors) *ClientErrors { TransactionAlreadyMined: regexp.MustCompile(errsRegex.TransactionAlreadyMined()), Fatal: regexp.MustCompile(errsRegex.Fatal()), ServiceUnavailable: regexp.MustCompile(errsRegex.ServiceUnavailable()), + TooManyResults: regexp.MustCompile(errsRegex.TooManyResults()), } } @@ -457,6 +460,11 @@ func isFatalSendError(err error) bool { return false } +var ( + _ rpc.Error = JsonError{} + _ rpc.DataError = JsonError{} +) + // go-ethereum@v1.10.0/rpc/json.go type JsonError struct { Code int `json:"code"` @@ -471,7 +479,17 @@ func (err JsonError) Error() string { return err.Message } -func (err *JsonError) String() string { +// To satisfy rpc.Error interface +func (err JsonError) ErrorCode() int { + return err.Code +} + +// To satisfy rpc.DataError +func (err JsonError) ErrorData() interface{} { + return err.Data +} + +func (err JsonError) String() string { return fmt.Sprintf("json-rpc error { Code = %d, Message = '%s', Data = '%v' }", err.Code, err.Message, err.Data) } @@ -610,3 +628,88 @@ func ClassifySendError(err error, clientErrors config.ClientErrors, lggr logger. lggr.Criticalw("Unknown error encountered when sending transaction", "err", err, "etx", tx) return commonclient.Unknown } + +var infura = ClientErrors{ + TooManyResults: regexp.MustCompile(`(: |^)query returned more than [0-9]+ results. Try with this block range \[0x[0-9A-F]+, 0x[0-9A-F]+\].$`), +} + +var alchemy = ClientErrors{ + TooManyResults: regexp.MustCompile(`(: |^)Log response size exceeded. You can make eth_getLogs requests with up to a [0-9A-Z]+ block range and no limit on the response size, or you can request any block range with a cap of [0-9A-Z]+ logs in the response. Based on your parameters and the response size limit, this block range should work: \[0x[0-9a-f]+, 0x[0-9a-f]+\]$`), +} + +var quicknode = ClientErrors{ + TooManyResults: regexp.MustCompile(`(: |^)eth_getLogs is limited to a [0-9,]+ range$`), +} + +var simplyvc = ClientErrors{ + TooManyResults: regexp.MustCompile(`too wide blocks range, the limit is [0-9,]+$`), +} + +var drpc = ClientErrors{ + TooManyResults: regexp.MustCompile(`(: |^)requested too many blocks from [0-9]+ to [0-9]+, maximum is set to [0-9,]+$`), +} + +// Linkpool, Blockdaemon, and Chainstack all return "request timed out" if the log results are too large for them to process +var defaultClient = ClientErrors{ + TooManyResults: regexp.MustCompile(`request timed out`), +} + +// JSON-RPC error codes which can indicate a refusal of the server to process an eth_getLogs request because the result set is too large +const ( + jsonRpcServerError = -32000 // Server error. SimplyVC uses this error code when too many results are returned + + // Server timeout. When the rpc server has its own limit on how long it can take to compile the results + // Examples: Linkpool, Chainstack, Block Daemon + jsonRpcTimedOut = -32002 + + // See: https://github.com/ethereum/go-ethereum/blob/master/rpc/errors.go#L63 + // Can occur if the rpc server is configured with a maximum byte limit on the response size of batch requests + jsonRpcResponseTooLarge = -32003 + + // Not implemented in geth by default, but is defined in EIP 1474 and implemented by infura and some other 3rd party rpc servers + // See: https://community.infura.io/t/getlogs-error-query-returned-more-than-1000-results/358/5 + jsonRpcLimitExceeded = -32005 // See also: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1474.md + + jsonRpcInvalidParams = -32602 // Invalid method params. Returned by alchemy if the block range is too large or there are too many results to return + + jsonRpcQuicknodeTooManyResults = -32614 // Undocumented error code used by Quicknode for too many results error +) + +func IsTooManyResults(err error, clientErrors config.ClientErrors) bool { + var rpcErr rpc.Error + + if !pkgerrors.As(err, &rpcErr) { + return false + } + configErrors := ClientErrorRegexes(clientErrors) + if configErrors.ErrIs(rpcErr, TooManyResults) { + return true + } + + switch rpcErr.ErrorCode() { + case jsonRpcResponseTooLarge: + return true + case jsonRpcLimitExceeded: + if infura.ErrIs(rpcErr, TooManyResults) { + return true + } + case jsonRpcInvalidParams: + if alchemy.ErrIs(rpcErr, TooManyResults) { + return true + } + case jsonRpcQuicknodeTooManyResults: + if quicknode.ErrIs(rpcErr, TooManyResults) { + return true + } + case jsonRpcTimedOut: + if defaultClient.ErrIs(rpcErr, TooManyResults) { + return true + } + case jsonRpcServerError: + if simplyvc.ErrIs(rpcErr, TooManyResults) || + drpc.ErrIs(rpcErr, TooManyResults) { + return true + } + } + return false +} diff --git a/core/chains/evm/client/errors_test.go b/core/chains/evm/client/errors_test.go index 49543b16e9..7d11279d32 100644 --- a/core/chains/evm/client/errors_test.go +++ b/core/chains/evm/client/errors_test.go @@ -1,7 +1,9 @@ package client_test import ( + "encoding/json" "errors" + "fmt" "testing" pkgerrors "github.com/pkg/errors" @@ -167,6 +169,7 @@ func Test_Eth_Errors(t *testing.T) { {"max fee per gas less than block base fee", true, "zkSync"}, {"virtual machine entered unexpected state. please contact developers and provide transaction details that caused this error. Error description: The operator included transaction with an unacceptable gas price", true, "zkSync"}, {"client error terminally underpriced", true, "tomlConfig"}, + {"gas price less than block base fee", true, "aStar"}, } for _, test := range tests { @@ -317,8 +320,6 @@ func Test_Eth_Errors(t *testing.T) { {"failed to add tx to the pool: not enough step counters to continue the execution", true, "Xlayer"}, {"failed to add tx to the pool: not enough keccak counters to continue the execution", true, "zkEVM"}, {"failed to add tx to the pool: not enough keccak counters to continue the execution", true, "Xlayer"}, - {"RPC error response: failed to add tx to the pool: out of counters at node level (Steps)", true, "zkEVM"}, - {"RPC error response: failed to add tx to the pool: out of counters at node level (GasUsed, KeccakHashes, PoseidonHashes, PoseidonPaddings, MemAligns, Arithmetics, Binaries, Steps, Sha256Hashes)", true, "Xlayer"}, } for _, test := range tests { @@ -440,3 +441,86 @@ func Test_Config_Errors(t *testing.T) { assert.False(t, clientErrors.ErrIs(errors.New("some old bollocks"), evmclient.NonceTooLow)) }) } + +func Test_IsTooManyResultsError(t *testing.T) { + customErrors := evmclient.NewTestClientErrors() + + tests := []errorCase{ + {`{ + "code":-32602, + "message":"Log response size exceeded. You can make eth_getLogs requests with up to a 2K block range and no limit on the response size, or you can request any block range with a cap of 10K logs in the response. Based on your parameters and the response size limit, this block range should work: [0x0, 0x133e71]"}`, + true, + "alchemy", + }, {`{ + "code":-32005, + "data":{"from":"0xCB3D","limit":10000,"to":"0x7B737"}, + "message":"query returned more than 10000 results. Try with this block range [0xCB3D, 0x7B737]."}`, + true, + "infura", + }, {`{ + "code":-32002, + "message":"request timed out"}`, + true, + "LinkPool-Blockdaemon-Chainstack", + }, {`{ + "code":-32614, + "message":"eth_getLogs is limited to a 10,000 range"}`, + true, + "Quicknode", + }, {`{ + "code":-32000, + "message":"too wide blocks range, the limit is 100"}`, + true, + "SimplyVC", + }, {`{ + "message":"requested too many blocks from 0 to 16777216, maximum is set to 2048", + "code":-32000}`, + true, + "Drpc", + }, {` + + + + 503 Backend fetch failed + + +

Error 503 Backend fetch failed

+

Backend fetch failed

+

Guru Meditation:

+

XID: 343710611

+
+

Varnish cache server

+ +`, + false, + "Nirvana Labs"}, // This isn't an error response we can handle, but including for completeness. }, + + {`{ + "code":-32000", + "message":"unrelated server error"}`, + false, + "any", + }, {`{ + "code":-32500, + "message":"unrelated error code"}`, + false, + "any2", + }, {fmt.Sprintf(`{ + "code" : -43106, + "message" : "%s"}`, customErrors.TooManyResults()), + true, + "custom chain with error specified in toml config", + }, + } + + for _, test := range tests { + t.Run(test.network, func(t *testing.T) { + jsonRpcErr := evmclient.JsonError{} + err := json.Unmarshal([]byte(test.message), &jsonRpcErr) + if err == nil { + err = jsonRpcErr + } + assert.Equal(t, test.expect, evmclient.IsTooManyResults(err, &customErrors)) + }) + } +} diff --git a/core/chains/evm/client/helpers_test.go b/core/chains/evm/client/helpers_test.go index e996ccc5e4..67977b180e 100644 --- a/core/chains/evm/client/helpers_test.go +++ b/core/chains/evm/client/helpers_test.go @@ -34,6 +34,7 @@ type TestClientErrors struct { transactionAlreadyMined string fatal string serviceUnavailable string + tooManyResults string } func NewTestClientErrors() TestClientErrors { @@ -52,6 +53,7 @@ func NewTestClientErrors() TestClientErrors { transactionAlreadyMined: "client error transaction already mined", fatal: "client error fatal", serviceUnavailable: "client error service unavailable", + tooManyResults: "client error too many results", } } @@ -77,6 +79,7 @@ func (c *TestClientErrors) L2Full() string { return c.l2Full } func (c *TestClientErrors) TransactionAlreadyMined() string { return c.transactionAlreadyMined } func (c *TestClientErrors) Fatal() string { return c.fatal } func (c *TestClientErrors) ServiceUnavailable() string { return c.serviceUnavailable } +func (c *TestClientErrors) TooManyResults() string { return c.serviceUnavailable } type TestNodePoolConfig struct { NodePollFailureThreshold uint32 diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index 295e24f7c9..0752def994 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -550,14 +550,14 @@ func (r *rpcClient) SubscribeToHeads(ctx context.Context) (ch <-chan *evmtypes.H return channel, forwarder, err } -func (r *rpcClient) SubscribeToFinalizedHeads(_ context.Context) (<-chan *evmtypes.Head, commontypes.Subscription, error) { +func (r *rpcClient) SubscribeToFinalizedHeads(ctx context.Context) (<-chan *evmtypes.Head, commontypes.Subscription, error) { interval := r.finalizedBlockPollInterval if interval == 0 { return nil, nil, errors.New("FinalizedBlockPollInterval is 0") } timeout := interval poller, channel := commonclient.NewPoller[*evmtypes.Head](interval, r.LatestFinalizedBlock, timeout, r.rpcLog) - if err := poller.Start(); err != nil { + if err := poller.Start(ctx); err != nil { return nil, nil, err } return channel, &poller, nil diff --git a/core/chains/evm/client/rpc_client_test.go b/core/chains/evm/client/rpc_client_test.go index 07e097727a..1555e436e3 100644 --- a/core/chains/evm/client/rpc_client_test.go +++ b/core/chains/evm/client/rpc_client_test.go @@ -136,6 +136,33 @@ func TestRPCClient_SubscribeNewHead(t *testing.T) { server := testutils.NewWSServer(t, chainId, serverCallBack) wsURL := server.WSURL() + rpc := client.NewRPCClient(lggr, *wsURL, nil, "rpc", 1, chainId, commonclient.Primary, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") + defer rpc.Close() + require.NoError(t, rpc.Dial(ctx)) + var wg sync.WaitGroup + for i := 0; i < numberOfAttempts; i++ { + ch := make(chan *evmtypes.Head) + sub, err := rpc.SubscribeNewHead(tests.Context(t), ch) + require.NoError(t, err) + wg.Add(2) + go func() { + server.MustWriteBinaryMessageSync(t, makeNewHeadWSMessage(&evmtypes.Head{Number: 256, TotalDifficulty: big.NewInt(1000)})) + wg.Done() + }() + go func() { + rpc.UnsubscribeAllExceptAliveLoop() + sub.Unsubscribe() + wg.Done() + }() + wg.Wait() + } + + }) + t.Run("Concurrent Unsubscribe and onNewHead calls do not lead to a deadlock", func(t *testing.T) { + const numberOfAttempts = 1000 // need a large number to increase the odds of reproducing the issue + server := testutils.NewWSServer(t, chainId, serverCallBack) + wsURL := server.WSURL() + rpc := client.NewRPCClient(lggr, *wsURL, nil, "rpc", 1, chainId, commonclient.Primary, 0, commonclient.QueryTimeout, commonclient.QueryTimeout, "") defer rpc.Close() require.NoError(t, rpc.Dial(ctx)) diff --git a/core/chains/evm/client/simulated_backend_client.go b/core/chains/evm/client/simulated_backend_client.go index 3ec1bff577..6c569b16b8 100644 --- a/core/chains/evm/client/simulated_backend_client.go +++ b/core/chains/evm/client/simulated_backend_client.go @@ -419,7 +419,7 @@ func (c *SimulatedBackendClient) CallContract(ctx context.Context, msg ethereum. res, err := c.b.CallContract(ctx, msg, blockNumber) if err != nil { dataErr := revertError{} - if errors.Is(err, &dataErr) { + if errors.As(err, &dataErr) { return nil, &JsonError{Data: dataErr.ErrorData(), Message: dataErr.Error(), Code: 3} } // Generic revert, no data @@ -438,7 +438,7 @@ func (c *SimulatedBackendClient) PendingCallContract(ctx context.Context, msg et res, err := c.b.PendingCallContract(ctx, msg) if err != nil { dataErr := revertError{} - if errors.Is(err, &dataErr) { + if errors.As(err, &dataErr) { return nil, &JsonError{Data: dataErr.ErrorData(), Message: dataErr.Error(), Code: 3} } // Generic revert, no data diff --git a/core/chains/evm/config/chain_scoped_client_errors.go b/core/chains/evm/config/chain_scoped_client_errors.go index 53bb04846d..f9d2096e90 100644 --- a/core/chains/evm/config/chain_scoped_client_errors.go +++ b/core/chains/evm/config/chain_scoped_client_errors.go @@ -48,3 +48,4 @@ func (c *clientErrorsConfig) Fatal() string { return derefOrDefault(c.c.Fatal) } func (c *clientErrorsConfig) ServiceUnavailable() string { return derefOrDefault(c.c.ServiceUnavailable) } +func (c *clientErrorsConfig) TooManyResults() string { return derefOrDefault(c.c.TooManyResults) } diff --git a/core/chains/evm/config/chain_scoped_workflow.go b/core/chains/evm/config/chain_scoped_workflow.go index 36dcb3ea41..42bc8aed95 100644 --- a/core/chains/evm/config/chain_scoped_workflow.go +++ b/core/chains/evm/config/chain_scoped_workflow.go @@ -16,3 +16,7 @@ func (b *workflowConfig) FromAddress() *types.EIP55Address { func (b *workflowConfig) ForwarderAddress() *types.EIP55Address { return b.c.ForwarderAddress } + +func (b *workflowConfig) DefaultGasLimit() uint64 { + return b.c.DefaultGasLimit +} diff --git a/core/chains/evm/config/config.go b/core/chains/evm/config/config.go index 3d00fe86a4..cf7a43d921 100644 --- a/core/chains/evm/config/config.go +++ b/core/chains/evm/config/config.go @@ -96,6 +96,7 @@ type ClientErrors interface { TransactionAlreadyMined() string Fatal() string ServiceUnavailable() string + TooManyResults() string } type Transactions interface { @@ -166,6 +167,7 @@ type FeeHistory interface { type Workflow interface { FromAddress() *types.EIP55Address ForwarderAddress() *types.EIP55Address + DefaultGasLimit() uint64 } type NodePool interface { diff --git a/core/chains/evm/config/config_test.go b/core/chains/evm/config/config_test.go index e0dec00e68..15b5b74e8b 100644 --- a/core/chains/evm/config/config_test.go +++ b/core/chains/evm/config/config_test.go @@ -361,6 +361,7 @@ func TestClientErrorsConfig(t *testing.T) { TransactionAlreadyMined: ptr("client error transaction already mined"), Fatal: ptr("client error fatal"), ServiceUnavailable: ptr("client error service unavailable"), + TooManyResults: ptr("client error too many results"), }, } }) @@ -380,6 +381,7 @@ func TestClientErrorsConfig(t *testing.T) { assert.Equal(t, "client error transaction already mined", errors.TransactionAlreadyMined()) assert.Equal(t, "client error fatal", errors.Fatal()) assert.Equal(t, "client error service unavailable", errors.ServiceUnavailable()) + assert.Equal(t, "client error too many results", errors.TooManyResults()) }) } diff --git a/core/chains/evm/config/toml/config.go b/core/chains/evm/config/toml/config.go index 90854d90cf..9e2004d30c 100644 --- a/core/chains/evm/config/toml/config.go +++ b/core/chains/evm/config/toml/config.go @@ -521,6 +521,7 @@ func (a *Automation) setFrom(f *Automation) { type Workflow struct { FromAddress *types.EIP55Address `toml:",omitempty"` ForwarderAddress *types.EIP55Address `toml:",omitempty"` + DefaultGasLimit uint64 } func (m *Workflow) setFrom(f *Workflow) { @@ -530,6 +531,8 @@ func (m *Workflow) setFrom(f *Workflow) { if v := f.ForwarderAddress; v != nil { m.ForwarderAddress = v } + + m.DefaultGasLimit = f.DefaultGasLimit } type BalanceMonitor struct { @@ -816,6 +819,7 @@ type ClientErrors struct { TransactionAlreadyMined *string `toml:",omitempty"` Fatal *string `toml:",omitempty"` ServiceUnavailable *string `toml:",omitempty"` + TooManyResults *string `toml:",omitempty"` } func (r *ClientErrors) setFrom(f *ClientErrors) bool { @@ -861,6 +865,9 @@ func (r *ClientErrors) setFrom(f *ClientErrors) bool { if v := f.ServiceUnavailable; v != nil { r.ServiceUnavailable = v } + if v := f.TooManyResults; v != nil { + r.TooManyResults = v + } return true } diff --git a/core/chains/evm/config/toml/defaults/BSC_Mainnet.toml b/core/chains/evm/config/toml/defaults/BSC_Mainnet.toml index 10f4c570be..e95f0af1eb 100644 --- a/core/chains/evm/config/toml/defaults/BSC_Mainnet.toml +++ b/core/chains/evm/config/toml/defaults/BSC_Mainnet.toml @@ -13,6 +13,8 @@ NoNewFinalizedHeadsThreshold = '45s' [GasEstimator] PriceDefault = '5 gwei' +# Set to the BSC node's default Eth.Miner.GasPrice config +PriceMin = '3 gwei' # 15s delay since feeds update every minute in volatile situations BumpThreshold = 5 diff --git a/core/chains/evm/config/toml/defaults/Polygon_Amoy.toml b/core/chains/evm/config/toml/defaults/Polygon_Amoy.toml index b05b3053a8..bca42d9b40 100644 --- a/core/chains/evm/config/toml/defaults/Polygon_Amoy.toml +++ b/core/chains/evm/config/toml/defaults/Polygon_Amoy.toml @@ -11,10 +11,10 @@ NoNewFinalizedHeadsThreshold = '12m' MaxQueued = 5000 [GasEstimator] -EIP1559DynamicFees = true -PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' PriceDefault = '25 gwei' +PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' PriceMin = '25 gwei' +EIP1559DynamicFees = true BumpMin = '20 gwei' BumpThreshold = 5 diff --git a/core/chains/evm/config/toml/defaults/fallback.toml b/core/chains/evm/config/toml/defaults/fallback.toml index fb8eed3949..52d8c06916 100644 --- a/core/chains/evm/config/toml/defaults/fallback.toml +++ b/core/chains/evm/config/toml/defaults/fallback.toml @@ -88,3 +88,6 @@ ObservationGracePeriod = '1s' [OCR2.Automation] GasLimit = 5400000 + +[Workflow] +DefaultGasLimit = 400_000 diff --git a/core/chains/evm/forwarders/forwarder_manager.go b/core/chains/evm/forwarders/forwarder_manager.go index 6436988ba8..0e9f8781c3 100644 --- a/core/chains/evm/forwarders/forwarder_manager.go +++ b/core/chains/evm/forwarders/forwarder_manager.go @@ -33,7 +33,9 @@ type Config interface { } type FwdMgr struct { - services.StateMachine + services.Service + eng *services.Engine + ORM ORM evmClient evmclient.Client cfg Config @@ -48,61 +50,51 @@ type FwdMgr struct { authRcvr authorized_receiver.AuthorizedReceiverInterface offchainAgg offchain_aggregator_wrapper.OffchainAggregatorInterface - stopCh services.StopChan - cacheMu sync.RWMutex - wg sync.WaitGroup } -func NewFwdMgr(ds sqlutil.DataSource, client evmclient.Client, logpoller evmlogpoller.LogPoller, l logger.Logger, cfg Config) *FwdMgr { - lggr := logger.Sugared(logger.Named(l, "EVMForwarderManager")) - fwdMgr := FwdMgr{ - logger: lggr, +func NewFwdMgr(ds sqlutil.DataSource, client evmclient.Client, logpoller evmlogpoller.LogPoller, lggr logger.Logger, cfg Config) *FwdMgr { + fm := FwdMgr{ cfg: cfg, evmClient: client, ORM: NewORM(ds), logpoller: logpoller, sendersCache: make(map[common.Address][]common.Address), } - fwdMgr.stopCh = make(chan struct{}) - return &fwdMgr -} - -func (f *FwdMgr) Name() string { - return f.logger.Name() + fm.Service, fm.eng = services.Config{ + Name: "ForwarderManager", + Start: fm.start, + }.NewServiceEngine(lggr) + fm.logger = logger.Sugared(fm.eng) + return &fm } -// Start starts Forwarder Manager. -func (f *FwdMgr) Start(ctx context.Context) error { - return f.StartOnce("EVMForwarderManager", func() error { - f.logger.Debug("Initializing EVM forwarder manager") - chainId := f.evmClient.ConfiguredChainID() +func (f *FwdMgr) start(ctx context.Context) error { + chainId := f.evmClient.ConfiguredChainID() - fwdrs, err := f.ORM.FindForwardersByChain(ctx, big.Big(*chainId)) - if err != nil { - return pkgerrors.Wrapf(err, "Failed to retrieve forwarders for chain %d", chainId) - } - if len(fwdrs) != 0 { - f.initForwardersCache(ctx, fwdrs) - if err = f.subscribeForwardersLogs(ctx, fwdrs); err != nil { - return err - } + fwdrs, err := f.ORM.FindForwardersByChain(ctx, big.Big(*chainId)) + if err != nil { + return pkgerrors.Wrapf(err, "Failed to retrieve forwarders for chain %d", chainId) + } + if len(fwdrs) != 0 { + f.initForwardersCache(ctx, fwdrs) + if err = f.subscribeForwardersLogs(ctx, fwdrs); err != nil { + return err } + } - f.authRcvr, err = authorized_receiver.NewAuthorizedReceiver(common.Address{}, f.evmClient) - if err != nil { - return pkgerrors.Wrap(err, "Failed to init AuthorizedReceiver") - } + f.authRcvr, err = authorized_receiver.NewAuthorizedReceiver(common.Address{}, f.evmClient) + if err != nil { + return pkgerrors.Wrap(err, "Failed to init AuthorizedReceiver") + } - f.offchainAgg, err = offchain_aggregator_wrapper.NewOffchainAggregator(common.Address{}, f.evmClient) - if err != nil { - return pkgerrors.Wrap(err, "Failed to init OffchainAggregator") - } + f.offchainAgg, err = offchain_aggregator_wrapper.NewOffchainAggregator(common.Address{}, f.evmClient) + if err != nil { + return pkgerrors.Wrap(err, "Failed to init OffchainAggregator") + } - f.wg.Add(1) - go f.runLoop() - return nil - }) + f.eng.Go(f.runLoop) + return nil } func FilterName(addr common.Address) string { @@ -176,7 +168,7 @@ func (f *FwdMgr) ConvertPayload(dest common.Address, origPayload []byte) ([]byte if err != nil { f.logger.AssumptionViolationw("Forwarder encoding failed, this should never happen", "err", err, "to", dest, "payload", origPayload) - f.SvcErrBuffer.Append(err) + f.eng.EmitHealthErr(err) } } return databytes, nil @@ -269,11 +261,7 @@ func (f *FwdMgr) getCachedSenders(addr common.Address) ([]common.Address, bool) return addrs, ok } -func (f *FwdMgr) runLoop() { - defer f.wg.Done() - ctx, cancel := f.stopCh.NewCtx() - defer cancel() - +func (f *FwdMgr) runLoop(ctx context.Context) { ticker := services.NewTicker(time.Minute) defer ticker.Stop() @@ -353,16 +341,3 @@ func (f *FwdMgr) collectAddresses() (addrs []common.Address) { } return } - -// Stop cancels all outgoings calls and stops internal ticker loop. -func (f *FwdMgr) Close() error { - return f.StopOnce("EVMForwarderManager", func() (err error) { - close(f.stopCh) - f.wg.Wait() - return nil - }) -} - -func (f *FwdMgr) HealthReport() map[string]error { - return map[string]error{f.Name(): f.Healthy()} -} diff --git a/core/chains/evm/gas/block_history_estimator.go b/core/chains/evm/gas/block_history_estimator.go index 4075b46f90..b933ea2382 100644 --- a/core/chains/evm/gas/block_history_estimator.go +++ b/core/chains/evm/gas/block_history_estimator.go @@ -276,6 +276,8 @@ func (b *BlockHistoryEstimator) setMaxPercentileGasPrice(gasPrice *assets.Wei) { } func (b *BlockHistoryEstimator) getBlockHistoryNumbers() (numsInHistory []int64) { + b.blocksMu.RLock() + defer b.blocksMu.RUnlock() for _, b := range b.blocks { numsInHistory = append(numsInHistory, b.Number) } diff --git a/core/chains/evm/gas/rollups/l1_oracle_abi.go b/core/chains/evm/gas/rollups/l1_oracle_abi.go index fa5d9c8539..848957ce53 100644 --- a/core/chains/evm/gas/rollups/l1_oracle_abi.go +++ b/core/chains/evm/gas/rollups/l1_oracle_abi.go @@ -19,3 +19,4 @@ const OPBaseFeeScalarAbiString = `[{"inputs":[],"name":"baseFeeScalar","outputs" const OPBlobBaseFeeAbiString = `[{"inputs":[],"name":"blobBaseFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]` const OPBlobBaseFeeScalarAbiString = `[{"inputs":[],"name":"blobBaseFeeScalar","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"}]` const OPDecimalsAbiString = `[{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"}]` +const MantleTokenRatioAbiString = `[{"inputs":[],"name":"tokenRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]` diff --git a/core/chains/evm/gas/rollups/op_l1_oracle.go b/core/chains/evm/gas/rollups/op_l1_oracle.go index 28071a268f..dd54bdb859 100644 --- a/core/chains/evm/gas/rollups/op_l1_oracle.go +++ b/core/chains/evm/gas/rollups/op_l1_oracle.go @@ -50,6 +50,7 @@ type optimismL1Oracle struct { blobBaseFeeCalldata []byte blobBaseFeeScalarCalldata []byte decimalsCalldata []byte + tokenRatioCalldata []byte isEcotoneCalldata []byte isEcotoneMethodAbi abi.ABI isFjordCalldata []byte @@ -87,6 +88,10 @@ const ( // decimals is a hex encoded call to: // `function decimals() public pure returns (uint256);` decimalsMethod = "decimals" + // tokenRatio fetches the tokenRatio used for Mantle's gas price calculation + // tokenRatio is a hex encoded call to: + // `function tokenRatio() public pure returns (uint256);` + tokenRatioMethod = "tokenRatio" // OPGasOracleAddress is the address of the precompiled contract that exists on Optimism, Base and Mantle. OPGasOracleAddress = "0x420000000000000000000000000000000000000F" // KromaGasOracleAddress is the address of the precompiled contract that exists on Kroma. @@ -187,6 +192,16 @@ func newOpStackL1GasOracle(lggr logger.Logger, ethClient l1OracleClient, chainTy return nil, fmt.Errorf("failed to parse GasPriceOracle %s() calldata for chain: %s; %w", decimalsMethod, chainType, err) } + // Encode calldata for tokenRatio method + tokenRatioMethodAbi, err := abi.JSON(strings.NewReader(MantleTokenRatioAbiString)) + if err != nil { + return nil, fmt.Errorf("failed to parse GasPriceOracle %s() method ABI for chain: %s; %w", tokenRatioMethod, chainType, err) + } + tokenRatioCalldata, err := tokenRatioMethodAbi.Pack(tokenRatioMethod) + if err != nil { + return nil, fmt.Errorf("failed to parse GasPriceOracle %s() calldata for chain: %s; %w", tokenRatioMethod, chainType, err) + } + return &optimismL1Oracle{ client: ethClient, pollPeriod: PollPeriod, @@ -208,6 +223,7 @@ func newOpStackL1GasOracle(lggr logger.Logger, ethClient l1OracleClient, chainTy blobBaseFeeCalldata: blobBaseFeeCalldata, blobBaseFeeScalarCalldata: blobBaseFeeScalarCalldata, decimalsCalldata: decimalsCalldata, + tokenRatioCalldata: tokenRatioCalldata, isEcotoneCalldata: isEcotoneCalldata, isEcotoneMethodAbi: isEcotoneMethodAbi, isFjordCalldata: isFjordCalldata, @@ -350,6 +366,10 @@ func (o *optimismL1Oracle) GetGasCost(ctx context.Context, tx *gethtypes.Transac } func (o *optimismL1Oracle) GetDAGasPrice(ctx context.Context) (*big.Int, error) { + if o.chainType == chaintype.ChainMantle { + return o.getMantleGasPrice(ctx) + } + err := o.checkForUpgrade(ctx) if err != nil { return nil, err @@ -447,6 +467,69 @@ func (o *optimismL1Oracle) getV1GasPrice(ctx context.Context) (*big.Int, error) return new(big.Int).SetBytes(b), nil } +// Returns the gas price for Mantle. The formula is the same as Optimism Bedrock (getV1GasPrice), but the tokenRatio parameter is multiplied +func (o *optimismL1Oracle) getMantleGasPrice(ctx context.Context) (*big.Int, error) { + // call oracle to get l1BaseFee and tokenRatio + rpcBatchCalls := []rpc.BatchElem{ + { + Method: "eth_call", + Args: []any{ + map[string]interface{}{ + "from": common.Address{}, + "to": o.l1OracleAddress, + "data": hexutil.Bytes(o.l1BaseFeeCalldata), + }, + "latest", + }, + Result: new(string), + }, + { + Method: "eth_call", + Args: []any{ + map[string]interface{}{ + "from": common.Address{}, + "to": o.l1OracleAddress, + "data": hexutil.Bytes(o.tokenRatioCalldata), + }, + "latest", + }, + Result: new(string), + }, + } + + err := o.client.BatchCallContext(ctx, rpcBatchCalls) + if err != nil { + return nil, fmt.Errorf("fetch gas price parameters batch call failed: %w", err) + } + if rpcBatchCalls[0].Error != nil { + return nil, fmt.Errorf("%s call failed in a batch: %w", l1BaseFeeMethod, err) + } + if rpcBatchCalls[1].Error != nil { + return nil, fmt.Errorf("%s call failed in a batch: %w", tokenRatioMethod, err) + } + + // Extract values from responses + l1BaseFeeResult := *(rpcBatchCalls[0].Result.(*string)) + tokenRatioResult := *(rpcBatchCalls[1].Result.(*string)) + + // Decode the responses into bytes + l1BaseFeeBytes, err := hexutil.Decode(l1BaseFeeResult) + if err != nil { + return nil, fmt.Errorf("failed to decode %s rpc result: %w", l1BaseFeeMethod, err) + } + tokenRatioBytes, err := hexutil.Decode(tokenRatioResult) + if err != nil { + return nil, fmt.Errorf("failed to decode %s rpc result: %w", tokenRatioMethod, err) + } + + // Convert bytes to big int for calculations + l1BaseFee := new(big.Int).SetBytes(l1BaseFeeBytes) + tokenRatio := new(big.Int).SetBytes(tokenRatioBytes) + + // multiply l1BaseFee and tokenRatio and return + return new(big.Int).Mul(l1BaseFee, tokenRatio), nil +} + // Returns the scaled gas price using baseFeeScalar, l1BaseFee, blobBaseFeeScalar, and blobBaseFee fields from the oracle // Confirmed the same calculation is used to determine gas price for both Ecotone and Fjord func (o *optimismL1Oracle) getEcotoneFjordGasPrice(ctx context.Context) (*big.Int, error) { diff --git a/core/chains/evm/gas/rollups/op_l1_oracle_test.go b/core/chains/evm/gas/rollups/op_l1_oracle_test.go index f5f009f1ea..88bb96534d 100644 --- a/core/chains/evm/gas/rollups/op_l1_oracle_test.go +++ b/core/chains/evm/gas/rollups/op_l1_oracle_test.go @@ -111,6 +111,94 @@ func TestOPL1Oracle_ReadV1GasPrice(t *testing.T) { } } +func TestOPL1Oracle_ReadMantleGasPrice(t *testing.T) { + l1BaseFee := big.NewInt(100) + tokenRatio := big.NewInt(40) + oracleAddress := common.HexToAddress("0x1234").String() + + t.Parallel() + t.Run("correctly fetches gas price if chain is Mantle", func(t *testing.T) { + // Encode calldata for l1BaseFee method + l1BaseFeeMethodAbi, err := abi.JSON(strings.NewReader(L1BaseFeeAbiString)) + require.NoError(t, err) + l1BaseFeeCalldata, err := l1BaseFeeMethodAbi.Pack(l1BaseFeeMethod) + require.NoError(t, err) + + // Encode calldata for tokenRatio method + tokenRatioMethodAbi, err := abi.JSON(strings.NewReader(MantleTokenRatioAbiString)) + require.NoError(t, err) + tokenRatioCalldata, err := tokenRatioMethodAbi.Pack(tokenRatioMethod) + require.NoError(t, err) + + ethClient := mocks.NewL1OracleClient(t) + ethClient.On("BatchCallContext", mock.Anything, mock.IsType([]rpc.BatchElem{})).Run(func(args mock.Arguments) { + rpcElements := args.Get(1).([]rpc.BatchElem) + require.Equal(t, 2, len(rpcElements)) + for _, rE := range rpcElements { + require.Equal(t, "eth_call", rE.Method) + require.Equal(t, oracleAddress, rE.Args[0].(map[string]interface{})["to"]) + require.Equal(t, "latest", rE.Args[1]) + } + require.Equal(t, hexutil.Bytes(l1BaseFeeCalldata), rpcElements[0].Args[0].(map[string]interface{})["data"]) + require.Equal(t, hexutil.Bytes(tokenRatioCalldata), rpcElements[1].Args[0].(map[string]interface{})["data"]) + + res1 := common.BigToHash(l1BaseFee).Hex() + res2 := common.BigToHash(tokenRatio).Hex() + + rpcElements[0].Result = &res1 + rpcElements[1].Result = &res2 + }).Return(nil).Once() + + oracle, err := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainMantle, oracleAddress) + require.NoError(t, err) + + gasPrice, err := oracle.GetDAGasPrice(tests.Context(t)) + require.NoError(t, err) + + assert.Equal(t, new(big.Int).Mul(l1BaseFee, tokenRatio), gasPrice) + }) + + t.Run("fetching Mantle price but rpc returns bad data", func(t *testing.T) { + ethClient := mocks.NewL1OracleClient(t) + ethClient.On("BatchCallContext", mock.Anything, mock.IsType([]rpc.BatchElem{})).Run(func(args mock.Arguments) { + rpcElements := args.Get(1).([]rpc.BatchElem) + var badData = "zzz" + rpcElements[0].Result = &badData + rpcElements[1].Result = &badData + }).Return(nil).Once() + + oracle, err := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainMantle, oracleAddress) + require.NoError(t, err) + _, err = oracle.GetDAGasPrice(tests.Context(t)) + assert.Error(t, err) + }) + + t.Run("fetching Mantle price but rpc parent call errors", func(t *testing.T) { + ethClient := mocks.NewL1OracleClient(t) + ethClient.On("BatchCallContext", mock.Anything, mock.IsType([]rpc.BatchElem{})).Return(fmt.Errorf("revert")).Once() + + oracle, err := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainMantle, oracleAddress) + require.NoError(t, err) + _, err = oracle.GetDAGasPrice(tests.Context(t)) + assert.Error(t, err) + }) + + t.Run("fetching Mantle price but one of the sub rpc call errors", func(t *testing.T) { + ethClient := mocks.NewL1OracleClient(t) + ethClient.On("BatchCallContext", mock.Anything, mock.IsType([]rpc.BatchElem{})).Run(func(args mock.Arguments) { + rpcElements := args.Get(1).([]rpc.BatchElem) + res := common.BigToHash(l1BaseFee).Hex() + rpcElements[0].Result = &res + rpcElements[1].Error = fmt.Errorf("revert") + }).Return(nil).Once() + + oracle, err := newOpStackL1GasOracle(logger.Test(t), ethClient, chaintype.ChainMantle, oracleAddress) + require.NoError(t, err) + _, err = oracle.GetDAGasPrice(tests.Context(t)) + assert.Error(t, err) + }) +} + func setupUpgradeCheck(t *testing.T, oracleAddress string, isFjord, isEcotone bool) *mocks.L1OracleClient { trueHex := "0x0000000000000000000000000000000000000000000000000000000000000001" falseHex := "0x0000000000000000000000000000000000000000000000000000000000000000" diff --git a/core/chains/evm/headtracker/head_listener.go b/core/chains/evm/headtracker/head_listener.go deleted file mode 100644 index 04535a3486..0000000000 --- a/core/chains/evm/headtracker/head_listener.go +++ /dev/null @@ -1,28 +0,0 @@ -package headtracker - -import ( - "math/big" - - "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/common" - - "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink/v2/common/headtracker" - - htrktypes "github.com/smartcontractkit/chainlink/v2/common/headtracker/types" - evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" - evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" -) - -type headListener = headtracker.HeadListener[*evmtypes.Head, common.Hash] - -func NewHeadListener( - lggr logger.Logger, - ethClient evmclient.Client, - config htrktypes.Config, chStop chan struct{}, -) headListener { - return headtracker.NewHeadListener[ - *evmtypes.Head, - ethereum.Subscription, *big.Int, common.Hash, - ](lggr, ethClient, config, chStop) -} diff --git a/core/chains/evm/headtracker/head_listener_test.go b/core/chains/evm/headtracker/head_listener_test.go index 29b090bbff..2e459af2a2 100644 --- a/core/chains/evm/headtracker/head_listener_test.go +++ b/core/chains/evm/headtracker/head_listener_test.go @@ -16,9 +16,9 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink/v2/common/headtracker" commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/testutils" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ) @@ -40,17 +40,10 @@ func Test_HeadListener_HappyPath(t *testing.T) { evmcfg := testutils.NewTestChainScopedConfig(t, func(c *toml.EVMConfig) { c.NoNewHeadsThreshold = &commonconfig.Duration{} }) - chStop := make(chan struct{}) - hl := headtracker.NewHeadListener(lggr, ethClient, evmcfg.EVM(), chStop) var headCount atomic.Int32 - handler := func(context.Context, *evmtypes.Head) error { - headCount.Add(1) - return nil - } - - subscribeAwaiter := testutils.NewAwaiter() unsubscribeAwaiter := testutils.NewAwaiter() + subscribeAwaiter := testutils.NewAwaiter() var chHeads chan<- *evmtypes.Head var chErr = make(chan error) var chSubErr <-chan error = chErr @@ -66,23 +59,23 @@ func Test_HeadListener_HappyPath(t *testing.T) { close(chErr) }) - doneAwaiter := testutils.NewAwaiter() - done := func() { - doneAwaiter.ItHappened() - } - go hl.ListenForNewHeads(func() {}, handler, done) - - subscribeAwaiter.AwaitOrFail(t, tests.WaitTimeout(t)) - require.Eventually(t, hl.Connected, tests.WaitTimeout(t), tests.TestInterval) + func() { + hl := headtracker.NewHeadListener(lggr, ethClient, evmcfg.EVM(), nil, func(context.Context, *evmtypes.Head) error { + headCount.Add(1) + return nil + }) + require.NoError(t, hl.Start(tests.Context(t))) + defer func() { assert.NoError(t, hl.Close()) }() - chHeads <- testutils.Head(0) - chHeads <- testutils.Head(1) - chHeads <- testutils.Head(2) + subscribeAwaiter.AwaitOrFail(t, tests.WaitTimeout(t)) + require.Eventually(t, hl.Connected, tests.WaitTimeout(t), tests.TestInterval) - require.True(t, hl.ReceivingHeads()) + chHeads <- testutils.Head(0) + chHeads <- testutils.Head(1) + chHeads <- testutils.Head(2) - close(chStop) - doneAwaiter.AwaitOrFail(t) + require.True(t, hl.ReceivingHeads()) + }() unsubscribeAwaiter.AwaitOrFail(t) require.Equal(t, int32(3), headCount.Load()) @@ -101,14 +94,8 @@ func Test_HeadListener_NotReceivingHeads(t *testing.T) { evmcfg := testutils.NewTestChainScopedConfig(t, func(c *toml.EVMConfig) { c.NoNewHeadsThreshold = commonconfig.MustNewDuration(time.Second) }) - chStop := make(chan struct{}) - hl := headtracker.NewHeadListener(lggr, ethClient, evmcfg.EVM(), chStop) firstHeadAwaiter := testutils.NewAwaiter() - handler := func(context.Context, *evmtypes.Head) error { - firstHeadAwaiter.ItHappened() - return nil - } subscribeAwaiter := testutils.NewAwaiter() var chHeads chan<- *evmtypes.Head @@ -125,25 +112,25 @@ func Test_HeadListener_NotReceivingHeads(t *testing.T) { close(chErr) }) - doneAwaiter := testutils.NewAwaiter() - done := func() { - doneAwaiter.ItHappened() - } - go hl.ListenForNewHeads(func() {}, handler, done) - - subscribeAwaiter.AwaitOrFail(t, tests.WaitTimeout(t)) + func() { + hl := headtracker.NewHeadListener(lggr, ethClient, evmcfg.EVM(), nil, func(context.Context, *evmtypes.Head) error { + firstHeadAwaiter.ItHappened() + return nil + }) + require.NoError(t, hl.Start(tests.Context(t))) + defer func() { assert.NoError(t, hl.Close()) }() - chHeads <- testutils.Head(0) - firstHeadAwaiter.AwaitOrFail(t) + subscribeAwaiter.AwaitOrFail(t, tests.WaitTimeout(t)) - require.True(t, hl.ReceivingHeads()) + chHeads <- testutils.Head(0) + firstHeadAwaiter.AwaitOrFail(t) - time.Sleep(time.Second * 2) + require.True(t, hl.ReceivingHeads()) - require.False(t, hl.ReceivingHeads()) + time.Sleep(time.Second * 2) - close(chStop) - doneAwaiter.AwaitOrFail(t) + require.False(t, hl.ReceivingHeads()) + }() } func Test_HeadListener_SubscriptionErr(t *testing.T) { @@ -161,19 +148,11 @@ func Test_HeadListener_SubscriptionErr(t *testing.T) { for _, test := range cases { test := test t.Run(test.name, func(t *testing.T) { - l := logger.Test(t) + lggr := logger.Test(t) ethClient := testutils.NewEthClientMockWithDefaultChain(t) evmcfg := testutils.NewTestChainScopedConfig(t, nil) - chStop := make(chan struct{}) - hl := headtracker.NewHeadListener(l, ethClient, evmcfg.EVM(), chStop) hnhCalled := make(chan *evmtypes.Head) - hnh := func(_ context.Context, header *evmtypes.Head) error { - hnhCalled <- header - return nil - } - doneAwaiter := testutils.NewAwaiter() - done := doneAwaiter.ItHappened chSubErrTest := make(chan error) var chSubErr <-chan error = chSubErrTest @@ -189,63 +168,66 @@ func Test_HeadListener_SubscriptionErr(t *testing.T) { headsCh = args.Get(1).(chan<- *evmtypes.Head) subscribeAwaiter.ItHappened() }) - go func() { - hl.ListenForNewHeads(func() {}, hnh, done) - }() - - // Put a head on the channel to ensure we test all code paths - subscribeAwaiter.AwaitOrFail(t, tests.WaitTimeout(t)) - head := testutils.Head(0) - headsCh <- head - - h := <-hnhCalled - assert.Equal(t, head, h) - - // Expect a call to unsubscribe on error - sub.On("Unsubscribe").Once().Run(func(_ mock.Arguments) { - close(headsCh) - // geth guarantees that Unsubscribe closes the errors channel - if !test.closeErr { + func() { + hl := headtracker.NewHeadListener(lggr, ethClient, evmcfg.EVM(), nil, func(_ context.Context, header *evmtypes.Head) error { + hnhCalled <- header + return nil + }) + require.NoError(t, hl.Start(tests.Context(t))) + defer func() { assert.NoError(t, hl.Close()) }() + + // Put a head on the channel to ensure we test all code paths + subscribeAwaiter.AwaitOrFail(t, tests.WaitTimeout(t)) + head := testutils.Head(0) + headsCh <- head + + h := <-hnhCalled + assert.Equal(t, head, h) + + // Expect a call to unsubscribe on error + sub.On("Unsubscribe").Once().Run(func(_ mock.Arguments) { + close(headsCh) + // geth guarantees that Unsubscribe closes the errors channel + if !test.closeErr { + close(chSubErrTest) + } + }) + // Expect a resubscribe + chSubErrTest2 := make(chan error) + var chSubErr2 <-chan error = chSubErrTest2 + sub2 := commonmocks.NewSubscription(t) + sub2.On("Err").Return(chSubErr2) + subscribeAwaiter2 := testutils.NewAwaiter() + + var headsCh2 chan<- *evmtypes.Head + ethClient.On("SubscribeNewHead", mock.Anything, mock.AnythingOfType("chan<- *types.Head")).Return(sub2, nil).Once().Run(func(args mock.Arguments) { + headsCh2 = args.Get(1).(chan<- *evmtypes.Head) + subscribeAwaiter2.ItHappened() + }) + + // Sending test error + if test.closeErr { close(chSubErrTest) + } else { + chSubErrTest <- test.err } - }) - // Expect a resubscribe - chSubErrTest2 := make(chan error) - var chSubErr2 <-chan error = chSubErrTest2 - sub2 := commonmocks.NewSubscription(t) - sub2.On("Err").Return(chSubErr2) - subscribeAwaiter2 := testutils.NewAwaiter() - - var headsCh2 chan<- *evmtypes.Head - ethClient.On("SubscribeNewHead", mock.Anything, mock.AnythingOfType("chan<- *types.Head")).Return(sub2, nil).Once().Run(func(args mock.Arguments) { - headsCh2 = args.Get(1).(chan<- *evmtypes.Head) - subscribeAwaiter2.ItHappened() - }) - - // Sending test error - if test.closeErr { - close(chSubErrTest) - } else { - chSubErrTest <- test.err - } - // Wait for it to resubscribe - subscribeAwaiter2.AwaitOrFail(t, tests.WaitTimeout(t)) + // Wait for it to resubscribe + subscribeAwaiter2.AwaitOrFail(t, tests.WaitTimeout(t)) - head2 := testutils.Head(1) - headsCh2 <- head2 + head2 := testutils.Head(1) + headsCh2 <- head2 - h2 := <-hnhCalled - assert.Equal(t, head2, h2) + h2 := <-hnhCalled + assert.Equal(t, head2, h2) - // Second call to unsubscribe on close - sub2.On("Unsubscribe").Once().Run(func(_ mock.Arguments) { - close(headsCh2) - // geth guarantees that Unsubscribe closes the errors channel - close(chSubErrTest2) - }) - close(chStop) - doneAwaiter.AwaitOrFail(t) + // Second call to unsubscribe on close + sub2.On("Unsubscribe").Once().Run(func(_ mock.Arguments) { + close(headsCh2) + // geth guarantees that Unsubscribe closes the errors channel + close(chSubErrTest2) + }) + }() }) } } diff --git a/core/chains/evm/headtracker/head_tracker.go b/core/chains/evm/headtracker/head_tracker.go index d6c2cdc64e..f7607189f7 100644 --- a/core/chains/evm/headtracker/head_tracker.go +++ b/core/chains/evm/headtracker/head_tracker.go @@ -2,10 +2,8 @@ package headtracker import ( "context" - "math/big" "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/common" "go.uber.org/zap/zapcore" "github.com/smartcontractkit/chainlink-common/pkg/logger" @@ -27,7 +25,7 @@ func NewHeadTracker( headSaver httypes.HeadSaver, mailMon *mailbox.Monitor, ) httypes.HeadTracker { - return headtracker.NewHeadTracker[*evmtypes.Head, ethereum.Subscription, *big.Int, common.Hash]( + return headtracker.NewHeadTracker[*evmtypes.Head, ethereum.Subscription]( lggr, ethClient, config, diff --git a/core/chains/evm/headtracker/simulated_head_tracker.go b/core/chains/evm/headtracker/simulated_head_tracker.go index e1e550de99..62bb4968c2 100644 --- a/core/chains/evm/headtracker/simulated_head_tracker.go +++ b/core/chains/evm/headtracker/simulated_head_tracker.go @@ -2,6 +2,7 @@ package headtracker import ( "context" + "errors" "fmt" "math/big" @@ -51,3 +52,31 @@ func (ht *simulatedHeadTracker) LatestAndFinalizedBlock(ctx context.Context) (*e return latest, finalizedBlock, nil } + +func (ht *simulatedHeadTracker) LatestChain() *evmtypes.Head { + return nil +} + +func (ht *simulatedHeadTracker) HealthReport() map[string]error { + return nil +} + +func (ht *simulatedHeadTracker) Start(_ context.Context) error { + return nil +} + +func (ht *simulatedHeadTracker) Close() error { + return nil +} + +func (ht *simulatedHeadTracker) Backfill(_ context.Context, _ *evmtypes.Head) error { + return errors.New("unimplemented") +} + +func (ht *simulatedHeadTracker) Name() string { + return "SimulatedHeadTracker" +} + +func (ht *simulatedHeadTracker) Ready() error { + return nil +} diff --git a/core/chains/evm/logpoller/log_poller.go b/core/chains/evm/logpoller/log_poller.go index dee5d1d1a5..a4560c967c 100644 --- a/core/chains/evm/logpoller/log_poller.go +++ b/core/chains/evm/logpoller/log_poller.go @@ -29,6 +29,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/utils/mathutil" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" ) @@ -113,6 +114,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 logPrunePageSize int64 + clientErrors config.ClientErrors backupPollerNextBlock int64 // next block to be processed by Backup LogPoller backupPollerBlockDelay int64 // how far behind regular LogPoller should BackupLogPoller run. 0 = disabled @@ -143,6 +145,7 @@ type Opts struct { KeepFinalizedBlocksDepth int64 BackupPollerBlockDelay int64 LogPrunePageSize int64 + ClientErrors config.ClientErrors } // NewLogPoller creates a log poller. Note there is an assumption @@ -172,6 +175,7 @@ func NewLogPoller(orm ORM, ec Client, lggr logger.Logger, headTracker HeadTracke rpcBatchSize: opts.RpcBatchSize, keepFinalizedBlocksDepth: opts.KeepFinalizedBlocksDepth, logPrunePageSize: opts.LogPrunePageSize, + clientErrors: opts.ClientErrors, filters: make(map[string]Filter), filterDirty: true, // Always build Filter on first call to cache an empty filter if nothing registered yet. finalityViolated: new(atomic.Bool), @@ -794,8 +798,6 @@ func (lp *logPoller) blocksFromLogs(ctx context.Context, logs []types.Log, endBl return lp.GetBlocksRange(ctx, numbers) } -const jsonRpcLimitExceeded = -32005 // See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1474.md - // backfill will query FilterLogs in batches for logs in the // block range [start, end] and save them to the db. // Retries until ctx cancelled. Will return an error if cancelled @@ -807,13 +809,11 @@ func (lp *logPoller) backfill(ctx context.Context, start, end int64) error { gethLogs, err := lp.ec.FilterLogs(ctx, lp.Filter(big.NewInt(from), big.NewInt(to), nil)) if err != nil { - var rpcErr client.JsonError - if pkgerrors.As(err, &rpcErr) { - if rpcErr.Code != jsonRpcLimitExceeded { - lp.lggr.Errorw("Unable to query for logs", "err", err, "from", from, "to", to) - return err - } + if !client.IsTooManyResults(err, lp.clientErrors) { + lp.lggr.Errorw("Unable to query for logs", "err", err, "from", from, "to", to) + return err } + if batchSize == 1 { lp.lggr.Criticalw("Too many log results in a single block, failed to retrieve logs! Node may be running in a degraded state.", "err", err, "from", from, "to", to, "LogBackfillBatchSize", lp.backfillBatchSize) return err diff --git a/core/chains/evm/logpoller/log_poller_test.go b/core/chains/evm/logpoller/log_poller_test.go index 548711c19b..73302877f9 100644 --- a/core/chains/evm/logpoller/log_poller_test.go +++ b/core/chains/evm/logpoller/log_poller_test.go @@ -1542,6 +1542,8 @@ type getLogErrData struct { } func TestTooManyLogResults(t *testing.T) { + t.Parallel() + ctx := testutils.Context(t) ec := evmtest.NewEthClientMockWithDefaultChain(t) lggr, obs := logger.TestObserved(t, zapcore.DebugLevel) @@ -1561,89 +1563,126 @@ func TestTooManyLogResults(t *testing.T) { lp := logpoller.NewLogPoller(o, ec, lggr, headTracker, lpOpts) expected := []int64{10, 5, 2, 1} - clientErr := client.JsonError{ + tooLargeErr := client.JsonError{ Code: -32005, Data: getLogErrData{"0x100E698", "0x100E6D4", 10000}, Message: "query returned more than 10000 results. Try with this block range [0x100E698, 0x100E6D4].", } - // Simulate currentBlock = 300 - head := &evmtypes.Head{Number: 300} - finalized := &evmtypes.Head{Number: head.Number - lpOpts.FinalityDepth} - headTracker.On("LatestAndFinalizedBlock", mock.Anything).Return(head, finalized, nil).Once() - call1 := ec.On("HeadByNumber", mock.Anything, mock.Anything).Return(func(ctx context.Context, blockNumber *big.Int) (*evmtypes.Head, error) { + var filterLogsCall *mock.Call + head := &evmtypes.Head{} + finalized := &evmtypes.Head{} + + ec.On("HeadByNumber", mock.Anything, mock.Anything).Return(func(ctx context.Context, blockNumber *big.Int) (*evmtypes.Head, error) { if blockNumber == nil { require.FailNow(t, "unexpected call to get current head") } return &evmtypes.Head{Number: blockNumber.Int64()}, nil }) - call2 := ec.On("FilterLogs", mock.Anything, mock.Anything).Return(func(ctx context.Context, fq ethereum.FilterQuery) (logs []types.Log, err error) { - if fq.BlockHash != nil { - return []types.Log{}, nil // succeed when single block requested - } - from := fq.FromBlock.Uint64() - to := fq.ToBlock.Uint64() - if to-from >= 4 { - return []types.Log{}, &clientErr // return "too many results" error if block range spans 4 or more blocks - } - return logs, err - }) + t.Run("halves size until small enough, then succeeds", func(t *testing.T) { + // Simulate currentBlock = 300 + head.Number = 300 + finalized.Number = head.Number - lpOpts.FinalityDepth + headTracker.On("LatestAndFinalizedBlock", mock.Anything).Return(head, finalized, nil).Once() - addr := testutils.NewAddress() - err := lp.RegisterFilter(ctx, 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(ctx) - require.NoError(t, err2) - assert.Equal(t, int64(298), block.BlockNumber) - - logs := obs.FilterLevelExact(zapcore.WarnLevel).FilterMessageSnippet("halving block range batch size").FilterFieldKey("newBatchSize").All() - // Should have tried again 3 times--first reducing batch size to 10, then 5, then 2 - require.Len(t, logs, 3) - for i, s := range expected[:3] { - assert.Equal(t, s, logs[i].ContextMap()["newBatchSize"]) - } + filterLogsCall = ec.On("FilterLogs", mock.Anything, mock.Anything).Return(func(ctx context.Context, fq ethereum.FilterQuery) (logs []types.Log, err error) { + if fq.BlockHash != nil { + return []types.Log{}, nil // succeed when single block requested + } + from := fq.FromBlock.Uint64() + to := fq.ToBlock.Uint64() + if to-from >= 4 { + return []types.Log{}, tooLargeErr // return "too many results" error if block range spans 4 or more blocks + } + return logs, err + }) - obs.TakeAll() - call1.Unset() - call2.Unset() + addr := testutils.NewAddress() + err := lp.RegisterFilter(ctx, 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(ctx) + require.NoError(t, err2) + assert.Equal(t, int64(298), block.BlockNumber) - // Now jump to block 500, but return error no matter how small the block range gets. - // Should exit the loop with a critical error instead of hanging. - head = &evmtypes.Head{Number: 500} - finalized = &evmtypes.Head{Number: head.Number - lpOpts.FinalityDepth} - headTracker.On("LatestAndFinalizedBlock", mock.Anything).Return(head, finalized, nil).Once() - call1.On("HeadByNumber", mock.Anything, mock.Anything).Return(func(ctx context.Context, blockNumber *big.Int) (*evmtypes.Head, error) { - if blockNumber == nil { - require.FailNow(t, "unexpected call to get current head") + logs := obs.FilterLevelExact(zapcore.WarnLevel).FilterMessageSnippet("halving block range batch size").FilterFieldKey("newBatchSize").All() + // Should have tried again 3 times--first reducing batch size to 10, then 5, then 2 + require.Len(t, logs, 3) + for i, s := range expected[:3] { + assert.Equal(t, s, logs[i].ContextMap()["newBatchSize"]) } - return &evmtypes.Head{Number: blockNumber.Int64()}, nil + filterLogsCall.Unset() }) - call2.On("FilterLogs", mock.Anything, mock.Anything).Return(func(ctx context.Context, fq ethereum.FilterQuery) (logs []types.Log, err error) { - if fq.BlockHash != nil { - return []types.Log{}, nil // succeed when single block requested + + t.Run("Halves size until single block, then reports critical error", func(t *testing.T) { + obs.TakeAll() + + // Now jump to block 500, but return error no matter how small the block range gets. + // Should exit the loop with a critical error instead of hanging. + head.Number = 500 + finalized.Number = head.Number - lpOpts.FinalityDepth + headTracker.On("LatestAndFinalizedBlock", mock.Anything).Return(head, finalized, nil).Once() + filterLogsCall = ec.On("FilterLogs", mock.Anything, mock.Anything).Return(func(ctx context.Context, fq ethereum.FilterQuery) (logs []types.Log, err error) { + if fq.BlockHash != nil { + return []types.Log{}, nil // succeed when single block requested + } + return []types.Log{}, tooLargeErr // return "too many results" error if block range spans 4 or more blocks + }) + + lp.PollAndSaveLogs(ctx, 298) + block, err := o.SelectLatestBlock(ctx) + if err != nil { + assert.ErrorContains(t, err, "no rows") // In case this subtest is run by itself + } else { + assert.Equal(t, int64(298), block.BlockNumber) + } + warns := obs.FilterMessageSnippet("halving block range").FilterLevelExact(zapcore.WarnLevel).All() + crit := obs.FilterMessageSnippet("failed to retrieve logs").FilterLevelExact(zapcore.DPanicLevel).All() + require.Len(t, warns, 4) + for i, s := range expected { + assert.Equal(t, s, warns[i].ContextMap()["newBatchSize"]) } - return []types.Log{}, &clientErr // return "too many results" error if block range spans 4 or more blocks + + require.Len(t, crit, 1) + assert.Contains(t, crit[0].Message, "Too many log results in a single block") + filterLogsCall.Unset() }) - lp.PollAndSaveLogs(ctx, 298) - block, err2 = o.SelectLatestBlock(ctx) - require.NoError(t, err2) - assert.Equal(t, int64(298), block.BlockNumber) - warns := obs.FilterMessageSnippet("halving block range").FilterLevelExact(zapcore.WarnLevel).All() - crit := obs.FilterMessageSnippet("failed to retrieve logs").FilterLevelExact(zapcore.DPanicLevel).All() - require.Len(t, warns, 4) - for i, s := range expected { - assert.Equal(t, s, warns[i].ContextMap()["newBatchSize"]) - } + t.Run("Unrelated error are retried without adjusting size", func(t *testing.T) { + unrelatedError := fmt.Errorf("Unrelated to the size of the request") + head.Number = 500 + finalized.Number = head.Number - lpOpts.FinalityDepth + + obs.TakeAll() + filterLogsCall = ec.On("FilterLogs", mock.Anything, mock.Anything).Return(func(ctx context.Context, fq ethereum.FilterQuery) (logs []types.Log, err error) { + if fq.BlockHash != nil { + return []types.Log{}, nil // succeed when single block requested + } + return []types.Log{}, unrelatedError // return an unrelated error that should just be retried with same size + }) + headTracker.On("LatestAndFinalizedBlock", mock.Anything).Return(head, finalized, nil).Once() - require.Len(t, crit, 1) - assert.Contains(t, crit[0].Message, "Too many log results in a single block") + lp.PollAndSaveLogs(ctx, 298) + block, err := o.SelectLatestBlock(ctx) + if err != nil { + assert.ErrorContains(t, err, "no rows") // In case this subtest is run by itself + } else { + assert.Equal(t, int64(298), block.BlockNumber) + } + crit := obs.FilterLevelExact(zapcore.DPanicLevel).All() + errors := obs.FilterLevelExact(zapcore.ErrorLevel).All() + warns := obs.FilterLevelExact(zapcore.WarnLevel).All() + assert.Len(t, crit, 0) + require.Len(t, errors, 1) + assert.Equal(t, errors[0].Message, "Unable to query for logs") + require.Len(t, warns, 1) + assert.Contains(t, warns[0].Message, "retrying later") + }) } func Test_PollAndQueryFinalizedBlocks(t *testing.T) { diff --git a/core/chains/evm/logpoller/observability_test.go b/core/chains/evm/logpoller/observability_test.go index 5e668a4ad1..2f502438bb 100644 --- a/core/chains/evm/logpoller/observability_test.go +++ b/core/chains/evm/logpoller/observability_test.go @@ -119,6 +119,16 @@ func TestCountersAreProperlyPopulatedForWrites(t *testing.T) { assert.Equal(t, float64(20), testutil.ToFloat64(orm.logsInserted.WithLabelValues("420"))) assert.Equal(t, float64(2), testutil.ToFloat64(orm.blocksInserted.WithLabelValues("420"))) + rowsAffected, err := orm.DeleteExpiredLogs(ctx, 3) + require.NoError(t, err) + require.Equal(t, int64(3), rowsAffected) + assert.Equal(t, 3, counterFromGaugeByLabels(orm.datasetSize, "420", "DeleteExpiredLogs", "delete")) + + rowsAffected, err = orm.DeleteBlocksBefore(ctx, 30, 0) + require.NoError(t, err) + require.Equal(t, int64(2), rowsAffected) + assert.Equal(t, 2, counterFromGaugeByLabels(orm.datasetSize, "420", "DeleteBlocksBefore", "delete")) + // Don't update counters in case of an error require.Error(t, orm.InsertLogsWithBlock(ctx, logs, NewLogPollerBlock(utils.RandomBytes32(), 0, time.Now(), 0))) assert.Equal(t, float64(20), testutil.ToFloat64(orm.logsInserted.WithLabelValues("420"))) diff --git a/core/chains/evm/logpoller/orm.go b/core/chains/evm/logpoller/orm.go index 9cbb21a606..938d565f09 100644 --- a/core/chains/evm/logpoller/orm.go +++ b/core/chains/evm/logpoller/orm.go @@ -15,6 +15,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types/query" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -313,34 +314,30 @@ type Exp struct { ShouldDelete bool } +// DeleteExpiredLogs removes any logs which either: +// - don't match any currently registered filters, or +// - have a timestamp older than any matching filter's retention, UNLESS there is at +// least one matching filter with retention=0 func (o *DSORM) DeleteExpiredLogs(ctx context.Context, limit int64) (int64, error) { var err error var result sql.Result - if limit > 0 { - result, err = o.ds.ExecContext(ctx, ` - DELETE FROM evm.logs + query := `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 + LEFT JOIN ( + SELECT address, event, CASE WHEN MIN(retention) = 0 THEN 0 ELSE MAX(retention) END 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) + ) r ON l.address = r.address AND l.event_sig = r.event + WHERE l.evm_chain_id = $1 AND -- Must be WHERE rather than ON due to LEFT JOIN + r.retention IS NULL OR (r.retention != 0 AND l.block_timestamp <= STATEMENT_TIMESTAMP() - (r.retention / 10^9 * interval '1 second')) %s)` + + if limit > 0 { + result, err = o.ds.ExecContext(ctx, fmt.Sprintf(query, "LIMIT $2"), ubig.New(o.chainID), limit) } else { - result, err = o.ds.ExecContext(ctx, `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)) - ) DELETE FROM evm.logs l USING r - WHERE l.evm_chain_id = $1 AND l.address=r.address AND l.event_sig=r.event - AND l.block_timestamp <= STATEMENT_TIMESTAMP() - (r.retention / 10^9 * interval '1 second')`, // retention is in nanoseconds (time.Duration aka BIGINT) - ubig.New(o.chainID)) + result, err = o.ds.ExecContext(ctx, fmt.Sprintf(query, ""), ubig.New(o.chainID)) } if err != nil { diff --git a/core/chains/evm/logpoller/orm_test.go b/core/chains/evm/logpoller/orm_test.go index ed3f58504a..ee7f408061 100644 --- a/core/chains/evm/logpoller/orm_test.go +++ b/core/chains/evm/logpoller/orm_test.go @@ -7,6 +7,7 @@ import ( "fmt" "math" "math/big" + "strconv" "testing" "time" @@ -186,6 +187,7 @@ func TestORM_GetBlocks_From_Range_Recent_Blocks(t *testing.T) { } func TestORM(t *testing.T) { + t.Parallel() th := SetupTH(t, lpOpts) o1 := th.ORM o2 := th.ORM2 @@ -333,6 +335,36 @@ func TestORM(t *testing.T) { }, })) + // Insert a couple logs on a different chain, to make sure + // these aren't affected by any operations on the chain LogPoller + // is managing. + require.NoError(t, o2.InsertLogs(ctx, []logpoller.Log{ + { + EvmChainId: ubig.New(th.ChainID2), + LogIndex: 8, + BlockHash: common.HexToHash("0x1238"), + BlockNumber: int64(17), + EventSig: topic2, + Topics: [][]byte{topic2[:]}, + Address: common.HexToAddress("0x1236"), + TxHash: common.HexToHash("0x1888"), + Data: []byte("same log on unrelated chain"), + BlockTimestamp: time.Now(), + }, + { + EvmChainId: ubig.New(th.ChainID2), + LogIndex: 9, + BlockHash: common.HexToHash("0x1999"), + BlockNumber: int64(18), + EventSig: topic, + Topics: [][]byte{topic[:], topic2[:]}, + Address: common.HexToAddress("0x5555"), + TxHash: common.HexToHash("0x1543"), + Data: []byte("different log on unrelated chain"), + BlockTimestamp: time.Now(), + }, + })) + t.Log(latest.BlockNumber) logs, err := o1.SelectLogsByBlockRange(ctx, 1, 17) require.NoError(t, err) @@ -453,24 +485,38 @@ func TestORM(t *testing.T) { require.NoError(t, err) require.Len(t, logs, 8) - // Delete expired logs + // Delete expired logs with page limit time.Sleep(2 * time.Millisecond) // just in case we haven't reached the end of the 1ms retention period - deleted, err := o1.DeleteExpiredLogs(ctx, 0) + deleted, err := o1.DeleteExpiredLogs(ctx, 2) + require.NoError(t, err) + assert.Equal(t, int64(2), deleted) + + // Delete expired logs without page limit + deleted, err = o1.DeleteExpiredLogs(ctx, 0) require.NoError(t, err) - assert.Equal(t, int64(1), deleted) + assert.Equal(t, int64(2), deleted) + + // Ensure that both of the logs from the second chain are still there + logs, err = o2.SelectLogs(ctx, 0, 100, common.HexToAddress("0x1236"), topic2) + require.NoError(t, err) + assert.Len(t, logs, 1) + logs, err = o2.SelectLogs(ctx, 0, 100, common.HexToAddress("0x5555"), topic) + require.NoError(t, err) + assert.Len(t, logs, 1) + logs, err = o1.SelectLogsByBlockRange(ctx, 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) - // Importantly, it shouldn't delete any logs matching only filter0 (ret=0 meaning permanent retention). Anything - // matching filter12 should be kept regardless of what other filters it matches. - assert.Len(t, logs, 7) + // It should have retained the log matching filter0 (due to ret=0 meaning permanent retention) as well as all + // 3 logs matching filter12 (ret=1 hour). It should have deleted 3 logs not matching any filter, as well as 1 + // of the 2 logs matching filter1 (ret=1ms)--the one that doesn't also match filter12. + assert.Len(t, logs, 4) // Delete logs after should delete all logs. err = o1.DeleteLogsAndBlocksAfter(ctx, 1) require.NoError(t, err) logs, err = o1.SelectLogsByBlockRange(ctx, 1, latest.BlockNumber) require.NoError(t, err) - require.Zero(t, len(logs)) + assert.Zero(t, len(logs)) } type PgxLogger struct { @@ -642,7 +688,7 @@ func TestORM_IndexedLogs(t *testing.T) { require.NoError(t, err) assert.Equal(t, 2, len(lgs)) - blockRangeFilter := func(start, end uint64, topicIdx uint64, topicValues []uint64) query.KeyFilter { + blockRangeFilter := func(start, end string, topicIdx uint64, topicValues []uint64) query.KeyFilter { return query.KeyFilter{ Expressions: []query.Expression{ logpoller.NewAddressFilter(addr), @@ -658,7 +704,7 @@ func TestORM_IndexedLogs(t *testing.T) { require.NoError(t, err) assert.Equal(t, 1, len(lgs)) - lgs, err = o1.FilteredLogs(ctx, blockRangeFilter(1, 1, 1, []uint64{1}), limiter, "") + lgs, err = o1.FilteredLogs(ctx, blockRangeFilter("1", "1", 1, []uint64{1}), limiter, "") require.NoError(t, err) assert.Equal(t, 1, len(lgs)) @@ -666,7 +712,7 @@ func TestORM_IndexedLogs(t *testing.T) { require.NoError(t, err) assert.Equal(t, 1, len(lgs)) - lgs, err = o1.FilteredLogs(ctx, blockRangeFilter(1, 2, 1, []uint64{2}), limiter, "") + lgs, err = o1.FilteredLogs(ctx, blockRangeFilter("1", "2", 1, []uint64{2}), limiter, "") require.NoError(t, err) assert.Equal(t, 1, len(lgs)) @@ -674,7 +720,7 @@ func TestORM_IndexedLogs(t *testing.T) { require.NoError(t, err) assert.Equal(t, 1, len(lgs)) - lgs, err = o1.FilteredLogs(ctx, blockRangeFilter(1, 2, 1, []uint64{1}), limiter, "") + lgs, err = o1.FilteredLogs(ctx, blockRangeFilter("1", "2", 1, []uint64{1}), limiter, "") require.NoError(t, err) assert.Equal(t, 1, len(lgs)) @@ -682,7 +728,7 @@ func TestORM_IndexedLogs(t *testing.T) { require.Error(t, err) assert.Contains(t, err.Error(), "invalid index for topic: 0") - _, err = o1.FilteredLogs(ctx, blockRangeFilter(1, 2, 0, []uint64{1}), limiter, "") + _, err = o1.FilteredLogs(ctx, blockRangeFilter("1", "2", 0, []uint64{1}), limiter, "") require.Error(t, err) assert.Contains(t, err.Error(), "invalid index for topic: 0") @@ -690,7 +736,7 @@ func TestORM_IndexedLogs(t *testing.T) { require.Error(t, err) assert.Contains(t, err.Error(), "invalid index for topic: 4") - _, err = o1.FilteredLogs(ctx, blockRangeFilter(1, 2, 4, []uint64{1}), limiter, "") + _, err = o1.FilteredLogs(ctx, blockRangeFilter("1", "2", 4, []uint64{1}), limiter, "") require.Error(t, err) assert.Contains(t, err.Error(), "invalid index for topic: 4") @@ -1042,7 +1088,7 @@ func TestORM_SelectLogsWithSigsByBlockRangeFilter(t *testing.T) { } require.NoError(t, o1.InsertLogs(ctx, inputLogs)) - filter := func(sigs []common.Hash, startBlock, endBlock int64) query.KeyFilter { + filter := func(sigs []common.Hash, startBlock, endBlock string) query.KeyFilter { filters := []query.Expression{ logpoller.NewAddressFilter(sourceAddr), } @@ -1064,8 +1110,8 @@ func TestORM_SelectLogsWithSigsByBlockRangeFilter(t *testing.T) { filters = append(filters, query.Expression{ BoolExpression: query.BoolExpression{ Expressions: []query.Expression{ - query.Block(uint64(startBlock), primitives.Gte), - query.Block(uint64(endBlock), primitives.Lte), + query.Block(startBlock, primitives.Gte), + query.Block(endBlock, primitives.Lte), }, BoolOperator: query.AND, }, @@ -1097,8 +1143,7 @@ func TestORM_SelectLogsWithSigsByBlockRangeFilter(t *testing.T) { }) assertion(t, logs, err, startBlock, endBlock) - - logs, err = th.ORM.FilteredLogs(ctx, filter([]common.Hash{topic, topic2}, startBlock, endBlock), limiter, "") + logs, err = th.ORM.FilteredLogs(ctx, filter([]common.Hash{topic, topic2}, strconv.Itoa(int(startBlock)), strconv.Itoa(int(endBlock))), limiter, "") assertion(t, logs, err, startBlock, endBlock) } @@ -1160,7 +1205,7 @@ func TestLogPoller_Logs(t *testing.T) { assert.Equal(t, "0x0000000000000000000000000000000000000000000000000000000000000005", lgs[4].BlockHash.String()) assert.Equal(t, "0x0000000000000000000000000000000000000000000000000000000000000005", lgs[5].BlockHash.String()) - logFilter := func(start, end uint64, address common.Address) query.KeyFilter { + logFilter := func(start, end string, address common.Address) query.KeyFilter { return query.KeyFilter{ Expressions: []query.Expression{ logpoller.NewAddressFilter(address), @@ -1181,7 +1226,7 @@ func TestLogPoller_Logs(t *testing.T) { assert.Equal(t, "0x0000000000000000000000000000000000000000000000000000000000000005", lgs[1].BlockHash.String()) assert.Equal(t, address1, lgs[1].Address) - lgs, err = th.ORM.FilteredLogs(ctx, logFilter(1, 3, address1), query.LimitAndSort{ + lgs, err = th.ORM.FilteredLogs(ctx, logFilter("1", "3", address1), query.LimitAndSort{ SortBy: []query.SortBy{query.NewSortBySequence(query.Asc)}, }, "") require.NoError(t, err) @@ -1201,7 +1246,7 @@ func TestLogPoller_Logs(t *testing.T) { assert.Equal(t, address2, lgs[0].Address) assert.Equal(t, event1.Bytes(), lgs[0].Topics[0]) - lgs, err = th.ORM.FilteredLogs(ctx, logFilter(2, 2, address2), query.LimitAndSort{ + lgs, err = th.ORM.FilteredLogs(ctx, logFilter("2", "2", address2), query.LimitAndSort{ SortBy: []query.SortBy{query.NewSortBySequence(query.Asc)}, }, "") require.NoError(t, err) diff --git a/core/chains/evm/logpoller/parser_test.go b/core/chains/evm/logpoller/parser_test.go index 5e99ec7ba8..27af9e8318 100644 --- a/core/chains/evm/logpoller/parser_test.go +++ b/core/chains/evm/logpoller/parser_test.go @@ -141,7 +141,7 @@ func TestDSLParser(t *testing.T) { expressions := []query.Expression{ query.Timestamp(10, primitives.Eq), query.TxHash(common.HexToHash("0x84").String()), - query.Block(99, primitives.Neq), + query.Block("99", primitives.Neq), query.Confidence(primitives.Finalized), } limiter := query.NewLimitAndSort(query.CursorLimit("10-20-0x42", query.CursorPrevious, 20)) diff --git a/core/chains/evm/monitor/balance.go b/core/chains/evm/monitor/balance.go index b8194a38af..1f5275c13f 100644 --- a/core/chains/evm/monitor/balance.go +++ b/core/chains/evm/monitor/balance.go @@ -33,14 +33,15 @@ type ( } balanceMonitor struct { - services.StateMachine - logger logger.Logger + services.Service + eng *services.Engine + ethClient evmclient.Client chainID *big.Int chainIDStr string ethKeyStore keystore.Eth ethBalances map[gethCommon.Address]*assets.Eth - ethBalancesMtx *sync.RWMutex + ethBalancesMtx sync.RWMutex sleeperTask *utils.SleeperTask } @@ -53,62 +54,41 @@ var _ BalanceMonitor = (*balanceMonitor)(nil) func NewBalanceMonitor(ethClient evmclient.Client, ethKeyStore keystore.Eth, lggr logger.Logger) *balanceMonitor { chainId := ethClient.ConfiguredChainID() bm := &balanceMonitor{ - services.StateMachine{}, - logger.Named(lggr, "BalanceMonitor"), - ethClient, - chainId, - chainId.String(), - ethKeyStore, - make(map[gethCommon.Address]*assets.Eth), - new(sync.RWMutex), - nil, + ethClient: ethClient, + chainID: chainId, + chainIDStr: chainId.String(), + ethKeyStore: ethKeyStore, + ethBalances: make(map[gethCommon.Address]*assets.Eth), } + bm.Service, bm.eng = services.Config{ + Name: "BalanceMonitor", + Start: bm.start, + Close: bm.close, + }.NewServiceEngine(lggr) bm.sleeperTask = utils.NewSleeperTask(&worker{bm: bm}) return bm } -func (bm *balanceMonitor) Start(ctx context.Context) error { - return bm.StartOnce("BalanceMonitor", func() error { - // Always query latest balance on start - (&worker{bm}).WorkCtx(ctx) - return nil - }) -} - -// Close shuts down the BalanceMonitor, should not be used after this -func (bm *balanceMonitor) Close() error { - return bm.StopOnce("BalanceMonitor", func() error { - return bm.sleeperTask.Stop() - }) -} - -func (bm *balanceMonitor) Ready() error { +func (bm *balanceMonitor) start(ctx context.Context) error { + // Always query latest balance on start + (&worker{bm}).WorkCtx(ctx) return nil } -func (bm *balanceMonitor) Name() string { - return bm.logger.Name() -} - -func (bm *balanceMonitor) HealthReport() map[string]error { - return map[string]error{bm.Name(): bm.Healthy()} +// Close shuts down the BalanceMonitor, should not be used after this +func (bm *balanceMonitor) close() error { + return bm.sleeperTask.Stop() } // OnNewLongestChain checks the balance for each key -func (bm *balanceMonitor) OnNewLongestChain(_ context.Context, head *evmtypes.Head) { - ok := bm.IfStarted(func() { - bm.checkBalance(head) - }) +func (bm *balanceMonitor) OnNewLongestChain(_ context.Context, _ *evmtypes.Head) { + bm.eng.Debugw("BalanceMonitor: signalling balance worker") + ok := bm.sleeperTask.WakeUpIfStarted() if !ok { - bm.logger.Debugw("BalanceMonitor: ignoring OnNewLongestChain call, balance monitor is not started", "state", bm.State()) + bm.eng.Debugw("BalanceMonitor: ignoring OnNewLongestChain call, balance monitor is not started", "state", bm.sleeperTask.State()) } } -func (bm *balanceMonitor) checkBalance(head *evmtypes.Head) { - bm.logger.Debugw("BalanceMonitor: signalling balance worker") - bm.sleeperTask.WakeUp() -} - func (bm *balanceMonitor) updateBalance(ethBal assets.Eth, address gethCommon.Address) { bm.promUpdateEthBalance(ðBal, address) @@ -117,7 +97,7 @@ func (bm *balanceMonitor) updateBalance(ethBal assets.Eth, address gethCommon.Ad bm.ethBalances[address] = ðBal bm.ethBalancesMtx.Unlock() - lgr := logger.Named(bm.logger, "BalanceLog") + lgr := logger.Named(bm.eng, "BalanceLog") lgr = logger.With(lgr, "address", address.Hex(), "ethBalance", ethBal.String(), @@ -151,7 +131,7 @@ func (bm *balanceMonitor) promUpdateEthBalance(balance *assets.Eth, from gethCom balanceFloat, err := ApproximateFloat64(balance) if err != nil { - bm.logger.Error(fmt.Errorf("updatePrometheusEthBalance: %v", err)) + bm.eng.Error(fmt.Errorf("updatePrometheusEthBalance: %v", err)) return } @@ -174,7 +154,7 @@ func (w *worker) Work() { func (w *worker) WorkCtx(ctx context.Context) { enabledAddresses, err := w.bm.ethKeyStore.EnabledAddressesForChain(ctx, w.bm.chainID) if err != nil { - w.bm.logger.Error("BalanceMonitor: error getting keys", err) + w.bm.eng.Error("BalanceMonitor: error getting keys", err) } var wg sync.WaitGroup @@ -198,12 +178,12 @@ func (w *worker) checkAccountBalance(ctx context.Context, address gethCommon.Add bal, err := w.bm.ethClient.BalanceAt(ctx, address, nil) if err != nil { - w.bm.logger.Errorw(fmt.Sprintf("BalanceMonitor: error getting balance for key %s", address.Hex()), + w.bm.eng.Errorw(fmt.Sprintf("BalanceMonitor: error getting balance for key %s", address.Hex()), "err", err, "address", address, ) } else if bal == nil { - w.bm.logger.Errorw(fmt.Sprintf("BalanceMonitor: error getting balance for key %s: invariant violation, bal may not be nil", address.Hex()), + w.bm.eng.Errorw(fmt.Sprintf("BalanceMonitor: error getting balance for key %s: invariant violation, bal may not be nil", address.Hex()), "err", err, "address", address, ) diff --git a/core/chains/evm/txmgr/builder.go b/core/chains/evm/txmgr/builder.go index 8234d55b96..cbfb8775cf 100644 --- a/core/chains/evm/txmgr/builder.go +++ b/core/chains/evm/txmgr/builder.go @@ -1,6 +1,7 @@ package txmgr import ( + "context" "math/big" "time" @@ -18,6 +19,10 @@ import ( evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ) +type latestAndFinalizedBlockHeadTracker interface { + LatestAndFinalizedBlock(ctx context.Context) (latest, finalized *evmtypes.Head, err error) +} + // NewTxm constructs the necessary dependencies for the EvmTxm (broadcaster, confirmer, etc) and returns a new EvmTxManager func NewTxm( ds sqlutil.DataSource, @@ -32,6 +37,7 @@ func NewTxm( logPoller logpoller.LogPoller, keyStore keystore.Eth, estimator gas.EvmFeeEstimator, + headTracker latestAndFinalizedBlockHeadTracker, ) (txm TxManager, err error, ) { @@ -53,12 +59,13 @@ func NewTxm( evmBroadcaster := NewEvmBroadcaster(txStore, txmClient, txmCfg, feeCfg, txConfig, listenerConfig, keyStore, txAttemptBuilder, lggr, checker, chainConfig.NonceAutoSync(), chainConfig.ChainType()) evmTracker := NewEvmTracker(txStore, keyStore, chainID, lggr) stuckTxDetector := NewStuckTxDetector(lggr, client.ConfiguredChainID(), chainConfig.ChainType(), fCfg.PriceMax(), txConfig.AutoPurge(), estimator, txStore, client) - evmConfirmer := NewEvmConfirmer(txStore, txmClient, txmCfg, feeCfg, txConfig, dbConfig, keyStore, txAttemptBuilder, lggr, stuckTxDetector) + evmConfirmer := NewEvmConfirmer(txStore, txmClient, txmCfg, feeCfg, txConfig, dbConfig, keyStore, txAttemptBuilder, lggr, stuckTxDetector, headTracker) + evmFinalizer := NewEvmFinalizer(lggr, client.ConfiguredChainID(), chainConfig.RPCDefaultBatchSize(), txStore, client, headTracker) var evmResender *Resender if txConfig.ResendAfterThreshold() > 0 { evmResender = NewEvmResender(lggr, txStore, txmClient, evmTracker, keyStore, txmgr.DefaultResenderPollInterval, chainConfig, txConfig) } - txm = NewEvmTxm(chainID, txmCfg, txConfig, keyStore, lggr, checker, fwdMgr, txAttemptBuilder, txStore, evmBroadcaster, evmConfirmer, evmResender, evmTracker) + txm = NewEvmTxm(chainID, txmCfg, txConfig, keyStore, lggr, checker, fwdMgr, txAttemptBuilder, txStore, evmBroadcaster, evmConfirmer, evmResender, evmTracker, evmFinalizer) return txm, nil } @@ -77,8 +84,9 @@ func NewEvmTxm( confirmer *Confirmer, resender *Resender, tracker *Tracker, + finalizer Finalizer, ) *Txm { - return txmgr.NewTxm(chainId, cfg, txCfg, keyStore, lggr, checkerFactory, fwdMgr, txAttemptBuilder, txStore, broadcaster, confirmer, resender, tracker, client.NewTxError) + return txmgr.NewTxm(chainId, cfg, txCfg, keyStore, lggr, checkerFactory, fwdMgr, txAttemptBuilder, txStore, broadcaster, confirmer, resender, tracker, finalizer, client.NewTxError) } // NewEvmResender creates a new concrete EvmResender @@ -96,8 +104,8 @@ func NewEvmResender( } // NewEvmReaper instantiates a new EVM-specific reaper object -func NewEvmReaper(lggr logger.Logger, store txmgrtypes.TxHistoryReaper[*big.Int], config EvmReaperConfig, txConfig txmgrtypes.ReaperTransactionsConfig, chainID *big.Int) *Reaper { - return txmgr.NewReaper(lggr, store, config, txConfig, chainID) +func NewEvmReaper(lggr logger.Logger, store txmgrtypes.TxHistoryReaper[*big.Int], txConfig txmgrtypes.ReaperTransactionsConfig, chainID *big.Int) *Reaper { + return txmgr.NewReaper(lggr, store, txConfig, chainID) } // NewEvmConfirmer instantiates a new EVM confirmer @@ -112,8 +120,9 @@ func NewEvmConfirmer( txAttemptBuilder TxAttemptBuilder, lggr logger.Logger, stuckTxDetector StuckTxDetector, + headTracker latestAndFinalizedBlockHeadTracker, ) *Confirmer { - return txmgr.NewConfirmer(txStore, client, chainConfig, feeConfig, txConfig, dbConfig, keystore, txAttemptBuilder, lggr, func(r *evmtypes.Receipt) bool { return r == nil }, stuckTxDetector) + return txmgr.NewConfirmer(txStore, client, chainConfig, feeConfig, txConfig, dbConfig, keystore, txAttemptBuilder, lggr, func(r *evmtypes.Receipt) bool { return r == nil }, stuckTxDetector, headTracker) } // NewEvmTracker instantiates a new EVM tracker for abandoned transactions diff --git a/core/chains/evm/txmgr/client.go b/core/chains/evm/txmgr/client.go index 7d3570ed77..05f442e1cc 100644 --- a/core/chains/evm/txmgr/client.go +++ b/core/chains/evm/txmgr/client.go @@ -184,3 +184,7 @@ func (c *evmTxmClient) CallContract(ctx context.Context, a TxAttempt, blockNumbe }, blockNumber) return client.ExtractRPCError(errCall) } + +func (c *evmTxmClient) HeadByHash(ctx context.Context, hash common.Hash) (*evmtypes.Head, error) { + return c.client.HeadByHash(ctx, hash) +} diff --git a/core/chains/evm/txmgr/config.go b/core/chains/evm/txmgr/config.go index b53f99840b..af20c9a590 100644 --- a/core/chains/evm/txmgr/config.go +++ b/core/chains/evm/txmgr/config.go @@ -48,7 +48,6 @@ type ( EvmBroadcasterConfig txmgrtypes.BroadcasterChainConfig EvmConfirmerConfig txmgrtypes.ConfirmerChainConfig EvmResenderConfig txmgrtypes.ResenderChainConfig - EvmReaperConfig txmgrtypes.ReaperChainConfig ) var _ EvmTxmConfig = (*evmTxmConfig)(nil) diff --git a/core/chains/evm/txmgr/confirmer_test.go b/core/chains/evm/txmgr/confirmer_test.go index 82b668f168..a9dec223bf 100644 --- a/core/chains/evm/txmgr/confirmer_test.go +++ b/core/chains/evm/txmgr/confirmer_test.go @@ -34,6 +34,7 @@ import ( evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" gasmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/mocks" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/keystore" ksmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/keystore/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/testutils" @@ -131,7 +132,8 @@ func TestEthConfirmer_Lifecycle(t *testing.T) { feeEstimator := gas.NewEvmFeeEstimator(lggr, newEst, ge.EIP1559DynamicFees(), ge, ethClient) txBuilder := txmgr.NewEvmTxAttemptBuilder(*ethClient.ConfiguredChainID(), ge, ethKeyStore, feeEstimator) stuckTxDetector := txmgr.NewStuckTxDetector(lggr, testutils.FixtureChainID, "", assets.NewWei(assets.NewEth(100).ToInt()), config.EVM().Transactions().AutoPurge(), feeEstimator, txStore, ethClient) - ec := txmgr.NewEvmConfirmer(txStore, txmgr.NewEvmTxmClient(ethClient, nil), txmgr.NewEvmTxmConfig(config.EVM()), txmgr.NewEvmTxmFeeConfig(ge), config.EVM().Transactions(), gconfig.Database(), ethKeyStore, txBuilder, lggr, stuckTxDetector) + ht := headtracker.NewSimulatedHeadTracker(ethClient, true, 0) + ec := txmgr.NewEvmConfirmer(txStore, txmgr.NewEvmTxmClient(ethClient, nil), txmgr.NewEvmTxmConfig(config.EVM()), txmgr.NewEvmTxmFeeConfig(ge), config.EVM().Transactions(), gconfig.Database(), ethKeyStore, txBuilder, lggr, stuckTxDetector, ht) ctx := tests.Context(t) // Can't close unstarted instance @@ -145,19 +147,27 @@ func TestEthConfirmer_Lifecycle(t *testing.T) { // Can't start an already started instance err = ec.Start(ctx) require.Error(t, err) + + latestFinalizedHead := evmtypes.Head{ + Number: 8, + Hash: testutils.NewHash(), + Parent: nil, + IsFinalized: true, // We are guaranteed to receive a latestFinalizedHead. + } + head := evmtypes.Head{ Hash: testutils.NewHash(), Number: 10, Parent: &evmtypes.Head{ Hash: testutils.NewHash(), Number: 9, - Parent: &evmtypes.Head{ - Number: 8, - Hash: testutils.NewHash(), - Parent: nil, - }, + Parent: &latestFinalizedHead, }, } + + ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(&head, nil).Once() + ethClient.On("LatestFinalizedBlock", mock.Anything).Return(&latestFinalizedHead, nil).Once() + err = ec.ProcessHead(ctx, &head) require.NoError(t, err) // Can successfully close once @@ -199,6 +209,7 @@ func TestEthConfirmer_CheckForReceipts(t *testing.T) { nonce := int64(0) ctx := tests.Context(t) blockNum := int64(0) + latestFinalizedBlockNum := int64(0) t.Run("only finds eth_txes in unconfirmed state with at least one broadcast attempt", func(t *testing.T) { mustInsertFatalErrorEthTx(t, txStore, fromAddress) @@ -211,7 +222,7 @@ func TestEthConfirmer_CheckForReceipts(t *testing.T) { mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, config.EVM().ChainID()) // Do the thing - require.NoError(t, ec.CheckForReceipts(ctx, blockNum)) + require.NoError(t, ec.CheckForReceipts(ctx, blockNum, latestFinalizedBlockNum)) }) etx1 := cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, nonce, fromAddress) @@ -232,7 +243,7 @@ func TestEthConfirmer_CheckForReceipts(t *testing.T) { }).Once() // Do the thing - require.NoError(t, ec.CheckForReceipts(ctx, blockNum)) + require.NoError(t, ec.CheckForReceipts(ctx, blockNum, latestFinalizedBlockNum)) var err error etx1, err = txStore.FindTxWithAttempts(ctx, etx1.ID) @@ -261,7 +272,7 @@ func TestEthConfirmer_CheckForReceipts(t *testing.T) { }).Once() // No error because it is merely logged - require.NoError(t, ec.CheckForReceipts(ctx, blockNum)) + require.NoError(t, ec.CheckForReceipts(ctx, blockNum, latestFinalizedBlockNum)) etx, err := txStore.FindTxWithAttempts(ctx, etx1.ID) require.NoError(t, err) @@ -289,7 +300,7 @@ func TestEthConfirmer_CheckForReceipts(t *testing.T) { }).Once() // No error because it is merely logged - require.NoError(t, ec.CheckForReceipts(ctx, blockNum)) + require.NoError(t, ec.CheckForReceipts(ctx, blockNum, latestFinalizedBlockNum)) etx, err := txStore.FindTxWithAttempts(ctx, etx1.ID) require.NoError(t, err) @@ -326,7 +337,7 @@ func TestEthConfirmer_CheckForReceipts(t *testing.T) { }).Once() // Do the thing - require.NoError(t, ec.CheckForReceipts(ctx, blockNum)) + require.NoError(t, ec.CheckForReceipts(ctx, blockNum, latestFinalizedBlockNum)) // Check that the receipt was saved etx, err := txStore.FindTxWithAttempts(ctx, etx1.ID) @@ -388,7 +399,7 @@ func TestEthConfirmer_CheckForReceipts(t *testing.T) { }).Once() // Do the thing - require.NoError(t, ec.CheckForReceipts(ctx, blockNum)) + require.NoError(t, ec.CheckForReceipts(ctx, blockNum, latestFinalizedBlockNum)) // Check that the state was updated etx, err := txStore.FindTxWithAttempts(ctx, etx2.ID) @@ -416,7 +427,7 @@ func TestEthConfirmer_CheckForReceipts(t *testing.T) { }).Once() // Do the thing - require.NoError(t, ec.CheckForReceipts(ctx, blockNum)) + require.NoError(t, ec.CheckForReceipts(ctx, blockNum, latestFinalizedBlockNum)) // No receipt, but no error either etx, err := txStore.FindTxWithAttempts(ctx, etx3.ID) @@ -443,7 +454,7 @@ func TestEthConfirmer_CheckForReceipts(t *testing.T) { }).Once() // Do the thing - require.NoError(t, ec.CheckForReceipts(ctx, blockNum)) + require.NoError(t, ec.CheckForReceipts(ctx, blockNum, latestFinalizedBlockNum)) // No receipt, but no error either etx, err := txStore.FindTxWithAttempts(ctx, etx3.ID) @@ -472,7 +483,7 @@ func TestEthConfirmer_CheckForReceipts(t *testing.T) { }).Once() // Do the thing - require.NoError(t, ec.CheckForReceipts(ctx, blockNum)) + require.NoError(t, ec.CheckForReceipts(ctx, blockNum, latestFinalizedBlockNum)) // Check that the receipt was unchanged etx, err := txStore.FindTxWithAttempts(ctx, etx3.ID) @@ -523,7 +534,7 @@ func TestEthConfirmer_CheckForReceipts(t *testing.T) { }).Once() // Do the thing - require.NoError(t, ec.CheckForReceipts(ctx, blockNum)) + require.NoError(t, ec.CheckForReceipts(ctx, blockNum, latestFinalizedBlockNum)) // Check that the state was updated var err error @@ -576,7 +587,7 @@ func TestEthConfirmer_CheckForReceipts(t *testing.T) { }).Once() // Do the thing - require.NoError(t, ec.CheckForReceipts(ctx, blockNum)) + require.NoError(t, ec.CheckForReceipts(ctx, blockNum, latestFinalizedBlockNum)) // Check that the state was updated etx5, err = txStore.FindTxWithAttempts(ctx, etx5.ID) @@ -614,6 +625,7 @@ func TestEthConfirmer_CheckForReceipts_batching(t *testing.T) { etx := cltest.MustInsertUnconfirmedEthTx(t, txStore, 0, fromAddress) var attempts []txmgr.TxAttempt + latestFinalizedBlockNum := int64(0) // Total of 5 attempts should lead to 3 batched fetches (2, 2, 1) for i := 0; i < 5; i++ { @@ -650,7 +662,7 @@ func TestEthConfirmer_CheckForReceipts_batching(t *testing.T) { elems[0].Result = &evmtypes.Receipt{} }).Once() - require.NoError(t, ec.CheckForReceipts(ctx, 42)) + require.NoError(t, ec.CheckForReceipts(ctx, 42, latestFinalizedBlockNum)) } func TestEthConfirmer_CheckForReceipts_HandlesNonFwdTxsWithForwardingEnabled(t *testing.T) { @@ -671,6 +683,8 @@ func TestEthConfirmer_CheckForReceipts_HandlesNonFwdTxsWithForwardingEnabled(t * _, fromAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) ec := newEthConfirmer(t, txStore, ethClient, cfg, evmcfg, ethKeyStore, nil) ctx := tests.Context(t) + latestFinalizedBlockNum := int64(0) + // tx is not forwarded and doesn't have meta set. EthConfirmer should handle nil meta values etx := cltest.MustInsertUnconfirmedEthTx(t, txStore, 0, fromAddress) attempt := newBroadcastLegacyEthTxAttempt(t, etx.ID, 2) @@ -697,7 +711,7 @@ func TestEthConfirmer_CheckForReceipts_HandlesNonFwdTxsWithForwardingEnabled(t * *(elems[0].Result.(*evmtypes.Receipt)) = txmReceipt // confirmed }).Once() - require.NoError(t, ec.CheckForReceipts(ctx, 42)) + require.NoError(t, ec.CheckForReceipts(ctx, 42, latestFinalizedBlockNum)) // Check receipt is inserted correctly. dbtx, err = txStore.FindTxWithAttempts(ctx, etx.ID) @@ -724,6 +738,7 @@ func TestEthConfirmer_CheckForReceipts_only_likely_confirmed(t *testing.T) { ec := newEthConfirmer(t, txStore, ethClient, cfg, evmcfg, ethKeyStore, nil) ctx := tests.Context(t) + latestFinalizedBlockNum := int64(0) var attempts []txmgr.TxAttempt // inserting in DESC nonce order to test DB ASC ordering @@ -755,7 +770,7 @@ func TestEthConfirmer_CheckForReceipts_only_likely_confirmed(t *testing.T) { elems[3].Result = &evmtypes.Receipt{} }).Once() - require.NoError(t, ec.CheckForReceipts(ctx, 42)) + require.NoError(t, ec.CheckForReceipts(ctx, 42, latestFinalizedBlockNum)) cltest.BatchElemMustMatchParams(t, captured[0], attempts[0].Hash, "eth_getTransactionReceipt") cltest.BatchElemMustMatchParams(t, captured[1], attempts[1].Hash, "eth_getTransactionReceipt") @@ -778,6 +793,7 @@ func TestEthConfirmer_CheckForReceipts_should_not_check_for_likely_unconfirmed(t ec := newEthConfirmer(t, txStore, ethClient, gconfig, config, ethKeyStore, nil) ctx := tests.Context(t) + latestFinalizedBlockNum := int64(0) etx := cltest.MustInsertUnconfirmedEthTx(t, txStore, 1, fromAddress) for i := 0; i < 4; i++ { @@ -788,7 +804,7 @@ func TestEthConfirmer_CheckForReceipts_should_not_check_for_likely_unconfirmed(t // latest nonce is lower that all attempts' nonces ethClient.On("SequenceAt", mock.Anything, mock.Anything, mock.Anything).Return(evmtypes.Nonce(0), nil) - require.NoError(t, ec.CheckForReceipts(ctx, 42)) + require.NoError(t, ec.CheckForReceipts(ctx, 42, latestFinalizedBlockNum)) } func TestEthConfirmer_CheckForReceipts_confirmed_missing_receipt_scoped_to_key(t *testing.T) { @@ -809,6 +825,7 @@ func TestEthConfirmer_CheckForReceipts_confirmed_missing_receipt_scoped_to_key(t ec := newEthConfirmer(t, txStore, ethClient, cfg, evmcfg, ethKeyStore, nil) ctx := tests.Context(t) + latestFinalizedBlockNum := int64(0) // STATE // key 1, tx with nonce 0 is unconfirmed @@ -832,7 +849,7 @@ func TestEthConfirmer_CheckForReceipts_confirmed_missing_receipt_scoped_to_key(t *(elems[0].Result.(*evmtypes.Receipt)) = txmReceipt2_9 }).Once() - require.NoError(t, ec.CheckForReceipts(ctx, 10)) + require.NoError(t, ec.CheckForReceipts(ctx, 10, latestFinalizedBlockNum)) mustTxBeInState(t, txStore, etx1_0, txmgrcommon.TxUnconfirmed) mustTxBeInState(t, txStore, etx1_1, txmgrcommon.TxUnconfirmed) @@ -850,7 +867,7 @@ func TestEthConfirmer_CheckForReceipts_confirmed_missing_receipt_scoped_to_key(t *(elems[0].Result.(*evmtypes.Receipt)) = txmReceipt1_1 }).Once() - require.NoError(t, ec.CheckForReceipts(ctx, 11)) + require.NoError(t, ec.CheckForReceipts(ctx, 11, latestFinalizedBlockNum)) mustTxBeInState(t, txStore, etx1_0, txmgrcommon.TxConfirmedMissingReceipt) mustTxBeInState(t, txStore, etx1_1, txmgrcommon.TxConfirmed) @@ -861,9 +878,7 @@ func TestEthConfirmer_CheckForReceipts_confirmed_missing_receipt(t *testing.T) { t.Parallel() db := pgtest.NewSqlxDB(t) - cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.EVM[0].FinalityDepth = ptr[uint32](50) - }) + cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) {}) txStore := cltest.NewTestTxStore(t, db) ethKeyStore := cltest.NewKeyStore(t, db).Eth() @@ -876,6 +891,7 @@ func TestEthConfirmer_CheckForReceipts_confirmed_missing_receipt(t *testing.T) { ec := newEthConfirmer(t, txStore, ethClient, cfg, evmcfg, ethKeyStore, nil) ctx := tests.Context(t) + latestFinalizedBlockNum := int64(0) // STATE // eth_txes with nonce 0 has two attempts (broadcast before block 21 and 41) the first of which will get a receipt @@ -949,7 +965,7 @@ func TestEthConfirmer_CheckForReceipts_confirmed_missing_receipt(t *testing.T) { // PERFORM // Block num of 43 is one higher than the receipt (as would generally be expected) - require.NoError(t, ec.CheckForReceipts(ctx, 43)) + require.NoError(t, ec.CheckForReceipts(ctx, 43, latestFinalizedBlockNum)) // Expected state is that the "top" eth_tx is now confirmed, with the // two below it "confirmed_missing_receipt" and the "bottom" eth_tx also confirmed @@ -1009,7 +1025,7 @@ func TestEthConfirmer_CheckForReceipts_confirmed_missing_receipt(t *testing.T) { // PERFORM // Block num of 44 is one higher than the receipt (as would generally be expected) - require.NoError(t, ec.CheckForReceipts(ctx, 44)) + require.NoError(t, ec.CheckForReceipts(ctx, 44, latestFinalizedBlockNum)) // Expected state is that the "top" two eth_txes are now confirmed, with the // one below it still "confirmed_missing_receipt" and the bottom one remains confirmed @@ -1038,7 +1054,7 @@ func TestEthConfirmer_CheckForReceipts_confirmed_missing_receipt(t *testing.T) { // eth_txes with nonce 2 is confirmed // eth_txes with nonce 3 is confirmed - t.Run("continues to leave eth_txes with state 'confirmed_missing_receipt' unchanged if at least one attempt is above EVM.FinalityDepth", func(t *testing.T) { + t.Run("continues to leave eth_txes with state 'confirmed_missing_receipt' unchanged if at least one attempt is above LatestFinalizedBlockNum", func(t *testing.T) { ethClient.On("SequenceAt", mock.Anything, mock.Anything, mock.Anything).Return(evmtypes.Nonce(10), nil) ethClient.On("BatchCallContext", mock.Anything, mock.MatchedBy(func(b []rpc.BatchElem) bool { return len(b) == 2 && @@ -1051,9 +1067,11 @@ func TestEthConfirmer_CheckForReceipts_confirmed_missing_receipt(t *testing.T) { elems[1].Result = &evmtypes.Receipt{} }).Once() + latestFinalizedBlockNum = 30 + // PERFORM // Block num of 80 puts the first attempt (21) below threshold but second attempt (41) still above - require.NoError(t, ec.CheckForReceipts(ctx, 80)) + require.NoError(t, ec.CheckForReceipts(ctx, 80, latestFinalizedBlockNum)) // Expected state is that the "top" two eth_txes are now confirmed, with the // one below it still "confirmed_missing_receipt" and the bottom one remains confirmed @@ -1078,7 +1096,7 @@ func TestEthConfirmer_CheckForReceipts_confirmed_missing_receipt(t *testing.T) { // eth_txes with nonce 2 is confirmed // eth_txes with nonce 3 is confirmed - t.Run("marks eth_Txes with state 'confirmed_missing_receipt' as 'errored' if a receipt fails to show up and all attempts are buried deeper than EVM.FinalityDepth", func(t *testing.T) { + t.Run("marks eth_Txes with state 'confirmed_missing_receipt' as 'errored' if a receipt fails to show up and all attempts are buried deeper than LatestFinalizedBlockNum", func(t *testing.T) { ethClient.On("SequenceAt", mock.Anything, mock.Anything, mock.Anything).Return(evmtypes.Nonce(10), nil) ethClient.On("BatchCallContext", mock.Anything, mock.MatchedBy(func(b []rpc.BatchElem) bool { return len(b) == 2 && @@ -1091,9 +1109,11 @@ func TestEthConfirmer_CheckForReceipts_confirmed_missing_receipt(t *testing.T) { elems[1].Result = &evmtypes.Receipt{} }).Once() + latestFinalizedBlockNum = 50 + // PERFORM // Block num of 100 puts the first attempt (21) and second attempt (41) below threshold - require.NoError(t, ec.CheckForReceipts(ctx, 100)) + require.NoError(t, ec.CheckForReceipts(ctx, 100, latestFinalizedBlockNum)) // Expected state is that the "top" two eth_txes are now confirmed, with the // one below it marked as "fatal_error" and the bottom one remains confirmed @@ -1117,9 +1137,7 @@ func TestEthConfirmer_CheckConfirmedMissingReceipt(t *testing.T) { t.Parallel() db := pgtest.NewSqlxDB(t) - cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.EVM[0].FinalityDepth = ptr[uint32](50) - }) + cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) {}) txStore := cltest.NewTestTxStore(t, db) ethKeyStore := cltest.NewKeyStore(t, db).Eth() @@ -1197,9 +1215,7 @@ func TestEthConfirmer_CheckConfirmedMissingReceipt_batchSendTransactions_fails(t t.Parallel() db := pgtest.NewSqlxDB(t) - cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.EVM[0].FinalityDepth = ptr[uint32](50) - }) + cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) {}) txStore := cltest.NewTestTxStore(t, db) ethKeyStore := cltest.NewKeyStore(t, db).Eth() @@ -1262,7 +1278,6 @@ func TestEthConfirmer_CheckConfirmedMissingReceipt_smallEvmRPCBatchSize_middleBa db := pgtest.NewSqlxDB(t) cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.EVM[0].FinalityDepth = ptr[uint32](50) c.EVM[0].RPCDefaultBatchSize = ptr[uint32](1) }) txStore := cltest.NewTestTxStore(t, db) @@ -1651,8 +1666,9 @@ func TestEthConfirmer_RebroadcastWhereNecessary_WithConnectivityCheck(t *testing addresses := []gethCommon.Address{fromAddress} kst.On("EnabledAddressesForChain", mock.Anything, &cltest.FixtureChainID).Return(addresses, nil).Maybe() stuckTxDetector := txmgr.NewStuckTxDetector(lggr, testutils.FixtureChainID, "", assets.NewWei(assets.NewEth(100).ToInt()), ccfg.EVM().Transactions().AutoPurge(), feeEstimator, txStore, ethClient) + ht := headtracker.NewSimulatedHeadTracker(ethClient, true, 0) // Create confirmer with necessary state - ec := txmgr.NewEvmConfirmer(txStore, txmgr.NewEvmTxmClient(ethClient, nil), ccfg.EVM(), txmgr.NewEvmTxmFeeConfig(ccfg.EVM().GasEstimator()), ccfg.EVM().Transactions(), cfg.Database(), kst, txBuilder, lggr, stuckTxDetector) + ec := txmgr.NewEvmConfirmer(txStore, txmgr.NewEvmTxmClient(ethClient, nil), ccfg.EVM(), txmgr.NewEvmTxmFeeConfig(ccfg.EVM().GasEstimator()), ccfg.EVM().Transactions(), cfg.Database(), kst, txBuilder, lggr, stuckTxDetector, ht) servicetest.Run(t, ec) currentHead := int64(30) oldEnough := int64(15) @@ -1700,7 +1716,8 @@ func TestEthConfirmer_RebroadcastWhereNecessary_WithConnectivityCheck(t *testing addresses := []gethCommon.Address{fromAddress} kst.On("EnabledAddressesForChain", mock.Anything, &cltest.FixtureChainID).Return(addresses, nil).Maybe() stuckTxDetector := txmgr.NewStuckTxDetector(lggr, testutils.FixtureChainID, "", assets.NewWei(assets.NewEth(100).ToInt()), ccfg.EVM().Transactions().AutoPurge(), feeEstimator, txStore, ethClient) - ec := txmgr.NewEvmConfirmer(txStore, txmgr.NewEvmTxmClient(ethClient, nil), ccfg.EVM(), txmgr.NewEvmTxmFeeConfig(ccfg.EVM().GasEstimator()), ccfg.EVM().Transactions(), cfg.Database(), kst, txBuilder, lggr, stuckTxDetector) + ht := headtracker.NewSimulatedHeadTracker(ethClient, true, 0) + ec := txmgr.NewEvmConfirmer(txStore, txmgr.NewEvmTxmClient(ethClient, nil), ccfg.EVM(), txmgr.NewEvmTxmFeeConfig(ccfg.EVM().GasEstimator()), ccfg.EVM().Transactions(), cfg.Database(), kst, txBuilder, lggr, stuckTxDetector, ht) servicetest.Run(t, ec) currentHead := int64(30) oldEnough := int64(15) @@ -2724,6 +2741,13 @@ func TestEthConfirmer_EnsureConfirmedTransactionsInLongestChain(t *testing.T) { gconfig, config := newTestChainScopedConfig(t) ec := newEthConfirmer(t, txStore, ethClient, gconfig, config, ethKeyStore, nil) + latestFinalizedHead := evmtypes.Head{ + Number: 8, + Hash: testutils.NewHash(), + Parent: nil, + IsFinalized: false, // We are guaranteed to receive a latestFinalizedHead. + } + head := evmtypes.Head{ Hash: testutils.NewHash(), Number: 10, @@ -2737,16 +2761,15 @@ func TestEthConfirmer_EnsureConfirmedTransactionsInLongestChain(t *testing.T) { }, }, } - t.Run("does nothing if there aren't any transactions", func(t *testing.T) { - require.NoError(t, ec.EnsureConfirmedTransactionsInLongestChain(tests.Context(t), &head)) + require.NoError(t, ec.EnsureConfirmedTransactionsInLongestChain(tests.Context(t), &head, latestFinalizedHead.BlockNumber())) }) t.Run("does nothing to unconfirmed transactions", func(t *testing.T) { etx := cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 0, fromAddress) // Do the thing - require.NoError(t, ec.EnsureConfirmedTransactionsInLongestChain(tests.Context(t), &head)) + require.NoError(t, ec.EnsureConfirmedTransactionsInLongestChain(tests.Context(t), &head, latestFinalizedHead.BlockNumber())) etx, err := txStore.FindTxWithAttempts(ctx, etx.ID) require.NoError(t, err) @@ -2758,7 +2781,7 @@ func TestEthConfirmer_EnsureConfirmedTransactionsInLongestChain(t *testing.T) { mustInsertEthReceipt(t, txStore, head.Number, head.Hash, etx.TxAttempts[0].Hash) // Do the thing - require.NoError(t, ec.EnsureConfirmedTransactionsInLongestChain(tests.Context(t), &head)) + require.NoError(t, ec.EnsureConfirmedTransactionsInLongestChain(tests.Context(t), &head, latestFinalizedHead.BlockNumber())) etx, err := txStore.FindTxWithAttempts(ctx, etx.ID) require.NoError(t, err) @@ -2771,7 +2794,7 @@ func TestEthConfirmer_EnsureConfirmedTransactionsInLongestChain(t *testing.T) { mustInsertEthReceipt(t, txStore, head.Parent.Parent.Number-1, testutils.NewHash(), etx.TxAttempts[0].Hash) // Do the thing - require.NoError(t, ec.EnsureConfirmedTransactionsInLongestChain(tests.Context(t), &head)) + require.NoError(t, ec.EnsureConfirmedTransactionsInLongestChain(tests.Context(t), &head, latestFinalizedHead.BlockNumber())) etx, err := txStore.FindTxWithAttempts(ctx, etx.ID) require.NoError(t, err) @@ -2792,7 +2815,7 @@ func TestEthConfirmer_EnsureConfirmedTransactionsInLongestChain(t *testing.T) { }), fromAddress).Return(commonclient.Successful, nil).Once() // Do the thing - require.NoError(t, ec.EnsureConfirmedTransactionsInLongestChain(tests.Context(t), &head)) + require.NoError(t, ec.EnsureConfirmedTransactionsInLongestChain(tests.Context(t), &head, latestFinalizedHead.BlockNumber())) etx, err := txStore.FindTxWithAttempts(ctx, etx.ID) require.NoError(t, err) @@ -2815,7 +2838,7 @@ func TestEthConfirmer_EnsureConfirmedTransactionsInLongestChain(t *testing.T) { commonclient.Successful, nil).Once() // Do the thing - require.NoError(t, ec.EnsureConfirmedTransactionsInLongestChain(tests.Context(t), &head)) + require.NoError(t, ec.EnsureConfirmedTransactionsInLongestChain(tests.Context(t), &head, latestFinalizedHead.BlockNumber())) etx, err := txStore.FindTxWithAttempts(ctx, etx.ID) require.NoError(t, err) @@ -2850,7 +2873,7 @@ func TestEthConfirmer_EnsureConfirmedTransactionsInLongestChain(t *testing.T) { }), fromAddress).Return(commonclient.Successful, nil).Once() // Do the thing - require.NoError(t, ec.EnsureConfirmedTransactionsInLongestChain(tests.Context(t), &head)) + require.NoError(t, ec.EnsureConfirmedTransactionsInLongestChain(tests.Context(t), &head, latestFinalizedHead.BlockNumber())) etx, err := txStore.FindTxWithAttempts(ctx, etx.ID) require.NoError(t, err) @@ -2870,7 +2893,7 @@ func TestEthConfirmer_EnsureConfirmedTransactionsInLongestChain(t *testing.T) { // Add receipt that is higher than head mustInsertEthReceipt(t, txStore, head.Number+1, testutils.NewHash(), attempt.Hash) - require.NoError(t, ec.EnsureConfirmedTransactionsInLongestChain(tests.Context(t), &head)) + require.NoError(t, ec.EnsureConfirmedTransactionsInLongestChain(tests.Context(t), &head, latestFinalizedHead.BlockNumber())) etx, err := txStore.FindTxWithAttempts(ctx, etx.ID) require.NoError(t, err) @@ -3210,7 +3233,8 @@ func TestEthConfirmer_ProcessStuckTransactions(t *testing.T) { ge := evmcfg.EVM().GasEstimator() txBuilder := txmgr.NewEvmTxAttemptBuilder(*ethClient.ConfiguredChainID(), ge, ethKeyStore, feeEstimator) stuckTxDetector := txmgr.NewStuckTxDetector(lggr, testutils.FixtureChainID, "", assets.NewWei(assets.NewEth(100).ToInt()), evmcfg.EVM().Transactions().AutoPurge(), feeEstimator, txStore, ethClient) - ec := txmgr.NewEvmConfirmer(txStore, txmgr.NewEvmTxmClient(ethClient, nil), txmgr.NewEvmTxmConfig(evmcfg.EVM()), txmgr.NewEvmTxmFeeConfig(ge), evmcfg.EVM().Transactions(), cfg.Database(), ethKeyStore, txBuilder, lggr, stuckTxDetector) + ht := headtracker.NewSimulatedHeadTracker(ethClient, true, 0) + ec := txmgr.NewEvmConfirmer(txStore, txmgr.NewEvmTxmClient(ethClient, nil), txmgr.NewEvmTxmConfig(evmcfg.EVM()), txmgr.NewEvmTxmFeeConfig(ge), evmcfg.EVM().Transactions(), cfg.Database(), ethKeyStore, txBuilder, lggr, stuckTxDetector, ht) servicetest.Run(t, ec) ctx := tests.Context(t) @@ -3224,9 +3248,13 @@ func TestEthConfirmer_ProcessStuckTransactions(t *testing.T) { tx := mustInsertUnconfirmedTxWithBroadcastAttempts(t, txStore, nonce, fromAddress, autoPurgeMinAttempts, blockNum-int64(autoPurgeThreshold), marketGasPrice.Add(oneGwei)) head := evmtypes.Head{ - Hash: testutils.NewHash(), - Number: blockNum, + Hash: testutils.NewHash(), + Number: blockNum, + IsFinalized: true, } + + ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(&head, nil).Once() + ethClient.On("LatestFinalizedBlock", mock.Anything).Return(&head, nil).Once() ethClient.On("SequenceAt", mock.Anything, mock.Anything, mock.Anything).Return(evmtypes.Nonce(0), nil).Once() ethClient.On("BatchCallContext", mock.Anything, mock.Anything).Return(nil).Once() @@ -3248,9 +3276,12 @@ func TestEthConfirmer_ProcessStuckTransactions(t *testing.T) { require.Equal(t, bumpedFee.Legacy, latestAttempt.TxFee.Legacy) head = evmtypes.Head{ - Hash: testutils.NewHash(), - Number: blockNum + 1, + Hash: testutils.NewHash(), + Number: blockNum + 1, + IsFinalized: true, } + ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(&head, nil).Once() + ethClient.On("LatestFinalizedBlock", mock.Anything).Return(&head, nil).Once() ethClient.On("SequenceAt", mock.Anything, mock.Anything, mock.Anything).Return(evmtypes.Nonce(1), nil) ethClient.On("BatchCallContext", mock.Anything, mock.MatchedBy(func(b []rpc.BatchElem) bool { return len(b) == 4 && cltest.BatchElemMatchesParams(b[0], latestAttempt.Hash, "eth_getTransactionReceipt") @@ -3289,7 +3320,8 @@ func newEthConfirmer(t testing.TB, txStore txmgr.EvmTxStore, ethClient client.Cl }, ge.EIP1559DynamicFees(), ge, ethClient) txBuilder := txmgr.NewEvmTxAttemptBuilder(*ethClient.ConfiguredChainID(), ge, ks, estimator) stuckTxDetector := txmgr.NewStuckTxDetector(lggr, testutils.FixtureChainID, "", assets.NewWei(assets.NewEth(100).ToInt()), config.EVM().Transactions().AutoPurge(), estimator, txStore, ethClient) - ec := txmgr.NewEvmConfirmer(txStore, txmgr.NewEvmTxmClient(ethClient, nil), txmgr.NewEvmTxmConfig(config.EVM()), txmgr.NewEvmTxmFeeConfig(ge), config.EVM().Transactions(), gconfig.Database(), ks, txBuilder, lggr, stuckTxDetector) + ht := headtracker.NewSimulatedHeadTracker(ethClient, true, 0) + ec := txmgr.NewEvmConfirmer(txStore, txmgr.NewEvmTxmClient(ethClient, nil), txmgr.NewEvmTxmConfig(config.EVM()), txmgr.NewEvmTxmFeeConfig(ge), config.EVM().Transactions(), gconfig.Database(), ks, txBuilder, lggr, stuckTxDetector, ht) ec.SetResumeCallback(fn) servicetest.Run(t, ec) return ec diff --git a/core/chains/evm/txmgr/evm_tx_store.go b/core/chains/evm/txmgr/evm_tx_store.go index e83a83907e..fa2251168d 100644 --- a/core/chains/evm/txmgr/evm_tx_store.go +++ b/core/chains/evm/txmgr/evm_tx_store.go @@ -34,8 +34,8 @@ import ( var ( ErrKeyNotUpdated = errors.New("evmTxStore: Key not updated") - // ErrCouldNotGetReceipt is the error string we save if we reach our finality depth for a confirmed transaction without ever getting a receipt - // This most likely happened because an external wallet used the account for this nonce + // ErrCouldNotGetReceipt is the error string we save if we reach our LatestFinalizedBlockNum for a confirmed transaction + // without ever getting a receipt. This most likely happened because an external wallet used the account for this nonce ErrCouldNotGetReceipt = "could not get receipt" ) @@ -44,6 +44,10 @@ type EvmTxStore interface { // redeclare TxStore for mockery txmgrtypes.TxStore[common.Address, *big.Int, common.Hash, common.Hash, *evmtypes.Receipt, evmtypes.Nonce, gas.EvmFee] TxStoreWebApi + + // methods used solely in EVM components + FindConfirmedTxesReceipts(ctx context.Context, finalizedBlockNum int64, chainID *big.Int) (receipts []Receipt, err error) + UpdateTxStatesToFinalizedUsingReceiptIds(ctx context.Context, etxIDs []int64, chainId *big.Int) error } // TxStoreWebApi encapsulates the methods that are not used by the txmgr and only used by the various web controllers, readers, or evm specific components @@ -87,7 +91,7 @@ var _ TestEvmTxStore = (*evmTxStore)(nil) // Directly maps to columns of database table "evm.receipts". // Do not modify type unless you // intend to modify the database schema -type dbReceipt struct { +type DbReceipt struct { ID int64 TxHash common.Hash BlockHash common.Hash @@ -97,8 +101,8 @@ type dbReceipt struct { CreatedAt time.Time } -func DbReceiptFromEvmReceipt(evmReceipt *evmtypes.Receipt) dbReceipt { - return dbReceipt{ +func DbReceiptFromEvmReceipt(evmReceipt *evmtypes.Receipt) DbReceipt { + return DbReceipt{ TxHash: evmReceipt.TxHash, BlockHash: evmReceipt.BlockHash, BlockNumber: evmReceipt.BlockNumber.Int64(), @@ -107,7 +111,7 @@ func DbReceiptFromEvmReceipt(evmReceipt *evmtypes.Receipt) dbReceipt { } } -func DbReceiptToEvmReceipt(receipt *dbReceipt) *evmtypes.Receipt { +func DbReceiptToEvmReceipt(receipt *DbReceipt) *evmtypes.Receipt { return &receipt.Receipt } @@ -131,7 +135,7 @@ type dbReceiptPlus struct { FailOnRevert bool `db:"FailOnRevert"` } -func fromDBReceipts(rs []dbReceipt) []*evmtypes.Receipt { +func fromDBReceipts(rs []DbReceipt) []*evmtypes.Receipt { receipts := make([]*evmtypes.Receipt, len(rs)) for i := 0; i < len(rs); i++ { receipts[i] = DbReceiptToEvmReceipt(&rs[i]) @@ -665,10 +669,8 @@ func (o *evmTxStore) loadEthTxAttemptsReceipts(ctx context.Context, etx *Tx) (er return o.loadEthTxesAttemptsReceipts(ctx, []*Tx{etx}) } -func (o *evmTxStore) loadEthTxesAttemptsReceipts(ctx context.Context, etxs []*Tx) (err error) { - if len(etxs) == 0 { - return nil - } +// initEthTxesAttempts takes an input txes slice, return an initialized attempt map and attemptHashes slice +func initEthTxesAttempts(etxs []*Tx) (map[common.Hash]*TxAttempt, [][]byte) { attemptHashM := make(map[common.Hash]*TxAttempt, len(etxs)) // len here is lower bound attemptHashes := make([][]byte, len(etxs)) // len here is lower bound for _, etx := range etxs { @@ -677,7 +679,17 @@ func (o *evmTxStore) loadEthTxesAttemptsReceipts(ctx context.Context, etxs []*Tx attemptHashes = append(attemptHashes, attempt.Hash.Bytes()) } } - var rs []dbReceipt + + return attemptHashM, attemptHashes +} + +func (o *evmTxStore) loadEthTxesAttemptsReceipts(ctx context.Context, etxs []*Tx) (err error) { + if len(etxs) == 0 { + return nil + } + + attemptHashM, attemptHashes := initEthTxesAttempts(etxs) + var rs []DbReceipt if err = o.q.SelectContext(ctx, &rs, `SELECT * FROM evm.receipts WHERE tx_hash = ANY($1)`, pq.Array(attemptHashes)); err != nil { return pkgerrors.Wrap(err, "loadEthTxesAttemptsReceipts failed to load evm.receipts") } @@ -693,6 +705,37 @@ func (o *evmTxStore) loadEthTxesAttemptsReceipts(ctx context.Context, etxs []*Tx return nil } +// loadEthTxesAttemptsWithPartialReceipts loads ethTxes with attempts and partial receipts values for optimization +func (o *evmTxStore) loadEthTxesAttemptsWithPartialReceipts(ctx context.Context, etxs []*Tx) (err error) { + if len(etxs) == 0 { + return nil + } + + attemptHashM, attemptHashes := initEthTxesAttempts(etxs) + var rs []DbReceipt + if err = o.q.SelectContext(ctx, &rs, `SELECT evm.receipts.block_hash, evm.receipts.block_number, evm.receipts.transaction_index, evm.receipts.tx_hash FROM evm.receipts WHERE tx_hash = ANY($1)`, pq.Array(attemptHashes)); err != nil { + return pkgerrors.Wrap(err, "loadEthTxesAttemptsReceipts failed to load evm.receipts") + } + + receipts := make([]*evmtypes.Receipt, len(rs)) + for i := 0; i < len(rs); i++ { + receipts[i] = &evmtypes.Receipt{ + BlockHash: rs[i].BlockHash, + BlockNumber: big.NewInt(rs[i].BlockNumber), + TransactionIndex: rs[i].TransactionIndex, + TxHash: rs[i].TxHash, + } + } + + for _, receipt := range receipts { + attempt := attemptHashM[receipt.TxHash] + // Although the attempts struct supports multiple receipts, the expectation for EVM is that there is only one receipt + // per tx and therefore attempt too. + attempt.Receipts = append(attempt.Receipts, receipt) + } + return nil +} + func loadConfirmedAttemptsReceipts(ctx context.Context, q sqlutil.DataSource, attempts []TxAttempt) error { byHash := make(map[string]*TxAttempt, len(attempts)) hashes := make([][]byte, len(attempts)) @@ -700,7 +743,7 @@ func loadConfirmedAttemptsReceipts(ctx context.Context, q sqlutil.DataSource, at byHash[attempt.Hash.String()] = &attempts[i] hashes = append(hashes, attempt.Hash.Bytes()) } - var rs []dbReceipt + var rs []DbReceipt if err := q.SelectContext(ctx, &rs, `SELECT * FROM evm.receipts WHERE tx_hash = ANY($1)`, pq.Array(hashes)); err != nil { return pkgerrors.Wrap(err, "loadConfirmedAttemptsReceipts failed to load evm.receipts") } @@ -955,11 +998,11 @@ func (o *evmTxStore) SaveFetchedReceipts(ctx context.Context, r []*evmtypes.Rece // NOTE: We continue to attempt to resend evm.txes in this state on // every head to guard against the extremely rare scenario of nonce gap due to // reorg that excludes the transaction (from another wallet) that had this -// nonce (until finality depth is reached, after which we make the explicit +// nonce (until LatestFinalizedBlockNum is reached, after which we make the explicit // decision to give up). This is done in the EthResender. // // We will continue to try to fetch a receipt for these attempts until all -// attempts are below the finality depth from current head. +// attempts are equal to or below the LatestFinalizedBlockNum from current head. func (o *evmTxStore) MarkAllConfirmedMissingReceipt(ctx context.Context, chainID *big.Int) (err error) { var cancel context.CancelFunc ctx, cancel = o.stopCh.Ctx(ctx) @@ -1116,7 +1159,7 @@ func updateEthTxAttemptUnbroadcast(ctx context.Context, orm *evmTxStore, attempt func updateEthTxUnconfirm(ctx context.Context, orm *evmTxStore, etx Tx) error { if etx.State != txmgr.TxConfirmed { - return errors.New("expected eth_tx state to be confirmed") + return errors.New("expected tx state to be confirmed") } _, err := orm.q.ExecContext(ctx, `UPDATE evm.txes SET state = 'unconfirmed' WHERE id = $1`, etx.ID) return pkgerrors.Wrap(err, "updateEthTxUnconfirm failed") @@ -1168,7 +1211,9 @@ ORDER BY nonce ASC if err = orm.LoadTxesAttempts(ctx, etxs); err != nil { return pkgerrors.Wrap(err, "FindTransactionsConfirmedInBlockRange failed to load evm.tx_attempts") } - err = orm.loadEthTxesAttemptsReceipts(ctx, etxs) + + // retrieve tx with attempts and partial receipt values for optimization purpose + err = orm.loadEthTxesAttemptsWithPartialReceipts(ctx, etxs) return pkgerrors.Wrap(err, "FindTransactionsConfirmedInBlockRange failed to load evm.receipts") }) return etxs, pkgerrors.Wrap(err, "FindTransactionsConfirmedInBlockRange failed") @@ -1205,24 +1250,6 @@ AND evm_chain_id = $1`, chainID.String()).Scan(&earliestUnconfirmedTxBlock) return earliestUnconfirmedTxBlock, err } -func (o *evmTxStore) IsTxFinalized(ctx context.Context, blockHeight int64, txID int64, chainID *big.Int) (finalized bool, err error) { - var cancel context.CancelFunc - ctx, cancel = o.stopCh.Ctx(ctx) - defer cancel() - - var count int32 - err = o.q.GetContext(ctx, &count, ` - SELECT COUNT(evm.receipts.receipt) FROM evm.txes - INNER JOIN evm.tx_attempts ON evm.txes.id = evm.tx_attempts.eth_tx_id - INNER JOIN evm.receipts ON evm.tx_attempts.hash = evm.receipts.tx_hash - WHERE evm.receipts.block_number <= ($1 - evm.txes.min_confirmations) - AND evm.txes.id = $2 AND evm.txes.evm_chain_id = $3`, blockHeight, txID, chainID.String()) - if err != nil { - return false, fmt.Errorf("failed to retrieve transaction reciepts: %w", err) - } - return count > 0, nil -} - func (o *evmTxStore) saveAttemptWithNewState(ctx context.Context, attempt TxAttempt, broadcastAt time.Time) error { var dbAttempt DbEthTxAttempt dbAttempt.FromTxAttempt(&attempt) @@ -1444,23 +1471,18 @@ ORDER BY nonce ASC // markOldTxesMissingReceiptAsErrored // -// Once eth_tx has all of its attempts broadcast before some cutoff threshold +// Once eth_tx has all of its attempts broadcast equal to or before latestFinalizedBlockNum // without receiving any receipts, we mark it as fatally errored (never sent). // // The job run will also be marked as errored in this case since we never got a // receipt and thus cannot pass on any transaction hash -func (o *evmTxStore) MarkOldTxesMissingReceiptAsErrored(ctx context.Context, blockNum int64, finalityDepth uint32, chainID *big.Int) error { +func (o *evmTxStore) MarkOldTxesMissingReceiptAsErrored(ctx context.Context, blockNum int64, latestFinalizedBlockNum int64, chainID *big.Int) error { var cancel context.CancelFunc ctx, cancel = o.stopCh.Ctx(ctx) defer cancel() - // cutoffBlockNum is a block height - // Any 'confirmed_missing_receipt' eth_tx with all attempts older than this block height will be marked as errored - // We will not try to query for receipts for this transaction any more - cutoff := blockNum - int64(finalityDepth) - if cutoff <= 0 { - return nil - } - if cutoff <= 0 { + // Any 'confirmed_missing_receipt' eth_tx with all attempts equal to or older than latestFinalizedBlockNum will be marked as errored + // We will not try to query for receipts for this transaction anymore + if latestFinalizedBlockNum <= 0 { return nil } // note: if QOpt passes in a sql.Tx this will reuse it @@ -1480,12 +1502,12 @@ FROM ( WHERE e2.state = 'confirmed_missing_receipt' AND e2.evm_chain_id = $3 GROUP BY e2.id - HAVING max(evm.tx_attempts.broadcast_before_block_num) < $2 + HAVING max(evm.tx_attempts.broadcast_before_block_num) <= $2 ) FOR UPDATE OF e1 ) e0 WHERE e0.id = evm.txes.id -RETURNING e0.id, e0.nonce`, ErrCouldNotGetReceipt, cutoff, chainID.String()) +RETURNING e0.id, e0.nonce`, ErrCouldNotGetReceipt, latestFinalizedBlockNum, chainID.String()) if err != nil { return pkgerrors.Wrap(err, "markOldTxesMissingReceiptAsErrored failed to query") @@ -1872,7 +1894,7 @@ id < ( return } -func (o *evmTxStore) ReapTxHistory(ctx context.Context, minBlockNumberToKeep int64, timeThreshold time.Time, chainID *big.Int) error { +func (o *evmTxStore) ReapTxHistory(ctx context.Context, timeThreshold time.Time, chainID *big.Int) error { var cancel context.CancelFunc ctx, cancel = o.stopCh.Ctx(ctx) defer cancel() @@ -1885,19 +1907,18 @@ func (o *evmTxStore) ReapTxHistory(ctx context.Context, minBlockNumberToKeep int res, err := o.q.ExecContext(ctx, ` WITH old_enough_receipts AS ( SELECT tx_hash FROM evm.receipts - WHERE block_number < $1 ORDER BY block_number ASC, id ASC - LIMIT $2 + LIMIT $1 ) DELETE FROM evm.txes USING old_enough_receipts, evm.tx_attempts WHERE evm.tx_attempts.eth_tx_id = evm.txes.id AND evm.tx_attempts.hash = old_enough_receipts.tx_hash -AND evm.txes.created_at < $3 -AND evm.txes.state = 'confirmed' -AND evm_chain_id = $4`, minBlockNumberToKeep, limit, timeThreshold, chainID.String()) +AND evm.txes.created_at < $2 +AND evm.txes.state = 'finalized' +AND evm_chain_id = $3`, limit, timeThreshold, chainID.String()) if err != nil { - return count, pkgerrors.Wrap(err, "ReapTxes failed to delete old confirmed evm.txes") + return count, pkgerrors.Wrap(err, "ReapTxes failed to delete old finalized evm.txes") } rowsAffected, err := res.RowsAffected() if err != nil { @@ -1906,7 +1927,7 @@ AND evm_chain_id = $4`, minBlockNumberToKeep, limit, timeThreshold, chainID.Stri return uint(rowsAffected), err }, batchSize) if err != nil { - return pkgerrors.Wrap(err, "TxmReaper#reapEthTxes batch delete of confirmed evm.txes failed") + return pkgerrors.Wrap(err, "TxmReaper#reapEthTxes batch delete of finalized evm.txes failed") } // Delete old 'fatal_error' evm.txes err = sqlutil.Batch(func(_, limit uint) (count uint, err error) { @@ -1927,6 +1948,38 @@ AND evm_chain_id = $2`, timeThreshold, chainID.String()) if err != nil { return pkgerrors.Wrap(err, "TxmReaper#reapEthTxes batch delete of fatally errored evm.txes failed") } + // Delete old 'confirmed' evm.txes that were never finalized + // This query should never result in changes but added just in case transactions slip through the cracks + // to avoid them building up in the DB + err = sqlutil.Batch(func(_, limit uint) (count uint, err error) { + res, err := o.q.ExecContext(ctx, ` +WITH old_enough_receipts AS ( + SELECT tx_hash FROM evm.receipts + ORDER BY block_number ASC, id ASC + LIMIT $1 +) +DELETE FROM evm.txes +USING old_enough_receipts, evm.tx_attempts +WHERE evm.tx_attempts.eth_tx_id = evm.txes.id +AND evm.tx_attempts.hash = old_enough_receipts.tx_hash +AND evm.txes.created_at < $2 +AND evm.txes.state = 'confirmed' +AND evm_chain_id = $3`, limit, timeThreshold, chainID.String()) + if err != nil { + return count, pkgerrors.Wrap(err, "ReapTxes failed to delete old confirmed evm.txes") + } + rowsAffected, err := res.RowsAffected() + if err != nil { + return count, pkgerrors.Wrap(err, "ReapTxes failed to get rows affected") + } + if rowsAffected > 0 { + o.logger.Errorf("%d confirmed transactions were reaped before being marked as finalized. This should never happen unless the threshold is set too low or the transactions were lost track of", rowsAffected) + } + return uint(rowsAffected), err + }, batchSize) + if err != nil { + return pkgerrors.Wrap(err, "TxmReaper#reapEthTxes batch delete of confirmed evm.txes failed") + } return nil } @@ -2055,3 +2108,36 @@ func (o *evmTxStore) UpdateTxAttemptBroadcastBeforeBlockNum(ctx context.Context, _, err := o.q.ExecContext(ctx, sql, blockNum, id) return err } + +// FindConfirmedTxesReceipts Returns all confirmed transactions with receipt block nums older than or equal to the finalized block number +func (o *evmTxStore) FindConfirmedTxesReceipts(ctx context.Context, finalizedBlockNum int64, chainID *big.Int) (receipts []Receipt, err error) { + var cancel context.CancelFunc + ctx, cancel = o.stopCh.Ctx(ctx) + defer cancel() + + // note the receipts are partially loaded for performance reason + query := `SELECT evm.receipts.id, evm.receipts.tx_hash, evm.receipts.block_hash, evm.receipts.block_number FROM evm.receipts + INNER JOIN evm.tx_attempts ON evm.tx_attempts.hash = evm.receipts.tx_hash + INNER JOIN evm.txes ON evm.txes.id = evm.tx_attempts.eth_tx_id + WHERE evm.txes.state = 'confirmed' AND evm.receipts.block_number <= $1 AND evm.txes.evm_chain_id = $2` + err = o.q.SelectContext(ctx, &receipts, query, finalizedBlockNum, chainID.String()) + return receipts, err +} + +// Mark transactions corresponding to receipt IDs as finalized +func (o *evmTxStore) UpdateTxStatesToFinalizedUsingReceiptIds(ctx context.Context, receiptIDs []int64, chainId *big.Int) error { + if len(receiptIDs) == 0 { + return nil + } + var cancel context.CancelFunc + ctx, cancel = o.stopCh.Ctx(ctx) + defer cancel() + sql := ` +UPDATE evm.txes SET state = 'finalized' WHERE evm.txes.evm_chain_id = $1 AND evm.txes.id IN (SELECT evm.txes.id FROM evm.txes + INNER JOIN evm.tx_attempts ON evm.tx_attempts.eth_tx_id = evm.txes.id + INNER JOIN evm.receipts ON evm.receipts.tx_hash = evm.tx_attempts.hash + WHERE evm.receipts.id = ANY($2)) +` + _, err := o.q.ExecContext(ctx, sql, chainId.String(), pq.Array(receiptIDs)) + return err +} diff --git a/core/chains/evm/txmgr/evm_tx_store_test.go b/core/chains/evm/txmgr/evm_tx_store_test.go index afb8de4ca5..c711c2788e 100644 --- a/core/chains/evm/txmgr/evm_tx_store_test.go +++ b/core/chains/evm/txmgr/evm_tx_store_test.go @@ -783,30 +783,6 @@ func TestORM_UpdateTxForRebroadcast(t *testing.T) { }) } -func TestORM_IsTxFinalized(t *testing.T) { - t.Parallel() - - db := pgtest.NewSqlxDB(t) - txStore := cltest.NewTestTxStore(t, db) - ethClient := evmtest.NewEthClientMockWithDefaultChain(t) - - t.Run("confirmed tx not past finality_depth", func(t *testing.T) { - confirmedAddr := cltest.MustGenerateRandomKey(t).Address - tx := mustInsertConfirmedEthTxWithReceipt(t, txStore, confirmedAddr, 123, 1) - finalized, err := txStore.IsTxFinalized(tests.Context(t), 2, tx.ID, ethClient.ConfiguredChainID()) - require.NoError(t, err) - require.False(t, finalized) - }) - - t.Run("confirmed tx past finality_depth", func(t *testing.T) { - confirmedAddr := cltest.MustGenerateRandomKey(t).Address - tx := mustInsertConfirmedEthTxWithReceipt(t, txStore, confirmedAddr, 123, 1) - finalized, err := txStore.IsTxFinalized(tests.Context(t), 10, tx.ID, ethClient.ConfiguredChainID()) - require.NoError(t, err) - require.True(t, finalized) - }) -} - func TestORM_FindTransactionsConfirmedInBlockRange(t *testing.T) { t.Parallel() @@ -840,6 +816,12 @@ func TestORM_FindTransactionsConfirmedInBlockRange(t *testing.T) { assert.Equal(t, etxes[0].Sequence, etx_8.Sequence) assert.Equal(t, etxes[1].Sequence, etx_9.Sequence) }) + + t.Run("return empty txes when no transactions in range found", func(t *testing.T) { + etxes, err := txStore.FindTransactionsConfirmedInBlockRange(tests.Context(t), 0, 0, ethClient.ConfiguredChainID()) + require.NoError(t, err) + assert.Len(t, etxes, 0) + }) } func TestORM_FindEarliestUnconfirmedBroadcastTime(t *testing.T) { @@ -1141,13 +1123,14 @@ func TestORM_MarkOldTxesMissingReceiptAsErrored(t *testing.T) { ethKeyStore := cltest.NewKeyStore(t, db).Eth() ethClient := evmtest.NewEthClientMockWithDefaultChain(t) _, fromAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) + latestFinalizedBlockNum := int64(8) // tx state should be confirmed missing receipt - // attempt should be broadcast before cutoff time + // attempt should be before latestFinalizedBlockNum t.Run("successfully mark errored transactions", func(t *testing.T) { etx := mustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt(t, txStore, 1, 7, time.Now(), fromAddress) - err := txStore.MarkOldTxesMissingReceiptAsErrored(tests.Context(t), 10, 2, ethClient.ConfiguredChainID()) + err := txStore.MarkOldTxesMissingReceiptAsErrored(tests.Context(t), 10, latestFinalizedBlockNum, ethClient.ConfiguredChainID()) require.NoError(t, err) etx, err = txStore.FindTxWithAttempts(ctx, etx.ID) @@ -1157,7 +1140,7 @@ func TestORM_MarkOldTxesMissingReceiptAsErrored(t *testing.T) { t.Run("successfully mark errored transactions w/ qopt passing in sql.Tx", func(t *testing.T) { etx := mustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt(t, txStore, 1, 7, time.Now(), fromAddress) - err := txStore.MarkOldTxesMissingReceiptAsErrored(tests.Context(t), 10, 2, ethClient.ConfiguredChainID()) + err := txStore.MarkOldTxesMissingReceiptAsErrored(tests.Context(t), 10, latestFinalizedBlockNum, ethClient.ConfiguredChainID()) require.NoError(t, err) // must run other query outside of postgres transaction so changes are committed @@ -1382,7 +1365,7 @@ func TestORM_UpdateTxUnstartedToInProgress(t *testing.T) { evmTxmCfg := txmgr.NewEvmTxmConfig(ccfg.EVM()) ec := evmtest.NewEthClientMockWithDefaultChain(t) txMgr := txmgr.NewEvmTxm(ec.ConfiguredChainID(), evmTxmCfg, ccfg.EVM().Transactions(), nil, logger.Test(t), nil, nil, - nil, txStore, nil, nil, nil, nil) + nil, txStore, nil, nil, nil, nil, nil) err := txMgr.XXXTestAbandon(fromAddress) // mark transaction as abandoned require.NoError(t, err) @@ -1871,3 +1854,60 @@ func AssertCountPerSubject(t *testing.T, txStore txmgr.TestEvmTxStore, expected require.NoError(t, err) require.Equal(t, int(expected), count) } + +func TestORM_FindTransactionsByState(t *testing.T) { + t.Parallel() + + ctx := tests.Context(t) + db := pgtest.NewSqlxDB(t) + txStore := cltest.NewTestTxStore(t, db) + kst := cltest.NewKeyStore(t, db) + _, fromAddress := cltest.MustInsertRandomKey(t, kst.Eth()) + finalizedBlockNum := int64(100) + + mustInsertUnstartedTx(t, txStore, fromAddress) + mustInsertInProgressEthTxWithAttempt(t, txStore, 0, fromAddress) + mustInsertUnconfirmedEthTxWithAttemptState(t, txStore, 1, fromAddress, txmgrtypes.TxAttemptBroadcast) + mustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt(t, txStore, 2, finalizedBlockNum, time.Now(), fromAddress) + mustInsertConfirmedEthTxWithReceipt(t, txStore, fromAddress, 3, finalizedBlockNum+1) + mustInsertConfirmedEthTxWithReceipt(t, txStore, fromAddress, 4, finalizedBlockNum) + mustInsertFatalErrorEthTx(t, txStore, fromAddress) + + receipts, err := txStore.FindConfirmedTxesReceipts(ctx, finalizedBlockNum, testutils.FixtureChainID) + require.NoError(t, err) + require.Len(t, receipts, 1) +} + +func TestORM_UpdateTxesFinalized(t *testing.T) { + t.Parallel() + + ctx := tests.Context(t) + db := pgtest.NewSqlxDB(t) + txStore := cltest.NewTestTxStore(t, db) + kst := cltest.NewKeyStore(t, db) + broadcast := time.Now() + _, fromAddress := cltest.MustInsertRandomKey(t, kst.Eth()) + + t.Run("successfully finalizes a confirmed transaction", func(t *testing.T) { + nonce := evmtypes.Nonce(0) + tx := &txmgr.Tx{ + Sequence: &nonce, + FromAddress: fromAddress, + EncodedPayload: []byte{1, 2, 3}, + State: txmgrcommon.TxConfirmed, + BroadcastAt: &broadcast, + InitialBroadcastAt: &broadcast, + } + err := txStore.InsertTx(ctx, tx) + require.NoError(t, err) + attempt := newBroadcastLegacyEthTxAttempt(t, tx.ID) + err = txStore.InsertTxAttempt(ctx, &attempt) + require.NoError(t, err) + receipt := mustInsertEthReceipt(t, txStore, 100, testutils.NewHash(), attempt.Hash) + err = txStore.UpdateTxStatesToFinalizedUsingReceiptIds(ctx, []int64{receipt.ID}, testutils.FixtureChainID) + require.NoError(t, err) + etx, err := txStore.FindTxWithAttempts(ctx, tx.ID) + require.NoError(t, err) + require.Equal(t, txmgrcommon.TxFinalized, etx.State) + }) +} diff --git a/core/chains/evm/txmgr/finalizer.go b/core/chains/evm/txmgr/finalizer.go new file mode 100644 index 0000000000..6074463615 --- /dev/null +++ b/core/chains/evm/txmgr/finalizer.go @@ -0,0 +1,295 @@ +package txmgr + +import ( + "context" + "fmt" + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/rpc" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" + + evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" +) + +var _ Finalizer = (*evmFinalizer)(nil) + +// processHeadTimeout represents a sanity limit on how long ProcessHead should take to complete +const processHeadTimeout = 10 * time.Minute + +type finalizerTxStore interface { + FindConfirmedTxesReceipts(ctx context.Context, finalizedBlockNum int64, chainID *big.Int) ([]Receipt, error) + UpdateTxStatesToFinalizedUsingReceiptIds(ctx context.Context, txs []int64, chainId *big.Int) error +} + +type finalizerChainClient interface { + BatchCallContext(ctx context.Context, elems []rpc.BatchElem) error +} + +type finalizerHeadTracker interface { + LatestAndFinalizedBlock(ctx context.Context) (latest, finalized *evmtypes.Head, err error) +} + +// Finalizer handles processing new finalized blocks and marking transactions as finalized accordingly in the TXM DB +type evmFinalizer struct { + services.StateMachine + lggr logger.SugaredLogger + chainId *big.Int + rpcBatchSize int + + txStore finalizerTxStore + client finalizerChainClient + headTracker finalizerHeadTracker + + mb *mailbox.Mailbox[*evmtypes.Head] + stopCh services.StopChan + wg sync.WaitGroup + + lastProcessedFinalizedBlockNum int64 +} + +func NewEvmFinalizer( + lggr logger.Logger, + chainId *big.Int, + rpcBatchSize uint32, + txStore finalizerTxStore, + client finalizerChainClient, + headTracker finalizerHeadTracker, +) *evmFinalizer { + lggr = logger.Named(lggr, "Finalizer") + return &evmFinalizer{ + lggr: logger.Sugared(lggr), + chainId: chainId, + rpcBatchSize: int(rpcBatchSize), + txStore: txStore, + client: client, + headTracker: headTracker, + mb: mailbox.NewSingle[*evmtypes.Head](), + } +} + +// Start the finalizer +func (f *evmFinalizer) Start(ctx context.Context) error { + return f.StartOnce("Finalizer", func() error { + f.lggr.Debugf("started Finalizer with RPC batch size limit: %d", f.rpcBatchSize) + f.stopCh = make(chan struct{}) + f.wg.Add(1) + go f.runLoop() + return nil + }) +} + +// Close the finalizer +func (f *evmFinalizer) Close() error { + return f.StopOnce("Finalizer", func() error { + f.lggr.Debug("closing Finalizer") + close(f.stopCh) + f.wg.Wait() + return nil + }) +} + +func (f *evmFinalizer) Name() string { + return f.lggr.Name() +} + +func (f *evmFinalizer) HealthReport() map[string]error { + return map[string]error{f.Name(): f.Healthy()} +} + +func (f *evmFinalizer) runLoop() { + defer f.wg.Done() + ctx, cancel := f.stopCh.NewCtx() + defer cancel() + for { + select { + case <-f.mb.Notify(): + for { + if ctx.Err() != nil { + return + } + head, exists := f.mb.Retrieve() + if !exists { + break + } + if err := f.ProcessHead(ctx, head); err != nil { + f.lggr.Errorw("Error processing head", "err", err) + f.SvcErrBuffer.Append(err) + continue + } + } + case <-ctx.Done(): + return + } + } +} + +func (f *evmFinalizer) DeliverLatestHead(head *evmtypes.Head) bool { + return f.mb.Deliver(head) +} + +func (f *evmFinalizer) ProcessHead(ctx context.Context, head *evmtypes.Head) error { + ctx, cancel := context.WithTimeout(ctx, processHeadTimeout) + defer cancel() + _, latestFinalizedHead, err := f.headTracker.LatestAndFinalizedBlock(ctx) + if err != nil { + return fmt.Errorf("failed to retrieve latest finalized head: %w", err) + } + return f.processFinalizedHead(ctx, latestFinalizedHead) +} + +// Determines if any confirmed transactions can be marked as finalized by comparing their receipts against the latest finalized block +func (f *evmFinalizer) processFinalizedHead(ctx context.Context, latestFinalizedHead *evmtypes.Head) error { + // Cannot determine finality without a finalized head for comparison + if latestFinalizedHead == nil || !latestFinalizedHead.IsValid() { + return fmt.Errorf("invalid latestFinalizedHead") + } + // Only continue processing if the latestFinalizedHead has not already been processed + // Helps avoid unnecessary processing on every head if blocks are finalized in batches + if latestFinalizedHead.BlockNumber() == f.lastProcessedFinalizedBlockNum { + return nil + } + if latestFinalizedHead.BlockNumber() < f.lastProcessedFinalizedBlockNum { + f.lggr.Errorw("Received finalized block older than one already processed. This should never happen and could be an issue with RPCs.", "lastProcessedFinalizedBlockNum", f.lastProcessedFinalizedBlockNum, "retrievedFinalizedBlockNum", latestFinalizedHead.BlockNumber()) + return nil + } + + earliestBlockNumInChain := latestFinalizedHead.EarliestHeadInChain().BlockNumber() + f.lggr.Debugw("processing latest finalized head", "blockNum", latestFinalizedHead.BlockNumber(), "blockHash", latestFinalizedHead.BlockHash(), "earliestBlockNumInChain", earliestBlockNumInChain) + + // Retrieve all confirmed transactions with receipts older than or equal to the finalized block, loaded with attempts and receipts + unfinalizedReceipts, err := f.txStore.FindConfirmedTxesReceipts(ctx, latestFinalizedHead.BlockNumber(), f.chainId) + if err != nil { + return fmt.Errorf("failed to retrieve receipts for confirmed, unfinalized transactions: %w", err) + } + + var finalizedReceipts []Receipt + // Group by block hash transactions whose receipts cannot be validated using the cached heads + blockNumToReceiptsMap := make(map[int64][]Receipt) + // Find transactions with receipt block nums older than the latest finalized block num and block hashes still in chain + for _, receipt := range unfinalizedReceipts { + // The tx store query ensures transactions have receipts but leaving this check here for a belts and braces approach + if receipt.TxHash == utils.EmptyHash || receipt.BlockHash == utils.EmptyHash { + f.lggr.AssumptionViolationw("invalid receipt found for confirmed transaction", "receipt", receipt) + continue + } + // The tx store query only returns transactions with receipts older than or equal to the finalized block but leaving this check here for a belts and braces approach + if receipt.BlockNumber > latestFinalizedHead.BlockNumber() { + continue + } + // Receipt block num older than earliest head in chain. Validate hash using RPC call later + if receipt.BlockNumber < earliestBlockNumInChain { + blockNumToReceiptsMap[receipt.BlockNumber] = append(blockNumToReceiptsMap[receipt.BlockNumber], receipt) + continue + } + blockHashInChain := latestFinalizedHead.HashAtHeight(receipt.BlockNumber) + // Receipt block hash does not match the block hash in chain. Transaction has been re-org'd out but DB state has not been updated yet + if blockHashInChain.String() != receipt.BlockHash.String() { + // Log error if a transaction is marked as confirmed with a receipt older than the finalized block + // This scenario could potentially point to a re-org'd transaction the Confirmer has lost track of + f.lggr.Errorw("found confirmed transaction with re-org'd receipt older than finalized block", "receipt", receipt, "onchainBlockHash", blockHashInChain.String()) + continue + } + finalizedReceipts = append(finalizedReceipts, receipt) + } + + // Check if block hashes exist for receipts on-chain older than the earliest cached head + // Transactions are grouped by their receipt block hash to avoid repeat requests on the same hash in case transactions were confirmed in the same block + validatedReceipts := f.batchCheckReceiptHashesOnchain(ctx, blockNumToReceiptsMap) + finalizedReceipts = append(finalizedReceipts, validatedReceipts...) + + receiptIDs := f.buildReceiptIdList(finalizedReceipts) + + err = f.txStore.UpdateTxStatesToFinalizedUsingReceiptIds(ctx, receiptIDs, f.chainId) + if err != nil { + return fmt.Errorf("failed to update transactions as finalized: %w", err) + } + // Update lastProcessedFinalizedBlockNum after processing has completed to allow failed processing to retry on subsequent heads + // Does not need to be protected with mutex lock because the Finalizer only runs in a single loop + f.lastProcessedFinalizedBlockNum = latestFinalizedHead.BlockNumber() + return nil +} + +func (f *evmFinalizer) batchCheckReceiptHashesOnchain(ctx context.Context, blockNumToReceiptsMap map[int64][]Receipt) []Receipt { + if len(blockNumToReceiptsMap) == 0 { + return nil + } + // Group the RPC batch calls in groups of rpcBatchSize + var rpcBatchGroups [][]rpc.BatchElem + var rpcBatch []rpc.BatchElem + for blockNum := range blockNumToReceiptsMap { + elem := rpc.BatchElem{ + Method: "eth_getBlockByNumber", + Args: []any{ + hexutil.EncodeBig(big.NewInt(blockNum)), + false, + }, + Result: new(evmtypes.Head), + } + rpcBatch = append(rpcBatch, elem) + if len(rpcBatch) >= f.rpcBatchSize { + rpcBatchGroups = append(rpcBatchGroups, rpcBatch) + rpcBatch = []rpc.BatchElem{} + } + } + if len(rpcBatch) > 0 { + rpcBatchGroups = append(rpcBatchGroups, rpcBatch) + } + + var finalizedReceipts []Receipt + for _, rpcBatch := range rpcBatchGroups { + err := f.client.BatchCallContext(ctx, rpcBatch) + if err != nil { + // Continue if batch RPC call failed so other batches can still be considered for finalization + f.lggr.Errorw("failed to find blocks due to batch call failure", "error", err) + continue + } + for _, req := range rpcBatch { + if req.Error != nil { + // Continue if particular RPC call failed so other txs can still be considered for finalization + f.lggr.Errorw("failed to find block by number", "blockNum", req.Args[0], "error", req.Error) + continue + } + head, ok := req.Result.(*evmtypes.Head) + if !ok || !head.IsValid() { + // Continue if particular RPC call yielded a nil block so other txs can still be considered for finalization + f.lggr.Errorw("retrieved nil head for block number", "blockNum", req.Args[0]) + continue + } + receipts := blockNumToReceiptsMap[head.BlockNumber()] + // Check if transaction receipts match the block hash at the given block num + // If they do not, the transactions may have been re-org'd out + // The expectation is for the Confirmer to pick up on these re-orgs and get the transaction included + for _, receipt := range receipts { + if receipt.BlockHash.String() == head.BlockHash().String() { + finalizedReceipts = append(finalizedReceipts, receipt) + } else { + // Log error if a transaction is marked as confirmed with a receipt older than the finalized block + // This scenario could potentially point to a re-org'd transaction the Confirmer has lost track of + f.lggr.Errorw("found confirmed transaction with re-org'd receipt older than finalized block", "receipt", receipt, "onchainBlockHash", head.BlockHash().String()) + } + } + } + } + return finalizedReceipts +} + +// Build list of transaction IDs +func (f *evmFinalizer) buildReceiptIdList(finalizedReceipts []Receipt) []int64 { + receiptIds := make([]int64, len(finalizedReceipts)) + for i, receipt := range finalizedReceipts { + f.lggr.Debugw("transaction considered finalized", + "txHash", receipt.TxHash.String(), + "receiptBlockNum", receipt.BlockNumber, + "receiptBlockHash", receipt.BlockHash.String(), + ) + receiptIds[i] = receipt.ID + } + return receiptIds +} diff --git a/core/chains/evm/txmgr/finalizer_test.go b/core/chains/evm/txmgr/finalizer_test.go new file mode 100644 index 0000000000..f83a53bf49 --- /dev/null +++ b/core/chains/evm/txmgr/finalizer_test.go @@ -0,0 +1,240 @@ +package txmgr_test + +import ( + "errors" + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/rpc" + "github.com/google/uuid" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + + txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" + + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/testutils" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" + "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" +) + +func TestFinalizer_MarkTxFinalized(t *testing.T) { + t.Parallel() + ctx := tests.Context(t) + db := pgtest.NewSqlxDB(t) + txStore := cltest.NewTestTxStore(t, db) + ethKeyStore := cltest.NewKeyStore(t, db).Eth() + feeLimit := uint64(10_000) + ethClient := testutils.NewEthClientMockWithDefaultChain(t) + rpcBatchSize := uint32(1) + ht := headtracker.NewSimulatedHeadTracker(ethClient, true, 0) + + head := &evmtypes.Head{ + Hash: utils.NewHash(), + Number: 100, + Parent: &evmtypes.Head{ + Hash: utils.NewHash(), + Number: 99, + IsFinalized: true, + }, + } + + t.Run("returns not finalized for tx with receipt newer than finalized block", func(t *testing.T) { + finalizer := txmgr.NewEvmFinalizer(logger.Test(t), testutils.FixtureChainID, rpcBatchSize, txStore, ethClient, ht) + servicetest.Run(t, finalizer) + + idempotencyKey := uuid.New().String() + _, fromAddress := cltest.MustInsertRandomKey(t, ethKeyStore) + nonce := evmtypes.Nonce(0) + broadcast := time.Now() + tx := &txmgr.Tx{ + Sequence: &nonce, + IdempotencyKey: &idempotencyKey, + FromAddress: fromAddress, + EncodedPayload: []byte{1, 2, 3}, + FeeLimit: feeLimit, + State: txmgrcommon.TxConfirmed, + BroadcastAt: &broadcast, + InitialBroadcastAt: &broadcast, + } + attemptHash := insertTxAndAttemptWithIdempotencyKey(t, txStore, tx, idempotencyKey) + // Insert receipt for unfinalized block num + mustInsertEthReceipt(t, txStore, head.Number, head.Hash, attemptHash) + ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(head, nil).Once() + ethClient.On("LatestFinalizedBlock", mock.Anything).Return(head.Parent, nil).Once() + err := finalizer.ProcessHead(ctx, head) + require.NoError(t, err) + tx, err = txStore.FindTxWithIdempotencyKey(ctx, idempotencyKey, testutils.FixtureChainID) + require.NoError(t, err) + require.Equal(t, txmgrcommon.TxConfirmed, tx.State) + }) + + t.Run("returns not finalized for tx with receipt re-org'd out", func(t *testing.T) { + finalizer := txmgr.NewEvmFinalizer(logger.Test(t), testutils.FixtureChainID, rpcBatchSize, txStore, ethClient, ht) + servicetest.Run(t, finalizer) + + idempotencyKey := uuid.New().String() + _, fromAddress := cltest.MustInsertRandomKey(t, ethKeyStore) + nonce := evmtypes.Nonce(0) + broadcast := time.Now() + tx := &txmgr.Tx{ + Sequence: &nonce, + IdempotencyKey: &idempotencyKey, + FromAddress: fromAddress, + EncodedPayload: []byte{1, 2, 3}, + FeeLimit: feeLimit, + State: txmgrcommon.TxConfirmed, + BroadcastAt: &broadcast, + InitialBroadcastAt: &broadcast, + } + attemptHash := insertTxAndAttemptWithIdempotencyKey(t, txStore, tx, idempotencyKey) + // Insert receipt for finalized block num + mustInsertEthReceipt(t, txStore, head.Parent.Number, utils.NewHash(), attemptHash) + ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(head, nil).Once() + ethClient.On("LatestFinalizedBlock", mock.Anything).Return(head.Parent, nil).Once() + err := finalizer.ProcessHead(ctx, head) + require.NoError(t, err) + tx, err = txStore.FindTxWithIdempotencyKey(ctx, idempotencyKey, testutils.FixtureChainID) + require.NoError(t, err) + require.Equal(t, txmgrcommon.TxConfirmed, tx.State) + }) + + t.Run("returns finalized for tx with receipt in a finalized block", func(t *testing.T) { + finalizer := txmgr.NewEvmFinalizer(logger.Test(t), testutils.FixtureChainID, rpcBatchSize, txStore, ethClient, ht) + servicetest.Run(t, finalizer) + + idempotencyKey := uuid.New().String() + _, fromAddress := cltest.MustInsertRandomKey(t, ethKeyStore) + nonce := evmtypes.Nonce(0) + broadcast := time.Now() + tx := &txmgr.Tx{ + Sequence: &nonce, + IdempotencyKey: &idempotencyKey, + FromAddress: fromAddress, + EncodedPayload: []byte{1, 2, 3}, + FeeLimit: feeLimit, + State: txmgrcommon.TxConfirmed, + BroadcastAt: &broadcast, + InitialBroadcastAt: &broadcast, + } + attemptHash := insertTxAndAttemptWithIdempotencyKey(t, txStore, tx, idempotencyKey) + // Insert receipt for finalized block num + mustInsertEthReceipt(t, txStore, head.Parent.Number, head.Parent.Hash, attemptHash) + ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(head, nil).Once() + ethClient.On("LatestFinalizedBlock", mock.Anything).Return(head.Parent, nil).Once() + err := finalizer.ProcessHead(ctx, head) + require.NoError(t, err) + tx, err = txStore.FindTxWithIdempotencyKey(ctx, idempotencyKey, testutils.FixtureChainID) + require.NoError(t, err) + require.Equal(t, txmgrcommon.TxFinalized, tx.State) + }) + + t.Run("returns finalized for tx with receipt older than block history depth", func(t *testing.T) { + finalizer := txmgr.NewEvmFinalizer(logger.Test(t), testutils.FixtureChainID, rpcBatchSize, txStore, ethClient, ht) + servicetest.Run(t, finalizer) + + idempotencyKey := uuid.New().String() + _, fromAddress := cltest.MustInsertRandomKey(t, ethKeyStore) + nonce := evmtypes.Nonce(0) + broadcast := time.Now() + tx := &txmgr.Tx{ + Sequence: &nonce, + IdempotencyKey: &idempotencyKey, + FromAddress: fromAddress, + EncodedPayload: []byte{1, 2, 3}, + FeeLimit: feeLimit, + State: txmgrcommon.TxConfirmed, + BroadcastAt: &broadcast, + InitialBroadcastAt: &broadcast, + } + attemptHash := insertTxAndAttemptWithIdempotencyKey(t, txStore, tx, idempotencyKey) + // Insert receipt for finalized block num + receiptBlockHash1 := utils.NewHash() + mustInsertEthReceipt(t, txStore, head.Parent.Number-2, receiptBlockHash1, attemptHash) + idempotencyKey = uuid.New().String() + nonce = evmtypes.Nonce(1) + tx = &txmgr.Tx{ + Sequence: &nonce, + IdempotencyKey: &idempotencyKey, + FromAddress: fromAddress, + EncodedPayload: []byte{1, 2, 3}, + FeeLimit: feeLimit, + State: txmgrcommon.TxConfirmed, + BroadcastAt: &broadcast, + InitialBroadcastAt: &broadcast, + } + attemptHash = insertTxAndAttemptWithIdempotencyKey(t, txStore, tx, idempotencyKey) + // Insert receipt for finalized block num + receiptBlockHash2 := utils.NewHash() + mustInsertEthReceipt(t, txStore, head.Parent.Number-1, receiptBlockHash2, attemptHash) + // Separate batch calls will be made for each tx due to RPC batch size set to 1 when finalizer initialized above + ethClient.On("BatchCallContext", mock.Anything, mock.IsType([]rpc.BatchElem{})).Run(func(args mock.Arguments) { + rpcElements := args.Get(1).([]rpc.BatchElem) + require.Equal(t, 1, len(rpcElements)) + + require.Equal(t, "eth_getBlockByNumber", rpcElements[0].Method) + require.Equal(t, false, rpcElements[0].Args[1]) + + reqBlockNum := rpcElements[0].Args[0].(string) + req1BlockNum := hexutil.EncodeBig(big.NewInt(head.Parent.Number - 2)) + req2BlockNum := hexutil.EncodeBig(big.NewInt(head.Parent.Number - 1)) + var headResult evmtypes.Head + if req1BlockNum == reqBlockNum { + headResult = evmtypes.Head{Number: head.Parent.Number - 2, Hash: receiptBlockHash1} + } else if req2BlockNum == reqBlockNum { + headResult = evmtypes.Head{Number: head.Parent.Number - 1, Hash: receiptBlockHash2} + } else { + require.Fail(t, "unrecognized block hash") + } + rpcElements[0].Result = &headResult + }).Return(nil).Twice() + ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(head, nil).Once() + ethClient.On("LatestFinalizedBlock", mock.Anything).Return(head.Parent, nil).Once() + err := finalizer.ProcessHead(ctx, head) + require.NoError(t, err) + tx, err = txStore.FindTxWithIdempotencyKey(ctx, idempotencyKey, testutils.FixtureChainID) + require.NoError(t, err) + require.Equal(t, txmgrcommon.TxFinalized, tx.State) + }) + + t.Run("returns error if failed to retrieve latest head in headtracker", func(t *testing.T) { + finalizer := txmgr.NewEvmFinalizer(logger.Test(t), testutils.FixtureChainID, rpcBatchSize, txStore, ethClient, ht) + servicetest.Run(t, finalizer) + + ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(nil, errors.New("failed to get latest head")).Once() + err := finalizer.ProcessHead(ctx, head) + require.Error(t, err) + }) + + t.Run("returns error if failed to calculate latest finalized head in headtracker", func(t *testing.T) { + finalizer := txmgr.NewEvmFinalizer(logger.Test(t), testutils.FixtureChainID, rpcBatchSize, txStore, ethClient, ht) + servicetest.Run(t, finalizer) + + ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(head, nil).Once() + ethClient.On("LatestFinalizedBlock", mock.Anything).Return(nil, errors.New("failed to calculate latest finalized head")).Once() + err := finalizer.ProcessHead(ctx, head) + require.Error(t, err) + }) +} + +func insertTxAndAttemptWithIdempotencyKey(t *testing.T, txStore txmgr.TestEvmTxStore, tx *txmgr.Tx, idempotencyKey string) common.Hash { + ctx := tests.Context(t) + err := txStore.InsertTx(ctx, tx) + require.NoError(t, err) + tx, err = txStore.FindTxWithIdempotencyKey(ctx, idempotencyKey, testutils.FixtureChainID) + require.NoError(t, err) + attempt := cltest.NewLegacyEthTxAttempt(t, tx.ID) + err = txStore.InsertTxAttempt(ctx, &attempt) + require.NoError(t, err) + return attempt.Hash +} diff --git a/core/chains/evm/txmgr/mocks/evm_tx_store.go b/core/chains/evm/txmgr/mocks/evm_tx_store.go index b28e55ec32..a9a175e3d9 100644 --- a/core/chains/evm/txmgr/mocks/evm_tx_store.go +++ b/core/chains/evm/txmgr/mocks/evm_tx_store.go @@ -18,6 +18,8 @@ import ( time "time" + txmgr "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" + types "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" uuid "github.com/google/uuid" @@ -444,6 +446,66 @@ func (_c *EvmTxStore_DeleteInProgressAttempt_Call) RunAndReturn(run func(context return _c } +// FindConfirmedTxesReceipts provides a mock function with given fields: ctx, finalizedBlockNum, chainID +func (_m *EvmTxStore) FindConfirmedTxesReceipts(ctx context.Context, finalizedBlockNum int64, chainID *big.Int) ([]txmgr.DbReceipt, error) { + ret := _m.Called(ctx, finalizedBlockNum, chainID) + + if len(ret) == 0 { + panic("no return value specified for FindConfirmedTxesReceipts") + } + + var r0 []txmgr.DbReceipt + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64, *big.Int) ([]txmgr.DbReceipt, error)); ok { + return rf(ctx, finalizedBlockNum, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, int64, *big.Int) []txmgr.DbReceipt); ok { + r0 = rf(ctx, finalizedBlockNum, chainID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]txmgr.DbReceipt) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, int64, *big.Int) error); ok { + r1 = rf(ctx, finalizedBlockNum, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// EvmTxStore_FindConfirmedTxesReceipts_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FindConfirmedTxesReceipts' +type EvmTxStore_FindConfirmedTxesReceipts_Call struct { + *mock.Call +} + +// FindConfirmedTxesReceipts is a helper method to define mock.On call +// - ctx context.Context +// - finalizedBlockNum int64 +// - chainID *big.Int +func (_e *EvmTxStore_Expecter) FindConfirmedTxesReceipts(ctx interface{}, finalizedBlockNum interface{}, chainID interface{}) *EvmTxStore_FindConfirmedTxesReceipts_Call { + return &EvmTxStore_FindConfirmedTxesReceipts_Call{Call: _e.mock.On("FindConfirmedTxesReceipts", ctx, finalizedBlockNum, chainID)} +} + +func (_c *EvmTxStore_FindConfirmedTxesReceipts_Call) Run(run func(ctx context.Context, finalizedBlockNum int64, chainID *big.Int)) *EvmTxStore_FindConfirmedTxesReceipts_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(int64), args[2].(*big.Int)) + }) + return _c +} + +func (_c *EvmTxStore_FindConfirmedTxesReceipts_Call) Return(receipts []txmgr.DbReceipt, err error) *EvmTxStore_FindConfirmedTxesReceipts_Call { + _c.Call.Return(receipts, err) + return _c +} + +func (_c *EvmTxStore_FindConfirmedTxesReceipts_Call) RunAndReturn(run func(context.Context, int64, *big.Int) ([]txmgr.DbReceipt, error)) *EvmTxStore_FindConfirmedTxesReceipts_Call { + _c.Call.Return(run) + return _c +} + // FindEarliestUnconfirmedBroadcastTime provides a mock function with given fields: ctx, chainID func (_m *EvmTxStore) FindEarliestUnconfirmedBroadcastTime(ctx context.Context, chainID *big.Int) (null.Time, error) { ret := _m.Called(ctx, chainID) @@ -2058,65 +2120,6 @@ func (_c *EvmTxStore_HasInProgressTransaction_Call) RunAndReturn(run func(contex return _c } -// IsTxFinalized provides a mock function with given fields: ctx, blockHeight, txID, chainID -func (_m *EvmTxStore) IsTxFinalized(ctx context.Context, blockHeight int64, txID int64, chainID *big.Int) (bool, error) { - ret := _m.Called(ctx, blockHeight, txID, chainID) - - if len(ret) == 0 { - panic("no return value specified for IsTxFinalized") - } - - var r0 bool - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, int64, int64, *big.Int) (bool, error)); ok { - return rf(ctx, blockHeight, txID, chainID) - } - if rf, ok := ret.Get(0).(func(context.Context, int64, int64, *big.Int) bool); ok { - r0 = rf(ctx, blockHeight, txID, chainID) - } else { - r0 = ret.Get(0).(bool) - } - - if rf, ok := ret.Get(1).(func(context.Context, int64, int64, *big.Int) error); ok { - r1 = rf(ctx, blockHeight, txID, chainID) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// EvmTxStore_IsTxFinalized_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsTxFinalized' -type EvmTxStore_IsTxFinalized_Call struct { - *mock.Call -} - -// IsTxFinalized is a helper method to define mock.On call -// - ctx context.Context -// - blockHeight int64 -// - txID int64 -// - chainID *big.Int -func (_e *EvmTxStore_Expecter) IsTxFinalized(ctx interface{}, blockHeight interface{}, txID interface{}, chainID interface{}) *EvmTxStore_IsTxFinalized_Call { - return &EvmTxStore_IsTxFinalized_Call{Call: _e.mock.On("IsTxFinalized", ctx, blockHeight, txID, chainID)} -} - -func (_c *EvmTxStore_IsTxFinalized_Call) Run(run func(ctx context.Context, blockHeight int64, txID int64, chainID *big.Int)) *EvmTxStore_IsTxFinalized_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(int64), args[2].(int64), args[3].(*big.Int)) - }) - return _c -} - -func (_c *EvmTxStore_IsTxFinalized_Call) Return(finalized bool, err error) *EvmTxStore_IsTxFinalized_Call { - _c.Call.Return(finalized, err) - return _c -} - -func (_c *EvmTxStore_IsTxFinalized_Call) RunAndReturn(run func(context.Context, int64, int64, *big.Int) (bool, error)) *EvmTxStore_IsTxFinalized_Call { - _c.Call.Return(run) - return _c -} - // LoadTxAttempts provides a mock function with given fields: ctx, etx func (_m *EvmTxStore) LoadTxAttempts(ctx context.Context, etx *types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) error { ret := _m.Called(ctx, etx) @@ -2211,17 +2214,17 @@ func (_c *EvmTxStore_MarkAllConfirmedMissingReceipt_Call) RunAndReturn(run func( return _c } -// MarkOldTxesMissingReceiptAsErrored provides a mock function with given fields: ctx, blockNum, finalityDepth, chainID -func (_m *EvmTxStore) MarkOldTxesMissingReceiptAsErrored(ctx context.Context, blockNum int64, finalityDepth uint32, chainID *big.Int) error { - ret := _m.Called(ctx, blockNum, finalityDepth, chainID) +// MarkOldTxesMissingReceiptAsErrored provides a mock function with given fields: ctx, blockNum, latestFinalizedBlockNum, chainID +func (_m *EvmTxStore) MarkOldTxesMissingReceiptAsErrored(ctx context.Context, blockNum int64, latestFinalizedBlockNum int64, chainID *big.Int) error { + ret := _m.Called(ctx, blockNum, latestFinalizedBlockNum, chainID) if len(ret) == 0 { panic("no return value specified for MarkOldTxesMissingReceiptAsErrored") } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, int64, uint32, *big.Int) error); ok { - r0 = rf(ctx, blockNum, finalityDepth, chainID) + if rf, ok := ret.Get(0).(func(context.Context, int64, int64, *big.Int) error); ok { + r0 = rf(ctx, blockNum, latestFinalizedBlockNum, chainID) } else { r0 = ret.Error(0) } @@ -2237,15 +2240,15 @@ type EvmTxStore_MarkOldTxesMissingReceiptAsErrored_Call struct { // MarkOldTxesMissingReceiptAsErrored is a helper method to define mock.On call // - ctx context.Context // - blockNum int64 -// - finalityDepth uint32 +// - latestFinalizedBlockNum int64 // - chainID *big.Int -func (_e *EvmTxStore_Expecter) MarkOldTxesMissingReceiptAsErrored(ctx interface{}, blockNum interface{}, finalityDepth interface{}, chainID interface{}) *EvmTxStore_MarkOldTxesMissingReceiptAsErrored_Call { - return &EvmTxStore_MarkOldTxesMissingReceiptAsErrored_Call{Call: _e.mock.On("MarkOldTxesMissingReceiptAsErrored", ctx, blockNum, finalityDepth, chainID)} +func (_e *EvmTxStore_Expecter) MarkOldTxesMissingReceiptAsErrored(ctx interface{}, blockNum interface{}, latestFinalizedBlockNum interface{}, chainID interface{}) *EvmTxStore_MarkOldTxesMissingReceiptAsErrored_Call { + return &EvmTxStore_MarkOldTxesMissingReceiptAsErrored_Call{Call: _e.mock.On("MarkOldTxesMissingReceiptAsErrored", ctx, blockNum, latestFinalizedBlockNum, chainID)} } -func (_c *EvmTxStore_MarkOldTxesMissingReceiptAsErrored_Call) Run(run func(ctx context.Context, blockNum int64, finalityDepth uint32, chainID *big.Int)) *EvmTxStore_MarkOldTxesMissingReceiptAsErrored_Call { +func (_c *EvmTxStore_MarkOldTxesMissingReceiptAsErrored_Call) Run(run func(ctx context.Context, blockNum int64, latestFinalizedBlockNum int64, chainID *big.Int)) *EvmTxStore_MarkOldTxesMissingReceiptAsErrored_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(int64), args[2].(uint32), args[3].(*big.Int)) + run(args[0].(context.Context), args[1].(int64), args[2].(int64), args[3].(*big.Int)) }) return _c } @@ -2255,7 +2258,7 @@ func (_c *EvmTxStore_MarkOldTxesMissingReceiptAsErrored_Call) Return(_a0 error) return _c } -func (_c *EvmTxStore_MarkOldTxesMissingReceiptAsErrored_Call) RunAndReturn(run func(context.Context, int64, uint32, *big.Int) error) *EvmTxStore_MarkOldTxesMissingReceiptAsErrored_Call { +func (_c *EvmTxStore_MarkOldTxesMissingReceiptAsErrored_Call) RunAndReturn(run func(context.Context, int64, int64, *big.Int) error) *EvmTxStore_MarkOldTxesMissingReceiptAsErrored_Call { _c.Call.Return(run) return _c } @@ -2367,17 +2370,17 @@ func (_c *EvmTxStore_PruneUnstartedTxQueue_Call) RunAndReturn(run func(context.C return _c } -// ReapTxHistory provides a mock function with given fields: ctx, minBlockNumberToKeep, timeThreshold, chainID -func (_m *EvmTxStore) ReapTxHistory(ctx context.Context, minBlockNumberToKeep int64, timeThreshold time.Time, chainID *big.Int) error { - ret := _m.Called(ctx, minBlockNumberToKeep, timeThreshold, chainID) +// ReapTxHistory provides a mock function with given fields: ctx, timeThreshold, chainID +func (_m *EvmTxStore) ReapTxHistory(ctx context.Context, timeThreshold time.Time, chainID *big.Int) error { + ret := _m.Called(ctx, timeThreshold, chainID) if len(ret) == 0 { panic("no return value specified for ReapTxHistory") } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, int64, time.Time, *big.Int) error); ok { - r0 = rf(ctx, minBlockNumberToKeep, timeThreshold, chainID) + if rf, ok := ret.Get(0).(func(context.Context, time.Time, *big.Int) error); ok { + r0 = rf(ctx, timeThreshold, chainID) } else { r0 = ret.Error(0) } @@ -2392,16 +2395,15 @@ type EvmTxStore_ReapTxHistory_Call struct { // ReapTxHistory is a helper method to define mock.On call // - ctx context.Context -// - minBlockNumberToKeep int64 // - timeThreshold time.Time // - chainID *big.Int -func (_e *EvmTxStore_Expecter) ReapTxHistory(ctx interface{}, minBlockNumberToKeep interface{}, timeThreshold interface{}, chainID interface{}) *EvmTxStore_ReapTxHistory_Call { - return &EvmTxStore_ReapTxHistory_Call{Call: _e.mock.On("ReapTxHistory", ctx, minBlockNumberToKeep, timeThreshold, chainID)} +func (_e *EvmTxStore_Expecter) ReapTxHistory(ctx interface{}, timeThreshold interface{}, chainID interface{}) *EvmTxStore_ReapTxHistory_Call { + return &EvmTxStore_ReapTxHistory_Call{Call: _e.mock.On("ReapTxHistory", ctx, timeThreshold, chainID)} } -func (_c *EvmTxStore_ReapTxHistory_Call) Run(run func(ctx context.Context, minBlockNumberToKeep int64, timeThreshold time.Time, chainID *big.Int)) *EvmTxStore_ReapTxHistory_Call { +func (_c *EvmTxStore_ReapTxHistory_Call) Run(run func(ctx context.Context, timeThreshold time.Time, chainID *big.Int)) *EvmTxStore_ReapTxHistory_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(int64), args[2].(time.Time), args[3].(*big.Int)) + run(args[0].(context.Context), args[1].(time.Time), args[2].(*big.Int)) }) return _c } @@ -2411,7 +2413,7 @@ func (_c *EvmTxStore_ReapTxHistory_Call) Return(_a0 error) *EvmTxStore_ReapTxHis return _c } -func (_c *EvmTxStore_ReapTxHistory_Call) RunAndReturn(run func(context.Context, int64, time.Time, *big.Int) error) *EvmTxStore_ReapTxHistory_Call { +func (_c *EvmTxStore_ReapTxHistory_Call) RunAndReturn(run func(context.Context, time.Time, *big.Int) error) *EvmTxStore_ReapTxHistory_Call { _c.Call.Return(run) return _c } @@ -3197,6 +3199,54 @@ func (_c *EvmTxStore_UpdateTxForRebroadcast_Call) RunAndReturn(run func(context. return _c } +// UpdateTxStatesToFinalizedUsingReceiptIds provides a mock function with given fields: ctx, etxIDs, chainId +func (_m *EvmTxStore) UpdateTxStatesToFinalizedUsingReceiptIds(ctx context.Context, etxIDs []int64, chainId *big.Int) error { + ret := _m.Called(ctx, etxIDs, chainId) + + if len(ret) == 0 { + panic("no return value specified for UpdateTxStatesToFinalizedUsingReceiptIds") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, []int64, *big.Int) error); ok { + r0 = rf(ctx, etxIDs, chainId) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// EvmTxStore_UpdateTxStatesToFinalizedUsingReceiptIds_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateTxStatesToFinalizedUsingReceiptIds' +type EvmTxStore_UpdateTxStatesToFinalizedUsingReceiptIds_Call struct { + *mock.Call +} + +// UpdateTxStatesToFinalizedUsingReceiptIds is a helper method to define mock.On call +// - ctx context.Context +// - etxIDs []int64 +// - chainId *big.Int +func (_e *EvmTxStore_Expecter) UpdateTxStatesToFinalizedUsingReceiptIds(ctx interface{}, etxIDs interface{}, chainId interface{}) *EvmTxStore_UpdateTxStatesToFinalizedUsingReceiptIds_Call { + return &EvmTxStore_UpdateTxStatesToFinalizedUsingReceiptIds_Call{Call: _e.mock.On("UpdateTxStatesToFinalizedUsingReceiptIds", ctx, etxIDs, chainId)} +} + +func (_c *EvmTxStore_UpdateTxStatesToFinalizedUsingReceiptIds_Call) Run(run func(ctx context.Context, etxIDs []int64, chainId *big.Int)) *EvmTxStore_UpdateTxStatesToFinalizedUsingReceiptIds_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].([]int64), args[2].(*big.Int)) + }) + return _c +} + +func (_c *EvmTxStore_UpdateTxStatesToFinalizedUsingReceiptIds_Call) Return(_a0 error) *EvmTxStore_UpdateTxStatesToFinalizedUsingReceiptIds_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *EvmTxStore_UpdateTxStatesToFinalizedUsingReceiptIds_Call) RunAndReturn(run func(context.Context, []int64, *big.Int) error) *EvmTxStore_UpdateTxStatesToFinalizedUsingReceiptIds_Call { + _c.Call.Return(run) + return _c +} + // UpdateTxUnstartedToInProgress provides a mock function with given fields: ctx, etx, attempt func (_m *EvmTxStore) UpdateTxUnstartedToInProgress(ctx context.Context, etx *types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], attempt *types.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) error { ret := _m.Called(ctx, etx, attempt) diff --git a/core/chains/evm/txmgr/models.go b/core/chains/evm/txmgr/models.go index f8682ffd50..1ba3d193cb 100644 --- a/core/chains/evm/txmgr/models.go +++ b/core/chains/evm/txmgr/models.go @@ -36,12 +36,13 @@ type ( Tx = txmgrtypes.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] TxMeta = txmgrtypes.TxMeta[common.Address, common.Hash] TxAttempt = txmgrtypes.TxAttempt[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] - Receipt = dbReceipt // EvmReceipt is the exported DB table model for receipts + Receipt = DbReceipt // DbReceipt is the exported DB table model for receipts ReceiptPlus = txmgrtypes.ReceiptPlus[*evmtypes.Receipt] StuckTxDetector = txmgrtypes.StuckTxDetector[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] TxmClient = txmgrtypes.TxmClient[*big.Int, common.Address, common.Hash, common.Hash, *evmtypes.Receipt, evmtypes.Nonce, gas.EvmFee] TransactionClient = txmgrtypes.TransactionClient[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] ChainReceipt = txmgrtypes.ChainReceipt[common.Hash, common.Hash] + Finalizer = txmgrtypes.Finalizer[common.Hash, *evmtypes.Head] ) var _ KeyStore = (keystore.Eth)(nil) // check interface in txmgr to avoid circular import diff --git a/core/chains/evm/txmgr/reaper_test.go b/core/chains/evm/txmgr/reaper_test.go index b3ce48b702..cfaccdf04e 100644 --- a/core/chains/evm/txmgr/reaper_test.go +++ b/core/chains/evm/txmgr/reaper_test.go @@ -12,18 +12,17 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/utils" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" - txmgrmocks "github.com/smartcontractkit/chainlink/v2/common/txmgr/types/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" ) -func newReaperWithChainID(t *testing.T, db txmgrtypes.TxHistoryReaper[*big.Int], cfg txmgrtypes.ReaperChainConfig, txConfig txmgrtypes.ReaperTransactionsConfig, cid *big.Int) *txmgr.Reaper { - return txmgr.NewEvmReaper(logger.Test(t), db, cfg, txConfig, cid) +func newReaperWithChainID(t *testing.T, db txmgrtypes.TxHistoryReaper[*big.Int], txConfig txmgrtypes.ReaperTransactionsConfig, cid *big.Int) *txmgr.Reaper { + return txmgr.NewEvmReaper(logger.Test(t), db, txConfig, cid) } -func newReaper(t *testing.T, db txmgrtypes.TxHistoryReaper[*big.Int], cfg txmgrtypes.ReaperChainConfig, txConfig txmgrtypes.ReaperTransactionsConfig) *txmgr.Reaper { - return newReaperWithChainID(t, db, cfg, txConfig, &cltest.FixtureChainID) +func newReaper(t *testing.T, db txmgrtypes.TxHistoryReaper[*big.Int], txConfig txmgrtypes.ReaperTransactionsConfig) *txmgr.Reaper { + return newReaperWithChainID(t, db, txConfig, &cltest.FixtureChainID) } type reaperConfig struct { @@ -51,12 +50,9 @@ func TestReaper_ReapTxes(t *testing.T) { oneDayAgo := time.Now().Add(-24 * time.Hour) t.Run("with nothing in the database, doesn't error", func(t *testing.T) { - config := txmgrmocks.NewReaperConfig(t) - config.On("FinalityDepth").Return(uint32(10)) - tc := &reaperConfig{reaperThreshold: 1 * time.Hour} - r := newReaper(t, txStore, config, tc) + r := newReaper(t, txStore, tc) err := r.ReapTxes(42) assert.NoError(t, err) @@ -66,11 +62,9 @@ func TestReaper_ReapTxes(t *testing.T) { mustInsertConfirmedEthTxWithReceipt(t, txStore, from, nonce, 5) t.Run("skips if threshold=0", func(t *testing.T) { - config := txmgrmocks.NewReaperConfig(t) - tc := &reaperConfig{reaperThreshold: 0 * time.Second} - r := newReaper(t, txStore, config, tc) + r := newReaper(t, txStore, tc) err := r.ReapTxes(42) assert.NoError(t, err) @@ -79,12 +73,9 @@ func TestReaper_ReapTxes(t *testing.T) { }) t.Run("doesn't touch ethtxes with different chain ID", func(t *testing.T) { - config := txmgrmocks.NewReaperConfig(t) - config.On("FinalityDepth").Return(uint32(10)) - tc := &reaperConfig{reaperThreshold: 1 * time.Hour} - r := newReaperWithChainID(t, txStore, config, tc, big.NewInt(42)) + r := newReaperWithChainID(t, txStore, tc, big.NewInt(42)) err := r.ReapTxes(42) assert.NoError(t, err) @@ -92,41 +83,30 @@ func TestReaper_ReapTxes(t *testing.T) { cltest.AssertCount(t, db, "evm.txes", 1) }) - t.Run("deletes confirmed evm.txes that exceed the age threshold with at least EVM.FinalityDepth blocks above their receipt", func(t *testing.T) { - config := txmgrmocks.NewReaperConfig(t) - config.On("FinalityDepth").Return(uint32(10)) - + t.Run("deletes finalized evm.txes that exceed the age threshold", func(t *testing.T) { tc := &reaperConfig{reaperThreshold: 1 * time.Hour} - r := newReaper(t, txStore, config, tc) + r := newReaper(t, txStore, tc) err := r.ReapTxes(42) assert.NoError(t, err) // Didn't delete because eth_tx was not old enough cltest.AssertCount(t, db, "evm.txes", 1) - pgtest.MustExec(t, db, `UPDATE evm.txes SET created_at=$1`, oneDayAgo) - - err = r.ReapTxes(12) - assert.NoError(t, err) - // Didn't delete because eth_tx although old enough, was still within EVM.FinalityDepth of the current head - cltest.AssertCount(t, db, "evm.txes", 1) + pgtest.MustExec(t, db, `UPDATE evm.txes SET created_at=$1, state='finalized'`, oneDayAgo) err = r.ReapTxes(42) assert.NoError(t, err) - // Now it deleted because the eth_tx was past EVM.FinalityDepth + // Now it deleted because the eth_tx was past the age threshold cltest.AssertCount(t, db, "evm.txes", 0) }) mustInsertFatalErrorEthTx(t, txStore, from) t.Run("deletes errored evm.txes that exceed the age threshold", func(t *testing.T) { - config := txmgrmocks.NewReaperConfig(t) - config.On("FinalityDepth").Return(uint32(10)) - tc := &reaperConfig{reaperThreshold: 1 * time.Hour} - r := newReaper(t, txStore, config, tc) + r := newReaper(t, txStore, tc) err := r.ReapTxes(42) assert.NoError(t, err) @@ -140,4 +120,24 @@ func TestReaper_ReapTxes(t *testing.T) { // Deleted because it is old enough now cltest.AssertCount(t, db, "evm.txes", 0) }) + + mustInsertConfirmedEthTxWithReceipt(t, txStore, from, 0, 42) + + t.Run("deletes confirmed evm.txes that exceed the age threshold", func(t *testing.T) { + tc := &reaperConfig{reaperThreshold: 1 * time.Hour} + + r := newReaper(t, txStore, tc) + + err := r.ReapTxes(42) + assert.NoError(t, err) + // Didn't delete because eth_tx was not old enough + cltest.AssertCount(t, db, "evm.txes", 1) + + pgtest.MustExec(t, db, `UPDATE evm.txes SET created_at=$1`, oneDayAgo) + + err = r.ReapTxes(42) + assert.NoError(t, err) + // Now it deleted because the eth_tx was past the age threshold + cltest.AssertCount(t, db, "evm.txes", 0) + }) } diff --git a/core/chains/evm/txmgr/test_helpers.go b/core/chains/evm/txmgr/test_helpers.go index b1317cb421..a66727ce13 100644 --- a/core/chains/evm/txmgr/test_helpers.go +++ b/core/chains/evm/txmgr/test_helpers.go @@ -53,6 +53,7 @@ type TestEvmConfig struct { Threshold uint32 MinAttempts uint32 DetectionApiUrl *url.URL + RpcDefaultBatchSize uint32 } func (e *TestEvmConfig) Transactions() evmconfig.Transactions { @@ -65,6 +66,8 @@ func (e *TestEvmConfig) FinalityDepth() uint32 { return 42 } func (e *TestEvmConfig) ChainType() chaintype.ChainType { return "" } +func (e *TestEvmConfig) RPCDefaultBatchSize() uint32 { return e.RpcDefaultBatchSize } + type TestGasEstimatorConfig struct { bumpThreshold uint64 } @@ -152,10 +155,9 @@ type autoPurgeConfig struct { func (a *autoPurgeConfig) Enabled() bool { return false } type MockConfig struct { - EvmConfig *TestEvmConfig - RpcDefaultBatchSize uint32 - finalityDepth uint32 - finalityTagEnabled bool + EvmConfig *TestEvmConfig + finalityDepth uint32 + finalityTagEnabled bool } func (c *MockConfig) EVM() evmconfig.EVM { @@ -167,11 +169,10 @@ func (c *MockConfig) ChainType() chaintype.ChainType { return "" } func (c *MockConfig) FinalityDepth() uint32 { return c.finalityDepth } func (c *MockConfig) SetFinalityDepth(fd uint32) { c.finalityDepth = fd } func (c *MockConfig) FinalityTagEnabled() bool { return c.finalityTagEnabled } -func (c *MockConfig) RPCDefaultBatchSize() uint32 { return c.RpcDefaultBatchSize } func MakeTestConfigs(t *testing.T) (*MockConfig, *TestDatabaseConfig, *TestEvmConfig) { db := &TestDatabaseConfig{defaultQueryTimeout: utils.DefaultQueryTimeout} - ec := &TestEvmConfig{BumpThreshold: 42, MaxInFlight: uint32(42), MaxQueued: uint64(0), ReaperInterval: time.Duration(0), ReaperThreshold: time.Duration(0)} + ec := &TestEvmConfig{BumpThreshold: 42, MaxInFlight: uint32(42), MaxQueued: uint64(0), ReaperInterval: time.Duration(0), ReaperThreshold: time.Duration(0), RpcDefaultBatchSize: uint32(250)} config := &MockConfig{EvmConfig: ec} return config, db, ec } diff --git a/core/chains/evm/txmgr/txmgr_test.go b/core/chains/evm/txmgr/txmgr_test.go index 2314225cb0..d4bfbffd12 100644 --- a/core/chains/evm/txmgr/txmgr_test.go +++ b/core/chains/evm/txmgr/txmgr_test.go @@ -85,7 +85,8 @@ func makeTestEvmTxm( lggr, lp, keyStore, - estimator) + estimator, + ht) } func TestTxm_SendNativeToken_DoesNotSendToZero(t *testing.T) { @@ -489,14 +490,20 @@ func TestTxm_Lifecycle(t *testing.T) { config, dbConfig, evmConfig := txmgr.MakeTestConfigs(t) config.SetFinalityDepth(uint32(42)) - config.RpcDefaultBatchSize = uint32(4) + evmConfig.RpcDefaultBatchSize = uint32(4) evmConfig.ResendAfterThreshold = 1 * time.Hour evmConfig.ReaperThreshold = 1 * time.Hour evmConfig.ReaperInterval = 1 * time.Hour kst.On("EnabledAddressesForChain", mock.Anything, &cltest.FixtureChainID).Return([]common.Address{}, nil) + head := cltest.Head(42) + finalizedHead := cltest.Head(0) + + ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(head, nil).Once() + ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(finalizedHead, nil).Once() + keyChangeCh := make(chan struct{}) unsub := cltest.NewAwaiter() kst.On("SubscribeToKeyChanges", mock.Anything).Return(keyChangeCh, unsub.ItHappened) @@ -505,7 +512,6 @@ func TestTxm_Lifecycle(t *testing.T) { txm, err := makeTestEvmTxm(t, db, ethClient, estimator, evmConfig, evmConfig.GasEstimator(), evmConfig.Transactions(), dbConfig, dbConfig.Listener(), kst) require.NoError(t, err) - head := cltest.Head(42) // It should not hang or panic txm.OnNewLongestChain(tests.Context(t), head) @@ -607,8 +613,21 @@ func TestTxm_GetTransactionStatus(t *testing.T) { gcfg := configtest.NewTestGeneralConfig(t) cfg := evmtest.NewChainScopedConfig(t, gcfg) + head := &evmtypes.Head{ + Hash: utils.NewHash(), + Number: 100, + Parent: &evmtypes.Head{ + Hash: utils.NewHash(), + Number: 99, + IsFinalized: true, + }, + } + ethClient := evmtest.NewEthClientMockWithDefaultChain(t) ethClient.On("PendingNonceAt", mock.Anything, mock.Anything).Return(uint64(0), nil).Maybe() + ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(head, nil).Once() + ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(head.Parent, nil).Once() + ethClient.On("HeadByNumber", mock.Anything, mock.Anything).Return(head, nil) feeEstimator := gasmocks.NewEvmFeeEstimator(t) feeEstimator.On("Start", mock.Anything).Return(nil).Once() feeEstimator.On("Close", mock.Anything).Return(nil).Once() @@ -617,15 +636,6 @@ func TestTxm_GetTransactionStatus(t *testing.T) { require.NoError(t, err) servicetest.Run(t, txm) - head := &evmtypes.Head{ - Hash: utils.NewHash(), - Number: 100, - Parent: &evmtypes.Head{ - Hash: utils.NewHash(), - Number: 99, - IsFinalized: true, - }, - } txm.OnNewLongestChain(ctx, head) t.Run("returns error if transaction not found", func(t *testing.T) { @@ -671,7 +681,7 @@ func TestTxm_GetTransactionStatus(t *testing.T) { require.Equal(t, commontypes.Unknown, state) }) - t.Run("returns unconfirmed for unconfirmed state", func(t *testing.T) { + t.Run("returns pending for unconfirmed state", func(t *testing.T) { idempotencyKey := uuid.New().String() _, fromAddress := cltest.MustInsertRandomKey(t, ethKeyStore) nonce := evmtypes.Nonce(0) @@ -690,7 +700,7 @@ func TestTxm_GetTransactionStatus(t *testing.T) { require.NoError(t, err) state, err := txm.GetTransactionStatus(ctx, idempotencyKey) require.NoError(t, err) - require.Equal(t, commontypes.Unconfirmed, state) + require.Equal(t, commontypes.Pending, state) }) t.Run("returns unconfirmed for confirmed state", func(t *testing.T) { @@ -715,14 +725,43 @@ func TestTxm_GetTransactionStatus(t *testing.T) { attempt := cltest.NewLegacyEthTxAttempt(t, tx.ID) err = txStore.InsertTxAttempt(ctx, &attempt) require.NoError(t, err) - // Insert receipt for finalized block num - mustInsertEthReceipt(t, txStore, head.Parent.Number, head.ParentHash, attempt.Hash) + // Insert receipt for unfinalized block num + mustInsertEthReceipt(t, txStore, head.Number, head.Hash, attempt.Hash) state, err := txm.GetTransactionStatus(ctx, idempotencyKey) require.NoError(t, err) require.Equal(t, commontypes.Unconfirmed, state) }) - t.Run("returns unconfirmed for confirmed missing receipt state", func(t *testing.T) { + t.Run("returns finalized for finalized state", func(t *testing.T) { + idempotencyKey := uuid.New().String() + _, fromAddress := cltest.MustInsertRandomKey(t, ethKeyStore) + nonce := evmtypes.Nonce(0) + broadcast := time.Now() + tx := &txmgr.Tx{ + Sequence: &nonce, + IdempotencyKey: &idempotencyKey, + FromAddress: fromAddress, + EncodedPayload: []byte{1, 2, 3}, + FeeLimit: feeLimit, + State: txmgrcommon.TxFinalized, + BroadcastAt: &broadcast, + InitialBroadcastAt: &broadcast, + } + err := txStore.InsertTx(ctx, tx) + require.NoError(t, err) + tx, err = txStore.FindTxWithIdempotencyKey(ctx, idempotencyKey, testutils.FixtureChainID) + require.NoError(t, err) + attempt := cltest.NewLegacyEthTxAttempt(t, tx.ID) + err = txStore.InsertTxAttempt(ctx, &attempt) + require.NoError(t, err) + // Insert receipt for finalized block num + mustInsertEthReceipt(t, txStore, head.Parent.Number, head.Parent.Hash, attempt.Hash) + state, err := txm.GetTransactionStatus(ctx, idempotencyKey) + require.NoError(t, err) + require.Equal(t, commontypes.Finalized, state) + }) + + t.Run("returns pending for confirmed missing receipt state", func(t *testing.T) { idempotencyKey := uuid.New().String() _, fromAddress := cltest.MustInsertRandomKey(t, ethKeyStore) nonce := evmtypes.Nonce(0) @@ -741,7 +780,7 @@ func TestTxm_GetTransactionStatus(t *testing.T) { require.NoError(t, err) state, err := txm.GetTransactionStatus(ctx, idempotencyKey) require.NoError(t, err) - require.Equal(t, commontypes.Unconfirmed, state) + require.Equal(t, commontypes.Pending, state) }) t.Run("returns fatal for fatal error state with terminally stuck error", func(t *testing.T) { @@ -1042,6 +1081,12 @@ func mustCreateUnstartedTxFromEvmTxRequest(t testing.TB, txStore txmgr.EvmTxStor return tx } +func mustInsertUnstartedTx(t testing.TB, txStore txmgr.TestEvmTxStore, fromAddress common.Address) { + etx := cltest.NewEthTx(fromAddress) + ctx := tests.Context(t) + require.NoError(t, txStore.InsertTx(ctx, &etx)) +} + func txRequestWithStrategy(strategy txmgrtypes.TxStrategy) func(*txmgr.TxRequest) { return func(tx *txmgr.TxRequest) { tx.Strategy = strategy diff --git a/core/chains/evm/utils/big/big.go b/core/chains/evm/utils/big/big.go index 4bb51e2732..5706fda45b 100644 --- a/core/chains/evm/utils/big/big.go +++ b/core/chains/evm/utils/big/big.go @@ -188,3 +188,8 @@ func (b *Big) Sub(c *Big) *Big { func (b *Big) Mod(c *Big) *Big { return New(bigmath.Mod(b.ToInt(), c.ToInt())) } + +// IsZero returns true if b is zero +func (b *Big) IsZero() bool { + return b.ToInt().Sign() == 0 +} diff --git a/core/chains/legacyevm/chain.go b/core/chains/legacyevm/chain.go index 129c031882..022f4cc531 100644 --- a/core/chains/legacyevm/chain.go +++ b/core/chains/legacyevm/chain.go @@ -241,13 +241,14 @@ func newChain(ctx context.Context, cfg *evmconfig.ChainScoped, nodes []*toml.Nod KeepFinalizedBlocksDepth: int64(cfg.EVM().LogKeepBlocksDepth()), LogPrunePageSize: int64(cfg.EVM().LogPrunePageSize()), BackupPollerBlockDelay: int64(cfg.EVM().BackupLogPollerBlockDelay()), + ClientErrors: cfg.EVM().NodePool().Errors(), } logPoller = logpoller.NewLogPoller(logpoller.NewObservedORM(chainID, opts.DS, l), client, l, headTracker, lpOpts) } } // note: gas estimator is started as a part of the txm - txm, gasEstimator, err := newEvmTxm(opts.DS, cfg.EVM(), opts.AppConfig.EVMRPCEnabled(), opts.AppConfig.Database(), opts.AppConfig.Database().Listener(), client, l, logPoller, opts) + txm, gasEstimator, err := newEvmTxm(opts.DS, cfg.EVM(), opts.AppConfig.EVMRPCEnabled(), opts.AppConfig.Database(), opts.AppConfig.Database().Listener(), client, l, logPoller, opts, headTracker) if err != nil { return nil, fmt.Errorf("failed to instantiate EvmTxm for chain with ID %s: %w", chainID.String(), err) } diff --git a/core/chains/legacyevm/evm_txm.go b/core/chains/legacyevm/evm_txm.go index cecfd4ffaf..ab11674966 100644 --- a/core/chains/legacyevm/evm_txm.go +++ b/core/chains/legacyevm/evm_txm.go @@ -7,6 +7,7 @@ import ( evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" + httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -22,6 +23,7 @@ func newEvmTxm( lggr logger.Logger, logPoller logpoller.LogPoller, opts ChainRelayExtenderConfig, + headTracker httypes.HeadTracker, ) (txm txmgr.TxManager, estimator gas.EvmFeeEstimator, err error, @@ -63,7 +65,8 @@ func newEvmTxm( lggr, logPoller, opts.KeyStore, - estimator) + estimator, + headTracker) } else { txm = opts.GenTxManager(chainID) } diff --git a/core/cmd/app.go b/core/cmd/app.go index 1ccb3da9a0..53c96980de 100644 --- a/core/cmd/app.go +++ b/core/cmd/app.go @@ -168,6 +168,10 @@ func NewApp(s *Shell) *cli.App { Usage: "Prints a health report", Action: s.Health, Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "failing, f", + Usage: "filter for failing services", + }, cli.BoolFlag{ Name: "json, j", Usage: "json output", diff --git a/core/cmd/shell.go b/core/cmd/shell.go index 3d055bb03a..515cc96869 100644 --- a/core/cmd/shell.go +++ b/core/cmd/shell.go @@ -168,6 +168,7 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.G capabilitiesRegistry := capabilities.NewRegistry(appLggr) + unrestrictedClient := clhttp.NewUnrestrictedHTTPClient() // create the relayer-chain interoperators from application configuration relayerFactory := chainlink.RelayerFactory{ Logger: appLggr, @@ -175,6 +176,7 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.G GRPCOpts: grpcOpts, MercuryPool: mercuryPool, CapabilitiesRegistry: capabilitiesRegistry, + HTTPClient: unrestrictedClient, } evmFactoryCfg := chainlink.EVMFactoryConfig{ @@ -208,6 +210,13 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.G } initOps = append(initOps, chainlink.InitStarknet(ctx, relayerFactory, starkCfg)) } + if cfg.AptosEnabled() { + aptosCfg := chainlink.AptosFactoryConfig{ + Keystore: keyStore.Aptos(), + TOMLConfigs: cfg.AptosConfigs(), + } + initOps = append(initOps, chainlink.InitAptos(ctx, relayerFactory, aptosCfg)) + } relayChainInterops, err := chainlink.NewCoreRelayerChainInteroperators(initOps...) if err != nil { @@ -221,7 +230,6 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.G } restrictedClient := clhttp.NewRestrictedHTTPClient(cfg.Database(), appLggr) - unrestrictedClient := clhttp.NewUnrestrictedHTTPClient() externalInitiatorManager := webhook.NewExternalInitiatorManager(ds, unrestrictedClient) return chainlink.NewApplication(chainlink.ApplicationOpts{ Config: cfg, diff --git a/core/cmd/shell_local.go b/core/cmd/shell_local.go index e19cc485d8..604daf7568 100644 --- a/core/cmd/shell_local.go +++ b/core/cmd/shell_local.go @@ -435,6 +435,9 @@ func (s *Shell) runNode(c *cli.Context) error { if s.Config.StarkNetEnabled() { enabledChains = append(enabledChains, chaintype.StarkNet) } + if s.Config.AptosEnabled() { + enabledChains = append(enabledChains, chaintype.Aptos) + } err2 := app.GetKeyStore().OCR2().EnsureKeys(rootCtx, enabledChains...) if err2 != nil { return errors.Wrap(err2, "failed to ensure ocr key") @@ -464,6 +467,12 @@ func (s *Shell) runNode(c *cli.Context) error { return errors.Wrap(err2, "failed to ensure starknet key") } } + if s.Config.AptosEnabled() { + err2 := app.GetKeyStore().Aptos().EnsureKey(rootCtx) + if err2 != nil { + return errors.Wrap(err2, "failed to ensure aptos key") + } + } err2 := app.GetKeyStore().CSA().EnsureKey(rootCtx) if err2 != nil { @@ -669,7 +678,7 @@ func (s *Shell) RebroadcastTransactions(c *cli.Context) (err error) { feeCfg := txmgr.NewEvmTxmFeeConfig(chain.Config().EVM().GasEstimator()) stuckTxDetector := txmgr.NewStuckTxDetector(lggr, ethClient.ConfiguredChainID(), "", assets.NewWei(assets.NewEth(100).ToInt()), chain.Config().EVM().Transactions().AutoPurge(), nil, orm, ethClient) ec := txmgr.NewEvmConfirmer(orm, txmgr.NewEvmTxmClient(ethClient, chain.Config().EVM().NodePool().Errors()), - cfg, feeCfg, chain.Config().EVM().Transactions(), app.GetConfig().Database(), keyStore.Eth(), txBuilder, chain.Logger(), stuckTxDetector) + cfg, feeCfg, chain.Config().EVM().Transactions(), app.GetConfig().Database(), keyStore.Eth(), txBuilder, chain.Logger(), stuckTxDetector, chain.HeadTracker()) totalNonces := endingNonce - beginningNonce + 1 nonces := make([]evmtypes.Nonce, totalNonces) for i := int64(0); i < totalNonces; i++ { diff --git a/core/cmd/shell_local_test.go b/core/cmd/shell_local_test.go index 0bea1816a8..783781723e 100644 --- a/core/cmd/shell_local_test.go +++ b/core/cmd/shell_local_test.go @@ -11,9 +11,9 @@ import ( commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" - "github.com/smartcontractkit/chainlink/v2/core/capabilities" "github.com/smartcontractkit/chainlink/v2/common/client" + "github.com/smartcontractkit/chainlink/v2/core/capabilities" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/cmd" cmdMocks "github.com/smartcontractkit/chainlink/v2/core/cmd/mocks" diff --git a/core/cmd/shell_remote.go b/core/cmd/shell_remote.go index aab4a94da6..0aa3f3837d 100644 --- a/core/cmd/shell_remote.go +++ b/core/cmd/shell_remote.go @@ -517,7 +517,11 @@ func (s *Shell) Health(c *cli.Context) error { if c.Bool("json") { mime = gin.MIMEJSON } - resp, err := s.HTTP.Get(s.ctx(), "/health", map[string]string{"Accept": mime}) + u := "/health" + if c.Bool("failing") { + u += "?failing" + } + resp, err := s.HTTP.Get(s.ctx(), u, map[string]string{"Accept": mime}) if err != nil { return s.errorOut(err) } diff --git a/core/config/docs/chains-evm.toml b/core/config/docs/chains-evm.toml index bffc9ddf0c..98d5dd74ba 100644 --- a/core/config/docs/chains-evm.toml +++ b/core/config/docs/chains-evm.toml @@ -439,6 +439,8 @@ TransactionAlreadyMined = '(: |^)transaction already mined' # Example Fatal = '(: |^)fatal' # Example # ServiceUnavailable is a regex pattern to match against service unavailable errors. ServiceUnavailable = '(: |^)service unavailable' # Example +# TooManyResults is a regex pattern to match an eth_getLogs error indicating the result set is too large to return +TooManyResults = '(: |^)too many results' # Example [EVM.OCR] # ContractConfirmations sets `OCR.ContractConfirmations` for this EVM chain. @@ -479,3 +481,5 @@ GasLimit = 5400000 # Default FromAddress = '0x2a3e23c6f242F5345320814aC8a1b4E58707D292' # Example # ForwarderAddress is the keystone forwarder contract address on chain. ForwarderAddress = '0x2a3e23c6f242F5345320814aC8a1b4E58707D292' # Example +# DefaultGasLimit is the default gas limit for workflow transactions. +DefaultGasLimit = 400_000 # Default diff --git a/core/config/docs/core.toml b/core/config/docs/core.toml index 91b3388949..d0960779c6 100644 --- a/core/config/docs/core.toml +++ b/core/config/docs/core.toml @@ -14,7 +14,7 @@ LogPoller = false # Default # UICSAKeys enables CSA Keys in the UI. UICSAKeys = false # Default # CCIP enables the CCIP service. -CCIP = false # Default +CCIP = true # Default [Database] # DefaultIdleInTxSessionTimeout is the maximum time allowed for a transaction to be open and idle before timing out. See Postgres `idle_in_transaction_session_timeout` for more details. diff --git a/core/config/docs/docs_test.go b/core/config/docs/docs_test.go index 8f46497cb5..7a7d665044 100644 --- a/core/config/docs/docs_test.go +++ b/core/config/docs/docs_test.go @@ -83,8 +83,11 @@ func TestDoc(t *testing.T) { docDefaults.OperatorFactoryAddress = nil require.Empty(t, docDefaults.Workflow.FromAddress) require.Empty(t, docDefaults.Workflow.ForwarderAddress) + require.Equal(t, uint64(400_000), docDefaults.Workflow.DefaultGasLimit) + docDefaults.Workflow.FromAddress = nil docDefaults.Workflow.ForwarderAddress = nil + docDefaults.Workflow.DefaultGasLimit = uint64(400_000) docDefaults.NodePool.Errors = evmcfg.ClientErrors{} // Transactions.AutoPurge configs are only set if the feature is enabled diff --git a/core/gethwrappers/ccip/generated/arm_contract/arm_contract.go b/core/gethwrappers/ccip/generated/arm_contract/arm_contract.go new file mode 100644 index 0000000000..6e225576c1 --- /dev/null +++ b/core/gethwrappers/ccip/generated/arm_contract/arm_contract.go @@ -0,0 +1,2849 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package arm_contract + +import ( + "errors" + "fmt" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" +) + +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +type IRMNTaggedRoot struct { + CommitStore common.Address + Root [32]byte +} + +type RMNConfig struct { + Voters []RMNVoter + BlessWeightThreshold uint16 + CurseWeightThreshold uint16 +} + +type RMNOwnerUnvoteToCurseRequest struct { + CurseVoteAddr common.Address + Unit RMNUnvoteToCurseRequest + ForceUnvote bool +} + +type RMNRecordedCurseRelatedOp struct { + Tag uint8 + BlockTimestamp uint64 + Cursed bool + CurseVoteAddr common.Address + Subject [16]byte + CurseId [16]byte +} + +type RMNUnvoteToCurseRequest struct { + Subject [16]byte + CursesHash [28]byte +} + +type RMNVoter struct { + BlessVoteAddr common.Address + CurseVoteAddr common.Address + BlessWeight uint8 + CurseWeight uint8 +} + +var ARMContractMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"blessVoteAddr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"curseVoteAddr\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"blessWeight\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"curseWeight\",\"type\":\"uint8\"}],\"internalType\":\"structRMN.Voter[]\",\"name\":\"voters\",\"type\":\"tuple[]\"},{\"internalType\":\"uint16\",\"name\":\"blessWeightThreshold\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"curseWeightThreshold\",\"type\":\"uint16\"}],\"internalType\":\"structRMN.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"voter\",\"type\":\"address\"},{\"internalType\":\"bytes16\",\"name\":\"curseId\",\"type\":\"bytes16\"}],\"name\":\"ReusedCurseId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SubjectsMustBeStrictlyIncreasing\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"voter\",\"type\":\"address\"}],\"name\":\"UnauthorizedVoter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnvoteToCurseNoop\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"VoteToBlessForbiddenDuringActiveGlobalCurse\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"VoteToBlessNoop\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"VoteToCurseNoop\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"configVersion\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"voter\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"commitStore\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"indexed\":false,\"internalType\":\"structIRMN.TaggedRoot\",\"name\":\"taggedRoot\",\"type\":\"tuple\"}],\"name\":\"AlreadyBlessed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"configVersion\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"voter\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"commitStore\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"indexed\":false,\"internalType\":\"structIRMN.TaggedRoot\",\"name\":\"taggedRoot\",\"type\":\"tuple\"}],\"name\":\"AlreadyVotedToBless\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"configVersion\",\"type\":\"uint32\"},{\"components\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"blessVoteAddr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"curseVoteAddr\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"blessWeight\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"curseWeight\",\"type\":\"uint8\"}],\"internalType\":\"structRMN.Voter[]\",\"name\":\"voters\",\"type\":\"tuple[]\"},{\"internalType\":\"uint16\",\"name\":\"blessWeightThreshold\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"curseWeightThreshold\",\"type\":\"uint16\"}],\"indexed\":false,\"internalType\":\"structRMN.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes16\",\"name\":\"subject\",\"type\":\"bytes16\"}],\"name\":\"CurseLifted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"configVersion\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes16\",\"name\":\"subject\",\"type\":\"bytes16\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"blockTimestamp\",\"type\":\"uint64\"}],\"name\":\"Cursed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"commitStore\",\"type\":\"address\"}],\"name\":\"PermaBlessedCommitStoreAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"commitStore\",\"type\":\"address\"}],\"name\":\"PermaBlessedCommitStoreRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"voter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes16\",\"name\":\"subject\",\"type\":\"bytes16\"},{\"indexed\":false,\"internalType\":\"bytes28\",\"name\":\"onchainCursesHash\",\"type\":\"bytes28\"},{\"indexed\":false,\"internalType\":\"bytes28\",\"name\":\"cursesHash\",\"type\":\"bytes28\"}],\"name\":\"SkippedUnvoteToCurse\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"configVersion\",\"type\":\"uint32\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"commitStore\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"indexed\":false,\"internalType\":\"structIRMN.TaggedRoot\",\"name\":\"taggedRoot\",\"type\":\"tuple\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"wasBlessed\",\"type\":\"bool\"}],\"name\":\"TaggedRootBlessVotesReset\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"configVersion\",\"type\":\"uint32\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"commitStore\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"indexed\":false,\"internalType\":\"structIRMN.TaggedRoot\",\"name\":\"taggedRoot\",\"type\":\"tuple\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"accumulatedWeight\",\"type\":\"uint16\"}],\"name\":\"TaggedRootBlessed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"configVersion\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"voter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes16\",\"name\":\"subject\",\"type\":\"bytes16\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"weight\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes28\",\"name\":\"cursesHash\",\"type\":\"bytes28\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"remainingAccumulatedWeight\",\"type\":\"uint16\"}],\"name\":\"UnvotedToCurse\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"configVersion\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"voter\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"commitStore\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"indexed\":false,\"internalType\":\"structIRMN.TaggedRoot\",\"name\":\"taggedRoot\",\"type\":\"tuple\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"weight\",\"type\":\"uint8\"}],\"name\":\"VotedToBless\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"configVersion\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"voter\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes16\",\"name\":\"subject\",\"type\":\"bytes16\"},{\"indexed\":false,\"internalType\":\"bytes16\",\"name\":\"curseId\",\"type\":\"bytes16\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"weight\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"blockTimestamp\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes28\",\"name\":\"cursesHash\",\"type\":\"bytes28\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"accumulatedWeight\",\"type\":\"uint16\"}],\"name\":\"VotedToCurse\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"commitStore\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"internalType\":\"structIRMN.TaggedRoot\",\"name\":\"taggedRoot\",\"type\":\"tuple\"}],\"name\":\"getBlessProgress\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"blessVoteAddrs\",\"type\":\"address[]\"},{\"internalType\":\"uint16\",\"name\":\"accumulatedWeight\",\"type\":\"uint16\"},{\"internalType\":\"bool\",\"name\":\"blessed\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfigDetails\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"version\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"blockNumber\",\"type\":\"uint32\"},{\"components\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"blessVoteAddr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"curseVoteAddr\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"blessWeight\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"curseWeight\",\"type\":\"uint8\"}],\"internalType\":\"structRMN.Voter[]\",\"name\":\"voters\",\"type\":\"tuple[]\"},{\"internalType\":\"uint16\",\"name\":\"blessWeightThreshold\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"curseWeightThreshold\",\"type\":\"uint16\"}],\"internalType\":\"structRMN.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"subject\",\"type\":\"bytes16\"}],\"name\":\"getCurseProgress\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"curseVoteAddrs\",\"type\":\"address[]\"},{\"internalType\":\"bytes28[]\",\"name\":\"cursesHashes\",\"type\":\"bytes28[]\"},{\"internalType\":\"uint16\",\"name\":\"accumulatedWeight\",\"type\":\"uint16\"},{\"internalType\":\"bool\",\"name\":\"cursed\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCursedSubjectsCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getPermaBlessedCommitStores\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"offset\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"limit\",\"type\":\"uint256\"}],\"name\":\"getRecordedCurseRelatedOps\",\"outputs\":[{\"components\":[{\"internalType\":\"enumRMN.RecordedCurseRelatedOpTag\",\"name\":\"tag\",\"type\":\"uint8\"},{\"internalType\":\"uint64\",\"name\":\"blockTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"cursed\",\"type\":\"bool\"},{\"internalType\":\"address\",\"name\":\"curseVoteAddr\",\"type\":\"address\"},{\"internalType\":\"bytes16\",\"name\":\"subject\",\"type\":\"bytes16\"},{\"internalType\":\"bytes16\",\"name\":\"curseId\",\"type\":\"bytes16\"}],\"internalType\":\"structRMN.RecordedCurseRelatedOp[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRecordedCurseRelatedOpsCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"commitStore\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"internalType\":\"structIRMN.TaggedRoot\",\"name\":\"taggedRoot\",\"type\":\"tuple\"}],\"name\":\"isBlessed\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"subject\",\"type\":\"bytes16\"}],\"name\":\"isCursed\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isCursed\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"curseId\",\"type\":\"bytes16\"},{\"internalType\":\"bytes16[]\",\"name\":\"subjects\",\"type\":\"bytes16[]\"}],\"name\":\"ownerCurse\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"removes\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"adds\",\"type\":\"address[]\"}],\"name\":\"ownerRemoveThenAddPermaBlessedCommitStores\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"commitStore\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"internalType\":\"structIRMN.TaggedRoot[]\",\"name\":\"taggedRoots\",\"type\":\"tuple[]\"}],\"name\":\"ownerResetBlessVotes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"curseVoteAddr\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"bytes16\",\"name\":\"subject\",\"type\":\"bytes16\"},{\"internalType\":\"bytes28\",\"name\":\"cursesHash\",\"type\":\"bytes28\"}],\"internalType\":\"structRMN.UnvoteToCurseRequest\",\"name\":\"unit\",\"type\":\"tuple\"},{\"internalType\":\"bool\",\"name\":\"forceUnvote\",\"type\":\"bool\"}],\"internalType\":\"structRMN.OwnerUnvoteToCurseRequest[]\",\"name\":\"ownerUnvoteToCurseRequests\",\"type\":\"tuple[]\"}],\"name\":\"ownerUnvoteToCurse\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"blessVoteAddr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"curseVoteAddr\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"blessWeight\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"curseWeight\",\"type\":\"uint8\"}],\"internalType\":\"structRMN.Voter[]\",\"name\":\"voters\",\"type\":\"tuple[]\"},{\"internalType\":\"uint16\",\"name\":\"blessWeightThreshold\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"curseWeightThreshold\",\"type\":\"uint16\"}],\"internalType\":\"structRMN.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes16\",\"name\":\"subject\",\"type\":\"bytes16\"},{\"internalType\":\"bytes28\",\"name\":\"cursesHash\",\"type\":\"bytes28\"}],\"internalType\":\"structRMN.UnvoteToCurseRequest[]\",\"name\":\"unvoteToCurseRequests\",\"type\":\"tuple[]\"}],\"name\":\"unvoteToCurse\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"commitStore\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"internalType\":\"structIRMN.TaggedRoot[]\",\"name\":\"taggedRoots\",\"type\":\"tuple[]\"}],\"name\":\"voteToBless\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"curseId\",\"type\":\"bytes16\"},{\"internalType\":\"bytes16[]\",\"name\":\"subjects\",\"type\":\"bytes16[]\"}],\"name\":\"voteToCurse\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60806040523480156200001157600080fd5b506040516200596238038062005962833981016040819052620000349162000aff565b33806000816200008b5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000be57620000be8162000138565b505060408051608081018252600080825260208201819052918101919091526001600160c81b03606082015290506001620000fb81601062000c7d565b82606001516001600160c81b0316901c6001600160c81b0316101562000125576200012562000c99565b506200013181620001e3565b5062000e14565b336001600160a01b03821603620001925760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000082565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b620001ee816200071d565b6200020c576040516306b7c75960e31b815260040160405180910390fd5b602081015160038054604084015161ffff908116620100000263ffffffff199092169316929092179190911790555b60025415620003465760028054600091906200025a9060019062000c7d565b815481106200026d576200026d62000caf565b6000918252602080832060408051608081018252600294850290920180546001600160a01b0390811680855260019092015480821685870190815260ff600160a01b8304811687870152600160a81b909204909116606086015291875260058552828720805465ffffffffffff19169055905116855260099092529220805461ffff191690558054919250908062000309576200030962000cc5565b60008281526020902060026000199092019182020180546001600160a01b031916815560010180546001600160b01b03191690559055506200023b565b60005b81515181101562000403578151805160029190839081106200036f576200036f62000caf565b602090810291909101810151825460018181018555600094855293839020825160029092020180546001600160a01b039283166001600160a01b0319909116178155928201519284018054604084015160609094015160ff908116600160a81b0260ff60a81b1991909516600160a01b026001600160a81b0319909216959093169490941793909317161790550162000349565b50600480546000906200041c9063ffffffff1662000cdb565b82546101009290920a63ffffffff8181021990931691831602179091556004541660005b82515160ff821610156200054157600083600001518260ff16815181106200046c576200046c62000caf565b602090810291909101810151604080516060808201835263ffffffff80891683528385015160ff90811684880190815289821685870190815287516001600160a01b03908116600090815260058b5288812097518854945193518616650100000000000260ff60281b199487166401000000000264ffffffffff1990961691909716179390931791909116939093179094558587015190911683526009909552919020805491909201519092166101000261ffff1990921691909117600117905550620005398162000d01565b905062000440565b506001600160a01b0360005260096020527f3bddde647ecb7992f4c710d4e1d59d07614508581f7c22c879a79d28544538a7805461ffff191660011790556004805463ffffffff4381166401000000000263ffffffff60201b1990921691909117909155604051908216907f8c49fda8177c5c8c768eb39634bc6773695c7181711537b822451c12b2efd2a990620005db90859062000d23565b60405180910390a26040805160c08101825260048082526001600160401b03421660208301526000928201839052606082018390526080820183905260a08201839052600c80546001808201835591909452825160029094027fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7018054939490939092849260ff19909216919084908111156200067c576200067c62000dce565b021790555060208201518154604084015160608501516001600160a01b03166a010000000000000000000002600160501b600160f01b031991151569010000000000000000000260ff60481b196001600160401b039095166101000294909416610100600160501b031990931692909217929092179190911617815560808083015160a090930151811c600160801b0292901c919091176001909101555050565b80515160009015806200073257508151516010105b80620007445750602082015161ffff16155b80620007565750604082015161ffff16155b156200076457506000919050565b600080600084600001515160026200077d919062000de4565b6001600160401b0381111562000797576200079762000a24565b604051908082528060200260200182016040528015620007c1578160200160208202803683370190505b50905060005b8551518110156200095457600086600001518281518110620007ed57620007ed62000caf565b6020026020010151905060006001600160a01b031681600001516001600160a01b0316148062000828575060208101516001600160a01b0316155b806200083f575060208101516001600160a01b0316155b8062000858575060208101516001600160a01b03908116145b806200087a5750604081015160ff161580156200087a5750606081015160ff16155b156200088d575060009695505050505050565b8051836200089d84600262000de4565b620008aa90600062000dfe565b81518110620008bd57620008bd62000caf565b6001600160a01b0390921660209283029190910182015281015183620008e584600262000de4565b620008f290600162000dfe565b8151811062000905576200090562000caf565b6001600160a01b03909216602092830291909101909101526040810151620009319060ff168662000dfe565b9450806060015160ff168462000948919062000dfe565b935050600101620007c7565b5060005b8151811015620009f957600082828151811062000979576200097962000caf565b60200260200101519050600082600162000994919062000dfe565b90505b8351811015620009ee57838181518110620009b657620009b662000caf565b60200260200101516001600160a01b0316826001600160a01b031603620009e557506000979650505050505050565b60010162000997565b505060010162000958565b50846020015161ffff16831015801562000a1b5750846040015161ffff168210155b95945050505050565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b038111828210171562000a5f5762000a5f62000a24565b60405290565b604051608081016001600160401b038111828210171562000a5f5762000a5f62000a24565b604051601f8201601f191681016001600160401b038111828210171562000ab55762000ab562000a24565b604052919050565b80516001600160a01b038116811462000ad557600080fd5b919050565b805160ff8116811462000ad557600080fd5b805161ffff8116811462000ad557600080fd5b6000602080838503121562000b1357600080fd5b82516001600160401b038082111562000b2b57600080fd5b8185019150606080838803121562000b4257600080fd5b62000b4c62000a3a565b83518381111562000b5c57600080fd5b8401601f8101891362000b6e57600080fd5b80518481111562000b835762000b8362000a24565b62000b93878260051b0162000a8a565b818152878101955060079190911b82018701908a82111562000bb457600080fd5b918701915b8183101562000c33576080838c03121562000bd45760008081fd5b62000bde62000a65565b62000be98462000abd565b815262000bf889850162000abd565b89820152604062000c0b81860162000ada565b9082015262000c1c84870162000ada565b818701528652948701946080929092019162000bb9565b83525062000c45905084860162000aec565b8582015262000c576040850162000aec565b6040820152979650505050505050565b634e487b7160e01b600052601160045260246000fd5b8181038181111562000c935762000c9362000c67565b92915050565b634e487b7160e01b600052600160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052603160045260246000fd5b600063ffffffff80831681810362000cf75762000cf762000c67565b6001019392505050565b600060ff821660ff810362000d1a5762000d1a62000c67565b60010192915050565b60006020808352608080840185516060808588015282825180855260a0890191508684019450600093505b8084101562000da157845180516001600160a01b03908116845288820151168884015260408082015160ff9081169185019190915290840151168383015293860193600193909301929085019062000d4e565b509488015161ffff8116604089015294604089015161ffff811660608a0152955098975050505050505050565b634e487b7160e01b600052602160045260246000fd5b808202811582820484141762000c935762000c9362000c67565b8082018082111562000c935762000c9362000c67565b614b3e8062000e246000396000f3fe608060405234801561001057600080fd5b50600436106101825760003560e01c8063631ec73e116100d8578063979986111161008c578063d927f26711610066578063d927f26714610354578063f2fde38b14610374578063f33f28951461038757600080fd5b8063979986111461030b578063ba86a1f01461031e578063bd147ef41461033157600080fd5b806379ba5097116100bd57806379ba5097146102d35780638da5cb5b146102db578063970b8fc21461030357600080fd5b8063631ec73e146102ad5780636ba0526d146102c057600080fd5b8063397796f71161013a5780634102e4f4116101145780634102e4f4146102745780634d61677114610287578063586abe3c1461029a57600080fd5b8063397796f7146102425780633d0cf6101461024a5780633f42ab731461025d57600080fd5b8063181f5a771161016b578063181f5a77146101ba5780632cbc26bb14610203578063328d716c1461022657600080fd5b80630b009be21461018757806315c65588146101a5575b600080fd5b61018f6103a9565b60405161019c9190613e3f565b60405180910390f35b6101b86101b3366004613fdd565b6103ba565b005b6101f66040518060400160405280600981526020017f524d4e20312e352e30000000000000000000000000000000000000000000000081525081565b60405161019c9190614083565b6102166102113660046140f0565b6104e6565b604051901515815260200161019c565b600b5467ffffffffffffffff165b60405190815260200161019c565b6102166105b1565b6101b86102583660046141a0565b61068b565b6102656107ff565b60405161019c939291906142b3565b6101b86102823660046142ff565b610929565b610216610295366004614439565b61093d565b6101b86102a8366004614451565b6109cd565b6101b86102bb3660046144fc565b610a87565b6101b86102ce366004614451565b610ca0565b6101b8610d13565b60005460405173ffffffffffffffffffffffffffffffffffffffff909116815260200161019c565b600c54610234565b6101b86103193660046145d0565b610e10565b6101b861032c3660046145d0565b611368565b61034461033f3660046140f0565b61150d565b60405161019c9493929190614645565b6103676103623660046146b6565b611946565b60405161019c9190614707565b6101b8610382366004614800565b611b68565b61039a610395366004614439565b611b79565b60405161019c9392919061481b565b60606103b56007611de1565b905090565b336000818152600960205260409020805460ff16610421576040517f85412e7f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff831660048201526024015b60405180910390fd5b60045463ffffffff166000805b85518110156104a757600086828151811061044b5761044b614849565b602002602001015190506000610465858360000151611df5565b905060008061047b6001888b8760008d89611fd6565b91509150801561048d5761048d614878565b85806104965750815b95505050505080600101905061042e565b50806104df576040517ffb106b6a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050565b600b5460009067ffffffffffffffff16810361050457506000919050565b7f0100000000000000000000000000000100000000000000000000000000000000600052600a6020527fcf943f0e419056430919a3fdfd72276bc0b123ebdd670f4152b82bffbfb8bb385468010000000000000000900460ff16806105a657507fffffffffffffffffffffffffffffffff0000000000000000000000000000000082166000908152600a602052604090205468010000000000000000900460ff165b92915050565b919050565b600b5460009067ffffffffffffffff1681036105cd5750600090565b7f0100000000000000000000000000000100000000000000000000000000000000600052600a6020527fcf943f0e419056430919a3fdfd72276bc0b123ebdd670f4152b82bffbfb8bb385468010000000000000000900460ff16806103b55750507f0100000000000000000000000000000000000000000000000000000000000000600052600a6020527f1d4cd6d2639449a552dbfb463b59316946d78c518b3170daa4a4c217bef019ba5468010000000000000000900460ff1690565b6106936126a4565b60005b8251811015610746576106cc8382815181106106b4576106b4614849565b6020026020010151600761272790919063ffffffff16565b1561073e577fdca892154bbc36d0c05ccd01b3d0411875cb1b841fcdeebb384e5d0d6eb06b4483828151811061070457610704614849565b6020026020010151604051610735919073ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b60405180910390a15b600101610696565b5060005b81518110156107fa5761078082828151811061076857610768614849565b6020026020010151600761274990919063ffffffff16565b156107f2577f66b4b4752c65ae8cd2f3a0a48c7dc8b2118c60d5ea15514992eb2ddf56c9cb158282815181106107b8576107b8614849565b60200260200101516040516107e9919073ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b60405180910390a15b60010161074a565b505050565b6040805160608082018352808252600060208084018290528385018290526004548551600280549384028201608090810190985294810183815263ffffffff808416986401000000009094041696959194919385939192859285015b828210156108f95760008481526020908190206040805160808101825260028602909201805473ffffffffffffffffffffffffffffffffffffffff90811684526001918201549081168486015260ff740100000000000000000000000000000000000000008204811693850193909352750100000000000000000000000000000000000000000090049091166060830152908352909201910161085b565b505050908252506001919091015461ffff8082166020840152620100009091041660409091015292939192919050565b6109316126a4565b61093a8161276b565b50565b600060068161099b610954368690038601866148a7565b80516020918201516040805173ffffffffffffffffffffffffffffffffffffffff909316838501528281019190915280518083038201815260609092019052805191012090565b815260208101919091526040016000205460ff16806105a657506105a66109c56020840184614800565b600790612eef565b337fffffffffffffffffffffffff000000000000000000000000000000000000000181016109fd576109fd614878565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600960205260409020805460ff16610a75576040517f85412e7f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83166004820152602401610418565b610a8182858584612f1e565b50505050565b610a8f6126a4565b600454600090819063ffffffff16815b8451811015610b66576000858281518110610abc57610abc614849565b602002602001015190506000610ada84836020015160000151611df5565b9050600080610b3d600087866000015187602001518860400151600960008b6000015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002089611fd6565b915091508680610b4a5750815b96508780610b555750805b975050505050806001019050610a9f565b508215610c615760408051600280546080602082028401810190945260608301818152610c61948492849160009085015b82821015610c355760008481526020908190206040805160808101825260028602909201805473ffffffffffffffffffffffffffffffffffffffff90811684526001918201549081168486015260ff7401000000000000000000000000000000000000000082048116938501939093527501000000000000000000000000000000000000000000900490911660608301529083529092019101610b97565b505050908252506001919091015461ffff8082166020840152620100009091041660409091015261276b565b8180610c6a5750825b610a81576040517ffb106b6a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ca86126a4565b73ffffffffffffffffffffffffffffffffffffffff60005260096020527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f3bddde647ecb7992f4c710d4e1d59d07614508581f7c22c879a79d28544538a7610a8182858584612f1e565b60015473ffffffffffffffffffffffffffffffffffffffff163314610d94576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610418565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610e397f01000000000000000000000000000001000000000000000000000000000000006104e6565b15610e70576040517fcde2d97c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600454336000908152600560209081526040918290208251606081018452905463ffffffff81811680845260ff64010000000084048116958501959095526501000000000090920490931693820193909352921691908214610f00576040517f85412e7f000000000000000000000000000000000000000000000000000000008152336004820152602401610418565b600160005b8481101561132f576000868683818110610f2157610f21614849565b905060400201803603810190610f3791906148a7565b90506000610f868280516020918201516040805173ffffffffffffffffffffffffffffffffffffffff909316838501528281019190915280518083038201815260609092019052805191012090565b6000818152600660209081526040918290208251608081018452905460ff81161580158352610100820463ffffffff169383019390935265010000000000810461ffff169382019390935267010000000000000090920478ffffffffffffffffffffffffffffffffffffffffffffffffff16606083015291925090611062573373ffffffffffffffffffffffffffffffffffffffff168763ffffffff167f274d6d5b916b0a53974b7ab86c844b97a2e03a60f658cd9a4b1c028b604d7bf18560405161105291906148e0565b60405180910390a3505050611327565b8663ffffffff16816020015163ffffffff16146110a8575060408051608081018252600080825263ffffffff89166020830152918101829052606081019190915261110c565b6110ba816060015187604001516136d6565b1561110c573373ffffffffffffffffffffffffffffffffffffffff168763ffffffff167f6dfbb745226fa630aeb1b9557d17d508ddb789a04f0cb873ec16e58beb8beead8560405161105291906148e0565b6000945061112281606001518760400151613718565b78ffffffffffffffffffffffffffffffffffffffffffffffffff166060820152602086015160408201805160ff9092169161115e90839061493c565b61ffff1690525060208681015160408051865173ffffffffffffffffffffffffffffffffffffffff168152868401519381019390935260ff9091168282015251339163ffffffff8a16917f2a08a2bd2798f0aae9a843f0f4ad4de488c1b3d5f04049940cfed736ad69fb979181900360600190a3600354604082015161ffff91821691161061125757600181526040808201518151855173ffffffffffffffffffffffffffffffffffffffff1681526020808701519082015261ffff90911681830152905163ffffffff8916917f8257378aa73bf8e4ada848713526584a3dcee0fd3db3beed7397f7a7f5067cc9919081900360600190a25b60009182526006602090815260409283902082518154928401519484015160609094015178ffffffffffffffffffffffffffffffffffffffffffffffffff166701000000000000000266ffffffffffffff61ffff90951665010000000000029490941664ffffffffff63ffffffff909616610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000090941693909317179390931617179055505b600101610f05565b5080156104df576040517f604c767700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6113706126a4565b60045463ffffffff1660005b82811015610a8157600084848381811061139857611398614849565b9050604002018036038101906113ae91906148a7565b905060006113fd8280516020918201516040805173ffffffffffffffffffffffffffffffffffffffff909316838501528281019190915280518083038201815260609092019052805191012090565b60008181526006602081815260408084208151608081018352815460ff811615158252610100810463ffffffff90811683870190815265010000000000830461ffff169584019590955267010000000000000090910478ffffffffffffffffffffffffffffffffffffffffffffffffff16606083015287875294909352939093558051925193945092878216911614806114945750805b156114fe5760408051855173ffffffffffffffffffffffffffffffffffffffff1681526020808701519082015282151581830152905163ffffffff8816917f7d15a6eebaa019ea7d5b7d38937c51ebd3befbfdf51bb630a694fd28635bbcba919081900360600190a25b5050505080600101905061137c565b600454604080516002805460806020820284018101909452606083810182815290958695600095869563ffffffff9093169486949193928492918491879085015b828210156115ec5760008481526020908190206040805160808101825260028602909201805473ffffffffffffffffffffffffffffffffffffffff90811684526001918201549081168486015260ff740100000000000000000000000000000000000000008204811693850193909352750100000000000000000000000000000000000000000090049091166060830152908352909201910161154e565b505050908252506001919091015461ffff80821660208085019190915262010000909204166040928301527fffffffffffffffffffffffffffffffff000000000000000000000000000000008a166000908152600a909152908120805460ff6801000000000000000082041696509293509163ffffffff80861691161080156116725750845b6000965090508560015b60028111611939578451515b6000808760000151518310156116e35787518051849081106116ac576116ac614849565b6020026020010151602001519150876000015183815181106116d0576116d0614849565b602002602001015160600151905061170a565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff905060005b73ffffffffffffffffffffffffffffffffffffffff82166000908152600188016020908152604080832081518083019092525463ffffffff811682526401000000009004821b63ffffffff19169181019190915290878061177a57508a63ffffffff16826000015163ffffffff16145b8061179a575073ffffffffffffffffffffffffffffffffffffffff848116145b80156117b05750602082015163ffffffff191615155b9050801561186d57856001036117d0576117c987614957565b965061186d565b85600203610182576117e560ff84168e61493c565b9c506117f08761498f565b9650838f888151811061180557611805614849565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff168152505081602001518e888151811061185657611856614849565b63ffffffff19909216602092830291909101909101525b84156118835761187c8561498f565b945061188c565b50505050611895565b50505050611688565b81600103611928578267ffffffffffffffff8111156118b6576118b6613e52565b6040519080825280602002602001820160405280156118df578160200160208202803683370190505b509a508267ffffffffffffffff8111156118fb576118fb613e52565b604051908082528060200260200182016040528015611924578160200160208202803683370190505b5099505b5061193281614957565b905061167c565b5050505050509193509193565b600c5460609060009061195984866149c4565b11611965575081611988565b600c5484101561198457600c5461197d9085906149d7565b9050611988565b5060005b60008167ffffffffffffffff8111156119a3576119a3613e52565b604051908082528060200260200182016040528015611a2157816020015b6040805160c08101825260008082526020808301829052928201819052606082018190526080820181905260a082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816119c15790505b50905060005b82811015611b5f57600c611a3b82886149c4565b81548110611a4b57611a4b614849565b600091825260209091206040805160c081019091526002909202018054829060ff166004811115611a7e57611a7e6146d8565b6004811115611a8f57611a8f6146d8565b81528154610100810467ffffffffffffffff1660208301526901000000000000000000810460ff16151560408301526a0100000000000000000000900473ffffffffffffffffffffffffffffffffffffffff166060820152600190910154608081811b7fffffffffffffffffffffffffffffffff0000000000000000000000000000000090811682850152700100000000000000000000000000000000909204901b1660a0909101528251839083908110611b4c57611b4c614849565b6020908102919091010152600101611a27565b50949350505050565b611b706126a4565b61093a8161373b565b606060008080611b91610954368790038701876148a7565b6000818152600660209081526040918290208251608081018452905460ff81161515808352610100820463ffffffff90811694840185905265010000000000830461ffff169584019590955267010000000000000090910478ffffffffffffffffffffffffffffffffffffffffffffffffff166060830152600454909650939450929091169003611dd85760408101516060820151909450611c3281613830565b60ff1667ffffffffffffffff811115611c4d57611c4d613e52565b604051908082528060200260200182016040528015611c76578160200160208202803683370190505b506002805460408051602080840282018101909252828152939950600093929190849084015b82821015611d3a5760008481526020908190206040805160808101825260028602909201805473ffffffffffffffffffffffffffffffffffffffff90811684526001918201549081168486015260ff7401000000000000000000000000000000000000000082048116938501939093527501000000000000000000000000000000000000000000900490911660608301529083529092019101611c9c565b5050505090506000805b82518160ff161015611dd357611d5a84826136d6565b15611dc357828160ff1681518110611d7457611d74614849565b602002602001015160000151898381518110611d9257611d92614849565b73ffffffffffffffffffffffffffffffffffffffff90921660209283029190910190910152611dc082614957565b91505b611dcc816149ea565b9050611d44565b505050505b50509193909250565b60606000611dee8361389f565b9392505050565b7fffffffffffffffffffffffffffffffff0000000000000000000000000000000081166000908152600a60205260408120805463ffffffff858116911614611dee57805463ffffffff19811663ffffffff861690811783556003547fffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000909216176201000090910461ffff1664010000000002177fffffffffffffffffffffffffffffffffffffffffffffffff0000ffffffffffff1680825568010000000000000000900460ff1615611dee57600260005b8154811015611fcd576000826000018281548110611ee657611ee6614849565b6000918252602080832060016002909302018281015473ffffffffffffffffffffffffffffffffffffffff1684529187019052604090912080549192509063ffffffff808a169116108015611f4d57508054640100000000900460201b63ffffffff191615155b15611fc357805463ffffffff191663ffffffff891617815560018201548554750100000000000000000000000000000000000000000090910460ff16908690600690611fa89084906601000000000000900461ffff1661493c565b92506101000a81548161ffff021916908361ffff1602179055505b5050600101611ec6565b50509392505050565b6000806001896001811115611fed57611fed6146d8565b148061200a57506000896001811115612008576120086146d8565b145b61201657612016614878565b8480612037575073ffffffffffffffffffffffffffffffffffffffff878116145b80612056575073ffffffffffffffffffffffffffffffffffffffff8716155b1561207c57600089600181111561206f5761206f6146d8565b1461207c5761207c614878565b73ffffffffffffffffffffffffffffffffffffffff8716600090815260018401602090815260409182902082518084019093525463ffffffff811683526401000000009004811b63ffffffff191690820152845460ff16801561210d575073ffffffffffffffffffffffffffffffffffffffff888116148061210d57508863ffffffff16816000015163ffffffff16145b80156121235750602081015163ffffffff191615155b801561214b5750866020015163ffffffff1916816020015163ffffffff1916148061214b5750855b156122765773ffffffffffffffffffffffffffffffffffffffff881660009081526001858101602052604082209190915585548554919450610100900460ff169085906006906121aa9084906601000000000000900461ffff16614a09565b825461010092830a61ffff818102199092169282160291909117909255895188546020808d01518a54604080517fffffffffffffffffffffffffffffffff0000000000000000000000000000000090961686529590930460ff169184019190915263ffffffff1916828401526601000000000000900490921660608301525173ffffffffffffffffffffffffffffffffffffffff8b16925063ffffffff8c16917fa96a155bd67c927a6c056befbd979b78465e2b2f1276bf7d4e90a31d4f430aa8919081900360800190a35b6000808b600181111561228b5761228b6146d8565b1480156122b3575083806122b3575073ffffffffffffffffffffffffffffffffffffffff8916155b90508080156122cf5750845468010000000000000000900460ff165b80156122e157506122df856138fb565b155b156123b45784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555600b80546001945060009061232a9067ffffffffffffffff16614a24565b91906101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055507f65d0e78c3625f0956f58610cf0fb157eaf627683258875ef29af2f71d25ac8fd88600001516040516123ab91907fffffffffffffffffffffffffffffffff0000000000000000000000000000000091909116815260200190565b60405180910390a15b83806123bd5750825b15612605576000808c60018111156123d7576123d76146d8565b036123f25787156123ea5750600361240f565b50600261240f565b60018c6001811115612406576124066146d8565b03610182575060015b600c6040518060c0016040528083600481111561242e5761242e6146d8565b81526020014267ffffffffffffffff168152885468010000000000000000900460ff16151560208083019190915273ffffffffffffffffffffffffffffffffffffffff8e1660408301528c517fffffffffffffffffffffffffffffffff00000000000000000000000000000000166060830152600060809092018290528354600180820186559483529120825160029092020180549293909283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0090911690836004811115612500576125006146d8565b0217905550602082015181546040840151606085015173ffffffffffffffffffffffffffffffffffffffff166a0100000000000000000000027fffff0000000000000000000000000000000000000000ffffffffffffffffffff9115156901000000000000000000027fffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffff67ffffffffffffffff90951661010002949094167fffffffffffffffffffffffffffffffffffffffffffff000000000000000000ff90931692909217929092179190911617815560808083015160a090930151811c7001000000000000000000000000000000000292901c9190911760019091015550612696565b8751602080840151818b0151604080517fffffffffffffffffffffffffffffffff00000000000000000000000000000000909516855263ffffffff1992831693850193909352169082015273ffffffffffffffffffffffffffffffffffffffff8a16907fbabb0d7099e6ca14a29fad2a2cfb4fda2bd30f97cb3c27e546174bfb4277c1cc9060600160405180910390a25b505097509795505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314612725576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610418565b565b6000611dee8373ffffffffffffffffffffffffffffffffffffffff841661395c565b6000611dee8373ffffffffffffffffffffffffffffffffffffffff8416613a56565b61277481613aa5565b6127aa576040517f35be3ac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b602081015160038054604084015161ffff908116620100000263ffffffff199092169316929092179190911790555b6002541561298e5760028054600091906127f5906001906149d7565b8154811061280557612805614849565b60009182526020808320604080516080810182526002948502909201805473ffffffffffffffffffffffffffffffffffffffff90811680855260019092015480821685870190815260ff740100000000000000000000000000000000000000008304811687870152750100000000000000000000000000000000000000000090920490911660608601529187526005855282872080547fffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000016905590511685526009909252922080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001690558054919250908061290457612904614a66565b60008281526020902060027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019182020180547fffffffffffffffffffffffff000000000000000000000000000000000000000016815560010180547fffffffffffffffffffff000000000000000000000000000000000000000000001690559055506127d9565b60005b815151811015612ac1578151805160029190839081106129b3576129b3614849565b6020908102919091018101518254600181810185556000948552938390208251600290920201805473ffffffffffffffffffffffffffffffffffffffff9283167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116178155928201519284018054604084015160609094015160ff9081167501000000000000000000000000000000000000000000027fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff9190951674010000000000000000000000000000000000000000027fffffffffffffffffffffff0000000000000000000000000000000000000000009092169590931694909417939093171617905501612991565b5060048054600090612ad89063ffffffff16614a95565b82546101009290920a63ffffffff8181021990931691831602179091556004541660005b82515160ff82161015612c5557600083600001518260ff1681518110612b2457612b24614849565b602090810291909101810151604080516060808201835263ffffffff80891683528385015160ff908116848801908152898216858701908152875173ffffffffffffffffffffffffffffffffffffffff908116600090815260058b528881209751885494519351861665010000000000027fffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffff948716640100000000027fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000009096169190971617939093179190911693909317909455858701519091168352600990955291902080549190920151909216610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000090921691909117600117905550612c4e816149ea565b9050612afc565b5073ffffffffffffffffffffffffffffffffffffffff60005260096020527f3bddde647ecb7992f4c710d4e1d59d07614508581f7c22c879a79d28544538a780547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001660011790556004805463ffffffff438116640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff90921691909117909155604051908216907f8c49fda8177c5c8c768eb39634bc6773695c7181711537b822451c12b2efd2a990612d2f908590614ab8565b60405180910390a26040805160c081018252600480825267ffffffffffffffff421660208301526000928201839052606082018390526080820183905260a08201839052600c80546001808201835591909452825160029094027fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c701805493949093909284927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0090921691908490811115612dec57612dec6146d8565b0217905550602082015181546040840151606085015173ffffffffffffffffffffffffffffffffffffffff166a0100000000000000000000027fffff0000000000000000000000000000000000000000ffffffffffffffffffff9115156901000000000000000000027fffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffff67ffffffffffffffff90951661010002949094167fffffffffffffffffffffffffffffffffffffffffffff000000000000000000ff90931692909217929092179190911617815560808083015160a090930151811c7001000000000000000000000000000000000292901c919091176001909101555050565b73ffffffffffffffffffffffffffffffffffffffff811660009081526001830160205260408120541515611dee565b8151600003612f59576040517f55e9b08b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffffffffffff000000000000000000000000000000008316600090815260018201602052604090205460ff1615613007576040517f078f340000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff851660048201527fffffffffffffffffffffffffffffffff0000000000000000000000000000000084166024820152604401610418565b7fffffffffffffffffffffffffffffffff000000000000000000000000000000008316600090815260018281016020526040822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016909117905560045463ffffffff16905b83518110156136ce57600181101580156130ed575083818151811061309657613096614849565b60200260200101516fffffffffffffffffffffffffffffffff1916846001836130bf91906149d7565b815181106130cf576130cf614849565b60200260200101516fffffffffffffffffffffffffffffffff191610155b15613124576040517f2432d8ea00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600084828151811061313857613138614849565b60200260200101519050600061314e8483611df5565b73ffffffffffffffffffffffffffffffffffffffff8981166000818152600184016020908152604080832081518083019092525463ffffffff811682526401000000009004821b63ffffffff19169181019190915293945091148015906131be5750815163ffffffff8088169116105b806131d25750602082015163ffffffff1916155b15613225575085548254600091610100900460ff169084906006906132069084906601000000000000900461ffff1661493c565b92506101000a81548161ffff021916908361ffff16021790555061322c565b5060208101515b60408051808201825263ffffffff88168152815163ffffffff1984166020828101919091527fffffffffffffffffffffffffffffffff000000000000000000000000000000008d16828501528351808303850181526060909201909352805190830120909182019063ffffffff1916905273ffffffffffffffffffffffffffffffffffffffff8b166000818152600186016020908152604090912083518285015190921c6401000000000263ffffffff92831617905589549294509091908816907f8137bc8a8d712aaa27bfc6506d5566ac405618bd53f9831b8ca6b6fe5442ee7a9087908d9060ff610100909104166133234290565b6020898101518b54604080517fffffffffffffffffffffffffffffffff000000000000000000000000000000009889168152979096169287019290925260ff9093169385019390935267ffffffffffffffff16606084015263ffffffff191660808301526601000000000000900461ffff1660a082015260c00160405180910390a363ffffffff1981161580156133c85750825468010000000000000000900460ff16155b80156133d857506133d8836138fb565b156134c35782547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff1668010000000000000000178355600b80546000906134289067ffffffffffffffff16614acb565b91906101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055508563ffffffff167fcfdbfd8ce9a56b5f7c202c0e102184d24f47ca87121dc165063fc4c290957bde8561347e4290565b604080517fffffffffffffffffffffffffffffffff00000000000000000000000000000000909316835267ffffffffffffffff90911660208301520160405180910390a25b6040805160c081018252600080825267ffffffffffffffff42166020830152855460ff680100000000000000009091041615159282019290925273ffffffffffffffffffffffffffffffffffffffff8c1660608201527fffffffffffffffffffffffffffffffff0000000000000000000000000000000086811660808301528b1660a0820152600c80546001808201835591909352815160029093027fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c701805492939092909183917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016908360048111156135c0576135c06146d8565b0217905550602082015181546040840151606085015173ffffffffffffffffffffffffffffffffffffffff166a0100000000000000000000027fffff0000000000000000000000000000000000000000ffffffffffffffffffff9115156901000000000000000000027fffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffff67ffffffffffffffff90951661010002949094167fffffffffffffffffffffffffffffffffffffffffffff000000000000000000ff90931692909217929092179190911617815560808083015160a090930151811c7001000000000000000000000000000000000292901c9190911760019182015594909401935061306f92505050565b505050505050565b600060108260ff16106136eb576136eb614878565b50600160ff82161b821678ffffffffffffffffffffffffffffffffffffffffffffffffff16151592915050565b600060108260ff161061372d5761372d614878565b50600160ff919091161b1790565b3373ffffffffffffffffffffffffffffffffffffffff8216036137ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610418565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60006201000078ffffffffffffffffffffffffffffffffffffffffffffffffff83161061385f5761385f614878565b78ffffffffffffffffffffffffffffffffffffffffffffffffff8216156105ac5761388b600183614ae8565b90911690613898816149ea565b905061385f565b6060816000018054806020026020016040519081016040528092919081815260200182805480156138ef57602002820191906000526020600020905b8154815260200190600101908083116138db575b50505050509050919050565b73ffffffffffffffffffffffffffffffffffffffff600090815260018201602090815260408220546401000000009004901b63ffffffff19161515806105a65750505461ffff64010000000082048116660100000000000090920416101590565b60008181526001830160205260408120548015613a455760006139806001836149d7565b8554909150600090613994906001906149d7565b90508082146139f95760008660000182815481106139b4576139b4614849565b90600052602060002001549050808760000184815481106139d7576139d7614849565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080613a0a57613a0a614a66565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506105a6565b60009150506105a6565b5092915050565b6000818152600183016020526040812054613a9d575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556105a6565b5060006105a6565b8051516000901580613ab957508151516010105b80613aca5750602082015161ffff16155b80613adb5750604082015161ffff16155b15613ae857506000919050565b60008060008460000151516002613aff9190614b1a565b67ffffffffffffffff811115613b1757613b17613e52565b604051908082528060200260200182016040528015613b40578160200160208202803683370190505b50905060005b855151811015613d1157600086600001518281518110613b6857613b68614849565b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff16816000015173ffffffffffffffffffffffffffffffffffffffff161480613bc95750602081015173ffffffffffffffffffffffffffffffffffffffff16155b80613bec5750602081015173ffffffffffffffffffffffffffffffffffffffff16155b80613c115750602081015173ffffffffffffffffffffffffffffffffffffffff908116145b80613c315750604081015160ff16158015613c315750606081015160ff16155b15613c43575060009695505050505050565b805183613c51846002614b1a565b613c5c9060006149c4565b81518110613c6c57613c6c614849565b73ffffffffffffffffffffffffffffffffffffffff90921660209283029190910182015281015183613c9f846002614b1a565b613caa9060016149c4565b81518110613cba57613cba614849565b73ffffffffffffffffffffffffffffffffffffffff909216602092830291909101909101526040810151613cf19060ff16866149c4565b9450806060015160ff1684613d0691906149c4565b935050600101613b46565b5060005b8151811015613dc3576000828281518110613d3257613d32614849565b602002602001015190506000826001613d4b91906149c4565b90505b8351811015613db957838181518110613d6957613d69614849565b602002602001015173ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603613db157506000979650505050505050565b600101613d4e565b5050600101613d15565b50846020015161ffff168310158015613de45750846040015161ffff168210155b95945050505050565b60008151808452602080850194506020840160005b83811015613e3457815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101613e02565b509495945050505050565b602081526000611dee6020830184613ded565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715613ea457613ea4613e52565b60405290565b6040516060810167ffffffffffffffff81118282101715613ea457613ea4613e52565b6040516080810167ffffffffffffffff81118282101715613ea457613ea4613e52565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613f3757613f37613e52565b604052919050565b600067ffffffffffffffff821115613f5957613f59613e52565b5060051b60200190565b80357fffffffffffffffffffffffffffffffff00000000000000000000000000000000811681146105ac57600080fd5b600060408284031215613fa557600080fd5b613fad613e81565b9050613fb882613f63565b8152602082013563ffffffff1981168114613fd257600080fd5b602082015292915050565b60006020808385031215613ff057600080fd5b823567ffffffffffffffff81111561400757600080fd5b8301601f8101851361401857600080fd5b803561402b61402682613f3f565b613ef0565b8082825260208201915060208360061b85010192508783111561404d57600080fd5b6020840193505b82841015614078576140668885613f93565b82528482019150604084019350614054565b979650505050505050565b60006020808352835180602085015260005b818110156140b157858101830151858201604001528201614095565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b60006020828403121561410257600080fd5b611dee82613f63565b803573ffffffffffffffffffffffffffffffffffffffff811681146105ac57600080fd5b600082601f83011261414057600080fd5b8135602061415061402683613f3f565b8083825260208201915060208460051b87010193508684111561417257600080fd5b602086015b84811015614195576141888161410b565b8352918301918301614177565b509695505050505050565b600080604083850312156141b357600080fd5b823567ffffffffffffffff808211156141cb57600080fd5b6141d78683870161412f565b935060208501359150808211156141ed57600080fd5b506141fa8582860161412f565b9150509250929050565b8051606080845281518482018190526000926080916020918201918388019190865b82811015614280578451805173ffffffffffffffffffffffffffffffffffffffff908116865283820151168386015260408082015160ff908116918701919091529088015116878501529381019392850192600101614226565b508781015161ffff81168a83015295505050604086015193506142a9604088018561ffff169052565b9695505050505050565b600063ffffffff808616835280851660208401525060606040830152613de46060830184614204565b803560ff811681146105ac57600080fd5b803561ffff811681146105ac57600080fd5b6000602080838503121561431257600080fd5b823567ffffffffffffffff8082111561432a57600080fd5b8185019150606080838803121561434057600080fd5b614348613eaa565b83358381111561435757600080fd5b84019250601f8301881361436a57600080fd5b823561437861402682613f3f565b81815260079190911b8401860190868101908a83111561439757600080fd5b948701945b82861015614409576080868c0312156143b55760008081fd5b6143bd613ecd565b6143c68761410b565b81526143d389880161410b565b8982015260406143e48189016142dc565b908201526143f38787016142dc565b818701528252608095909501949087019061439c565b83525061441990508486016142ed565b85820152614429604085016142ed565b6040820152979650505050505050565b60006040828403121561444b57600080fd5b50919050565b6000806040838503121561446457600080fd5b61446d83613f63565b915060208084013567ffffffffffffffff81111561448a57600080fd5b8401601f8101861361449b57600080fd5b80356144a961402682613f3f565b81815260059190911b820183019083810190888311156144c857600080fd5b928401925b828410156144ed576144de84613f63565b825292840192908401906144cd565b80955050505050509250929050565b6000602080838503121561450f57600080fd5b823567ffffffffffffffff81111561452657600080fd5b8301601f8101851361453757600080fd5b803561454561402682613f3f565b81815260079190911b8201830190838101908783111561456457600080fd5b928401925b8284101561407857608084890312156145825760008081fd5b61458a613eaa565b6145938561410b565b81526145a189878701613f93565b86820152606085013580151581146145b95760008081fd5b604082015282526080939093019290840190614569565b600080602083850312156145e357600080fd5b823567ffffffffffffffff808211156145fb57600080fd5b818501915085601f83011261460f57600080fd5b81358181111561461e57600080fd5b8660208260061b850101111561463357600080fd5b60209290920196919550909350505050565b6080815260006146586080830187613ded565b82810360208481019190915286518083528782019282019060005b8181101561469657845163ffffffff191683529383019391830191600101614673565b505061ffff96909616604085015250505090151560609091015292915050565b600080604083850312156146c957600080fd5b50508035926020909101359150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60208082528251828201819052600091906040908185019086840185805b838110156147f2578251805160058110614766577f4e487b710000000000000000000000000000000000000000000000000000000084526021600452602484fd5b86528088015167ffffffffffffffff16888701528681015115158787015260608082015173ffffffffffffffffffffffffffffffffffffffff16908701526080808201517fffffffffffffffffffffffffffffffff000000000000000000000000000000009081169188019190915260a091820151169086015260c09094019391860191600101614725565b509298975050505050505050565b60006020828403121561481257600080fd5b611dee8261410b565b60608152600061482e6060830186613ded565b61ffff94909416602083015250901515604090910152919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b6000604082840312156148b957600080fd5b6148c1613e81565b6148ca8361410b565b8152602083013560208201528091505092915050565b815173ffffffffffffffffffffffffffffffffffffffff16815260208083015190820152604081016105a6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b61ffff818116838216019080821115613a4f57613a4f61490d565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036149885761498861490d565b5060010190565b60008161499e5761499e61490d565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b808201808211156105a6576105a661490d565b818103818111156105a6576105a661490d565b600060ff821660ff8103614a0057614a0061490d565b60010192915050565b61ffff828116828216039080821115613a4f57613a4f61490d565b600067ffffffffffffffff821680614a3e57614a3e61490d565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b600063ffffffff808316818103614aae57614aae61490d565b6001019392505050565b602081526000611dee6020830184614204565b600067ffffffffffffffff808316818103614aae57614aae61490d565b78ffffffffffffffffffffffffffffffffffffffffffffffffff828116828216039080821115613a4f57613a4f61490d565b80820281158282048414176105a6576105a661490d56fea164736f6c6343000818000a", +} + +var ARMContractABI = ARMContractMetaData.ABI + +var ARMContractBin = ARMContractMetaData.Bin + +func DeployARMContract(auth *bind.TransactOpts, backend bind.ContractBackend, config RMNConfig) (common.Address, *types.Transaction, *ARMContract, error) { + parsed, err := ARMContractMetaData.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(ARMContractBin), backend, config) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &ARMContract{address: address, abi: *parsed, ARMContractCaller: ARMContractCaller{contract: contract}, ARMContractTransactor: ARMContractTransactor{contract: contract}, ARMContractFilterer: ARMContractFilterer{contract: contract}}, nil +} + +type ARMContract struct { + address common.Address + abi abi.ABI + ARMContractCaller + ARMContractTransactor + ARMContractFilterer +} + +type ARMContractCaller struct { + contract *bind.BoundContract +} + +type ARMContractTransactor struct { + contract *bind.BoundContract +} + +type ARMContractFilterer struct { + contract *bind.BoundContract +} + +type ARMContractSession struct { + Contract *ARMContract + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type ARMContractCallerSession struct { + Contract *ARMContractCaller + CallOpts bind.CallOpts +} + +type ARMContractTransactorSession struct { + Contract *ARMContractTransactor + TransactOpts bind.TransactOpts +} + +type ARMContractRaw struct { + Contract *ARMContract +} + +type ARMContractCallerRaw struct { + Contract *ARMContractCaller +} + +type ARMContractTransactorRaw struct { + Contract *ARMContractTransactor +} + +func NewARMContract(address common.Address, backend bind.ContractBackend) (*ARMContract, error) { + abi, err := abi.JSON(strings.NewReader(ARMContractABI)) + if err != nil { + return nil, err + } + contract, err := bindARMContract(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &ARMContract{address: address, abi: abi, ARMContractCaller: ARMContractCaller{contract: contract}, ARMContractTransactor: ARMContractTransactor{contract: contract}, ARMContractFilterer: ARMContractFilterer{contract: contract}}, nil +} + +func NewARMContractCaller(address common.Address, caller bind.ContractCaller) (*ARMContractCaller, error) { + contract, err := bindARMContract(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &ARMContractCaller{contract: contract}, nil +} + +func NewARMContractTransactor(address common.Address, transactor bind.ContractTransactor) (*ARMContractTransactor, error) { + contract, err := bindARMContract(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &ARMContractTransactor{contract: contract}, nil +} + +func NewARMContractFilterer(address common.Address, filterer bind.ContractFilterer) (*ARMContractFilterer, error) { + contract, err := bindARMContract(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &ARMContractFilterer{contract: contract}, nil +} + +func bindARMContract(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := ARMContractMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_ARMContract *ARMContractRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _ARMContract.Contract.ARMContractCaller.contract.Call(opts, result, method, params...) +} + +func (_ARMContract *ARMContractRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ARMContract.Contract.ARMContractTransactor.contract.Transfer(opts) +} + +func (_ARMContract *ARMContractRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ARMContract.Contract.ARMContractTransactor.contract.Transact(opts, method, params...) +} + +func (_ARMContract *ARMContractCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _ARMContract.Contract.contract.Call(opts, result, method, params...) +} + +func (_ARMContract *ARMContractTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ARMContract.Contract.contract.Transfer(opts) +} + +func (_ARMContract *ARMContractTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ARMContract.Contract.contract.Transact(opts, method, params...) +} + +func (_ARMContract *ARMContractCaller) GetBlessProgress(opts *bind.CallOpts, taggedRoot IRMNTaggedRoot) (GetBlessProgress, + + error) { + var out []interface{} + err := _ARMContract.contract.Call(opts, &out, "getBlessProgress", taggedRoot) + + outstruct := new(GetBlessProgress) + if err != nil { + return *outstruct, err + } + + outstruct.BlessVoteAddrs = *abi.ConvertType(out[0], new([]common.Address)).(*[]common.Address) + outstruct.AccumulatedWeight = *abi.ConvertType(out[1], new(uint16)).(*uint16) + outstruct.Blessed = *abi.ConvertType(out[2], new(bool)).(*bool) + + return *outstruct, err + +} + +func (_ARMContract *ARMContractSession) GetBlessProgress(taggedRoot IRMNTaggedRoot) (GetBlessProgress, + + error) { + return _ARMContract.Contract.GetBlessProgress(&_ARMContract.CallOpts, taggedRoot) +} + +func (_ARMContract *ARMContractCallerSession) GetBlessProgress(taggedRoot IRMNTaggedRoot) (GetBlessProgress, + + error) { + return _ARMContract.Contract.GetBlessProgress(&_ARMContract.CallOpts, taggedRoot) +} + +func (_ARMContract *ARMContractCaller) GetConfigDetails(opts *bind.CallOpts) (GetConfigDetails, + + error) { + var out []interface{} + err := _ARMContract.contract.Call(opts, &out, "getConfigDetails") + + outstruct := new(GetConfigDetails) + if err != nil { + return *outstruct, err + } + + outstruct.Version = *abi.ConvertType(out[0], new(uint32)).(*uint32) + outstruct.BlockNumber = *abi.ConvertType(out[1], new(uint32)).(*uint32) + outstruct.Config = *abi.ConvertType(out[2], new(RMNConfig)).(*RMNConfig) + + return *outstruct, err + +} + +func (_ARMContract *ARMContractSession) GetConfigDetails() (GetConfigDetails, + + error) { + return _ARMContract.Contract.GetConfigDetails(&_ARMContract.CallOpts) +} + +func (_ARMContract *ARMContractCallerSession) GetConfigDetails() (GetConfigDetails, + + error) { + return _ARMContract.Contract.GetConfigDetails(&_ARMContract.CallOpts) +} + +func (_ARMContract *ARMContractCaller) GetCurseProgress(opts *bind.CallOpts, subject [16]byte) (GetCurseProgress, + + error) { + var out []interface{} + err := _ARMContract.contract.Call(opts, &out, "getCurseProgress", subject) + + outstruct := new(GetCurseProgress) + if err != nil { + return *outstruct, err + } + + outstruct.CurseVoteAddrs = *abi.ConvertType(out[0], new([]common.Address)).(*[]common.Address) + outstruct.CursesHashes = *abi.ConvertType(out[1], new([][28]byte)).(*[][28]byte) + outstruct.AccumulatedWeight = *abi.ConvertType(out[2], new(uint16)).(*uint16) + outstruct.Cursed = *abi.ConvertType(out[3], new(bool)).(*bool) + + return *outstruct, err + +} + +func (_ARMContract *ARMContractSession) GetCurseProgress(subject [16]byte) (GetCurseProgress, + + error) { + return _ARMContract.Contract.GetCurseProgress(&_ARMContract.CallOpts, subject) +} + +func (_ARMContract *ARMContractCallerSession) GetCurseProgress(subject [16]byte) (GetCurseProgress, + + error) { + return _ARMContract.Contract.GetCurseProgress(&_ARMContract.CallOpts, subject) +} + +func (_ARMContract *ARMContractCaller) GetCursedSubjectsCount(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _ARMContract.contract.Call(opts, &out, "getCursedSubjectsCount") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_ARMContract *ARMContractSession) GetCursedSubjectsCount() (*big.Int, error) { + return _ARMContract.Contract.GetCursedSubjectsCount(&_ARMContract.CallOpts) +} + +func (_ARMContract *ARMContractCallerSession) GetCursedSubjectsCount() (*big.Int, error) { + return _ARMContract.Contract.GetCursedSubjectsCount(&_ARMContract.CallOpts) +} + +func (_ARMContract *ARMContractCaller) GetPermaBlessedCommitStores(opts *bind.CallOpts) ([]common.Address, error) { + var out []interface{} + err := _ARMContract.contract.Call(opts, &out, "getPermaBlessedCommitStores") + + if err != nil { + return *new([]common.Address), err + } + + out0 := *abi.ConvertType(out[0], new([]common.Address)).(*[]common.Address) + + return out0, err + +} + +func (_ARMContract *ARMContractSession) GetPermaBlessedCommitStores() ([]common.Address, error) { + return _ARMContract.Contract.GetPermaBlessedCommitStores(&_ARMContract.CallOpts) +} + +func (_ARMContract *ARMContractCallerSession) GetPermaBlessedCommitStores() ([]common.Address, error) { + return _ARMContract.Contract.GetPermaBlessedCommitStores(&_ARMContract.CallOpts) +} + +func (_ARMContract *ARMContractCaller) GetRecordedCurseRelatedOps(opts *bind.CallOpts, offset *big.Int, limit *big.Int) ([]RMNRecordedCurseRelatedOp, error) { + var out []interface{} + err := _ARMContract.contract.Call(opts, &out, "getRecordedCurseRelatedOps", offset, limit) + + if err != nil { + return *new([]RMNRecordedCurseRelatedOp), err + } + + out0 := *abi.ConvertType(out[0], new([]RMNRecordedCurseRelatedOp)).(*[]RMNRecordedCurseRelatedOp) + + return out0, err + +} + +func (_ARMContract *ARMContractSession) GetRecordedCurseRelatedOps(offset *big.Int, limit *big.Int) ([]RMNRecordedCurseRelatedOp, error) { + return _ARMContract.Contract.GetRecordedCurseRelatedOps(&_ARMContract.CallOpts, offset, limit) +} + +func (_ARMContract *ARMContractCallerSession) GetRecordedCurseRelatedOps(offset *big.Int, limit *big.Int) ([]RMNRecordedCurseRelatedOp, error) { + return _ARMContract.Contract.GetRecordedCurseRelatedOps(&_ARMContract.CallOpts, offset, limit) +} + +func (_ARMContract *ARMContractCaller) GetRecordedCurseRelatedOpsCount(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _ARMContract.contract.Call(opts, &out, "getRecordedCurseRelatedOpsCount") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_ARMContract *ARMContractSession) GetRecordedCurseRelatedOpsCount() (*big.Int, error) { + return _ARMContract.Contract.GetRecordedCurseRelatedOpsCount(&_ARMContract.CallOpts) +} + +func (_ARMContract *ARMContractCallerSession) GetRecordedCurseRelatedOpsCount() (*big.Int, error) { + return _ARMContract.Contract.GetRecordedCurseRelatedOpsCount(&_ARMContract.CallOpts) +} + +func (_ARMContract *ARMContractCaller) IsBlessed(opts *bind.CallOpts, taggedRoot IRMNTaggedRoot) (bool, error) { + var out []interface{} + err := _ARMContract.contract.Call(opts, &out, "isBlessed", taggedRoot) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +func (_ARMContract *ARMContractSession) IsBlessed(taggedRoot IRMNTaggedRoot) (bool, error) { + return _ARMContract.Contract.IsBlessed(&_ARMContract.CallOpts, taggedRoot) +} + +func (_ARMContract *ARMContractCallerSession) IsBlessed(taggedRoot IRMNTaggedRoot) (bool, error) { + return _ARMContract.Contract.IsBlessed(&_ARMContract.CallOpts, taggedRoot) +} + +func (_ARMContract *ARMContractCaller) IsCursed(opts *bind.CallOpts, subject [16]byte) (bool, error) { + var out []interface{} + err := _ARMContract.contract.Call(opts, &out, "isCursed", subject) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +func (_ARMContract *ARMContractSession) IsCursed(subject [16]byte) (bool, error) { + return _ARMContract.Contract.IsCursed(&_ARMContract.CallOpts, subject) +} + +func (_ARMContract *ARMContractCallerSession) IsCursed(subject [16]byte) (bool, error) { + return _ARMContract.Contract.IsCursed(&_ARMContract.CallOpts, subject) +} + +func (_ARMContract *ARMContractCaller) IsCursed0(opts *bind.CallOpts) (bool, error) { + var out []interface{} + err := _ARMContract.contract.Call(opts, &out, "isCursed0") + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +func (_ARMContract *ARMContractSession) IsCursed0() (bool, error) { + return _ARMContract.Contract.IsCursed0(&_ARMContract.CallOpts) +} + +func (_ARMContract *ARMContractCallerSession) IsCursed0() (bool, error) { + return _ARMContract.Contract.IsCursed0(&_ARMContract.CallOpts) +} + +func (_ARMContract *ARMContractCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _ARMContract.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_ARMContract *ARMContractSession) Owner() (common.Address, error) { + return _ARMContract.Contract.Owner(&_ARMContract.CallOpts) +} + +func (_ARMContract *ARMContractCallerSession) Owner() (common.Address, error) { + return _ARMContract.Contract.Owner(&_ARMContract.CallOpts) +} + +func (_ARMContract *ARMContractCaller) TypeAndVersion(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _ARMContract.contract.Call(opts, &out, "typeAndVersion") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +func (_ARMContract *ARMContractSession) TypeAndVersion() (string, error) { + return _ARMContract.Contract.TypeAndVersion(&_ARMContract.CallOpts) +} + +func (_ARMContract *ARMContractCallerSession) TypeAndVersion() (string, error) { + return _ARMContract.Contract.TypeAndVersion(&_ARMContract.CallOpts) +} + +func (_ARMContract *ARMContractTransactor) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ARMContract.contract.Transact(opts, "acceptOwnership") +} + +func (_ARMContract *ARMContractSession) AcceptOwnership() (*types.Transaction, error) { + return _ARMContract.Contract.AcceptOwnership(&_ARMContract.TransactOpts) +} + +func (_ARMContract *ARMContractTransactorSession) AcceptOwnership() (*types.Transaction, error) { + return _ARMContract.Contract.AcceptOwnership(&_ARMContract.TransactOpts) +} + +func (_ARMContract *ARMContractTransactor) OwnerCurse(opts *bind.TransactOpts, curseId [16]byte, subjects [][16]byte) (*types.Transaction, error) { + return _ARMContract.contract.Transact(opts, "ownerCurse", curseId, subjects) +} + +func (_ARMContract *ARMContractSession) OwnerCurse(curseId [16]byte, subjects [][16]byte) (*types.Transaction, error) { + return _ARMContract.Contract.OwnerCurse(&_ARMContract.TransactOpts, curseId, subjects) +} + +func (_ARMContract *ARMContractTransactorSession) OwnerCurse(curseId [16]byte, subjects [][16]byte) (*types.Transaction, error) { + return _ARMContract.Contract.OwnerCurse(&_ARMContract.TransactOpts, curseId, subjects) +} + +func (_ARMContract *ARMContractTransactor) OwnerRemoveThenAddPermaBlessedCommitStores(opts *bind.TransactOpts, removes []common.Address, adds []common.Address) (*types.Transaction, error) { + return _ARMContract.contract.Transact(opts, "ownerRemoveThenAddPermaBlessedCommitStores", removes, adds) +} + +func (_ARMContract *ARMContractSession) OwnerRemoveThenAddPermaBlessedCommitStores(removes []common.Address, adds []common.Address) (*types.Transaction, error) { + return _ARMContract.Contract.OwnerRemoveThenAddPermaBlessedCommitStores(&_ARMContract.TransactOpts, removes, adds) +} + +func (_ARMContract *ARMContractTransactorSession) OwnerRemoveThenAddPermaBlessedCommitStores(removes []common.Address, adds []common.Address) (*types.Transaction, error) { + return _ARMContract.Contract.OwnerRemoveThenAddPermaBlessedCommitStores(&_ARMContract.TransactOpts, removes, adds) +} + +func (_ARMContract *ARMContractTransactor) OwnerResetBlessVotes(opts *bind.TransactOpts, taggedRoots []IRMNTaggedRoot) (*types.Transaction, error) { + return _ARMContract.contract.Transact(opts, "ownerResetBlessVotes", taggedRoots) +} + +func (_ARMContract *ARMContractSession) OwnerResetBlessVotes(taggedRoots []IRMNTaggedRoot) (*types.Transaction, error) { + return _ARMContract.Contract.OwnerResetBlessVotes(&_ARMContract.TransactOpts, taggedRoots) +} + +func (_ARMContract *ARMContractTransactorSession) OwnerResetBlessVotes(taggedRoots []IRMNTaggedRoot) (*types.Transaction, error) { + return _ARMContract.Contract.OwnerResetBlessVotes(&_ARMContract.TransactOpts, taggedRoots) +} + +func (_ARMContract *ARMContractTransactor) OwnerUnvoteToCurse(opts *bind.TransactOpts, ownerUnvoteToCurseRequests []RMNOwnerUnvoteToCurseRequest) (*types.Transaction, error) { + return _ARMContract.contract.Transact(opts, "ownerUnvoteToCurse", ownerUnvoteToCurseRequests) +} + +func (_ARMContract *ARMContractSession) OwnerUnvoteToCurse(ownerUnvoteToCurseRequests []RMNOwnerUnvoteToCurseRequest) (*types.Transaction, error) { + return _ARMContract.Contract.OwnerUnvoteToCurse(&_ARMContract.TransactOpts, ownerUnvoteToCurseRequests) +} + +func (_ARMContract *ARMContractTransactorSession) OwnerUnvoteToCurse(ownerUnvoteToCurseRequests []RMNOwnerUnvoteToCurseRequest) (*types.Transaction, error) { + return _ARMContract.Contract.OwnerUnvoteToCurse(&_ARMContract.TransactOpts, ownerUnvoteToCurseRequests) +} + +func (_ARMContract *ARMContractTransactor) SetConfig(opts *bind.TransactOpts, config RMNConfig) (*types.Transaction, error) { + return _ARMContract.contract.Transact(opts, "setConfig", config) +} + +func (_ARMContract *ARMContractSession) SetConfig(config RMNConfig) (*types.Transaction, error) { + return _ARMContract.Contract.SetConfig(&_ARMContract.TransactOpts, config) +} + +func (_ARMContract *ARMContractTransactorSession) SetConfig(config RMNConfig) (*types.Transaction, error) { + return _ARMContract.Contract.SetConfig(&_ARMContract.TransactOpts, config) +} + +func (_ARMContract *ARMContractTransactor) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { + return _ARMContract.contract.Transact(opts, "transferOwnership", to) +} + +func (_ARMContract *ARMContractSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _ARMContract.Contract.TransferOwnership(&_ARMContract.TransactOpts, to) +} + +func (_ARMContract *ARMContractTransactorSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _ARMContract.Contract.TransferOwnership(&_ARMContract.TransactOpts, to) +} + +func (_ARMContract *ARMContractTransactor) UnvoteToCurse(opts *bind.TransactOpts, unvoteToCurseRequests []RMNUnvoteToCurseRequest) (*types.Transaction, error) { + return _ARMContract.contract.Transact(opts, "unvoteToCurse", unvoteToCurseRequests) +} + +func (_ARMContract *ARMContractSession) UnvoteToCurse(unvoteToCurseRequests []RMNUnvoteToCurseRequest) (*types.Transaction, error) { + return _ARMContract.Contract.UnvoteToCurse(&_ARMContract.TransactOpts, unvoteToCurseRequests) +} + +func (_ARMContract *ARMContractTransactorSession) UnvoteToCurse(unvoteToCurseRequests []RMNUnvoteToCurseRequest) (*types.Transaction, error) { + return _ARMContract.Contract.UnvoteToCurse(&_ARMContract.TransactOpts, unvoteToCurseRequests) +} + +func (_ARMContract *ARMContractTransactor) VoteToBless(opts *bind.TransactOpts, taggedRoots []IRMNTaggedRoot) (*types.Transaction, error) { + return _ARMContract.contract.Transact(opts, "voteToBless", taggedRoots) +} + +func (_ARMContract *ARMContractSession) VoteToBless(taggedRoots []IRMNTaggedRoot) (*types.Transaction, error) { + return _ARMContract.Contract.VoteToBless(&_ARMContract.TransactOpts, taggedRoots) +} + +func (_ARMContract *ARMContractTransactorSession) VoteToBless(taggedRoots []IRMNTaggedRoot) (*types.Transaction, error) { + return _ARMContract.Contract.VoteToBless(&_ARMContract.TransactOpts, taggedRoots) +} + +func (_ARMContract *ARMContractTransactor) VoteToCurse(opts *bind.TransactOpts, curseId [16]byte, subjects [][16]byte) (*types.Transaction, error) { + return _ARMContract.contract.Transact(opts, "voteToCurse", curseId, subjects) +} + +func (_ARMContract *ARMContractSession) VoteToCurse(curseId [16]byte, subjects [][16]byte) (*types.Transaction, error) { + return _ARMContract.Contract.VoteToCurse(&_ARMContract.TransactOpts, curseId, subjects) +} + +func (_ARMContract *ARMContractTransactorSession) VoteToCurse(curseId [16]byte, subjects [][16]byte) (*types.Transaction, error) { + return _ARMContract.Contract.VoteToCurse(&_ARMContract.TransactOpts, curseId, subjects) +} + +type ARMContractAlreadyBlessedIterator struct { + Event *ARMContractAlreadyBlessed + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *ARMContractAlreadyBlessedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(ARMContractAlreadyBlessed) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(ARMContractAlreadyBlessed) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *ARMContractAlreadyBlessedIterator) Error() error { + return it.fail +} + +func (it *ARMContractAlreadyBlessedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type ARMContractAlreadyBlessed struct { + ConfigVersion uint32 + Voter common.Address + TaggedRoot IRMNTaggedRoot + Raw types.Log +} + +func (_ARMContract *ARMContractFilterer) FilterAlreadyBlessed(opts *bind.FilterOpts, configVersion []uint32, voter []common.Address) (*ARMContractAlreadyBlessedIterator, error) { + + var configVersionRule []interface{} + for _, configVersionItem := range configVersion { + configVersionRule = append(configVersionRule, configVersionItem) + } + var voterRule []interface{} + for _, voterItem := range voter { + voterRule = append(voterRule, voterItem) + } + + logs, sub, err := _ARMContract.contract.FilterLogs(opts, "AlreadyBlessed", configVersionRule, voterRule) + if err != nil { + return nil, err + } + return &ARMContractAlreadyBlessedIterator{contract: _ARMContract.contract, event: "AlreadyBlessed", logs: logs, sub: sub}, nil +} + +func (_ARMContract *ARMContractFilterer) WatchAlreadyBlessed(opts *bind.WatchOpts, sink chan<- *ARMContractAlreadyBlessed, configVersion []uint32, voter []common.Address) (event.Subscription, error) { + + var configVersionRule []interface{} + for _, configVersionItem := range configVersion { + configVersionRule = append(configVersionRule, configVersionItem) + } + var voterRule []interface{} + for _, voterItem := range voter { + voterRule = append(voterRule, voterItem) + } + + logs, sub, err := _ARMContract.contract.WatchLogs(opts, "AlreadyBlessed", configVersionRule, voterRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(ARMContractAlreadyBlessed) + if err := _ARMContract.contract.UnpackLog(event, "AlreadyBlessed", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_ARMContract *ARMContractFilterer) ParseAlreadyBlessed(log types.Log) (*ARMContractAlreadyBlessed, error) { + event := new(ARMContractAlreadyBlessed) + if err := _ARMContract.contract.UnpackLog(event, "AlreadyBlessed", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type ARMContractAlreadyVotedToBlessIterator struct { + Event *ARMContractAlreadyVotedToBless + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *ARMContractAlreadyVotedToBlessIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(ARMContractAlreadyVotedToBless) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(ARMContractAlreadyVotedToBless) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *ARMContractAlreadyVotedToBlessIterator) Error() error { + return it.fail +} + +func (it *ARMContractAlreadyVotedToBlessIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type ARMContractAlreadyVotedToBless struct { + ConfigVersion uint32 + Voter common.Address + TaggedRoot IRMNTaggedRoot + Raw types.Log +} + +func (_ARMContract *ARMContractFilterer) FilterAlreadyVotedToBless(opts *bind.FilterOpts, configVersion []uint32, voter []common.Address) (*ARMContractAlreadyVotedToBlessIterator, error) { + + var configVersionRule []interface{} + for _, configVersionItem := range configVersion { + configVersionRule = append(configVersionRule, configVersionItem) + } + var voterRule []interface{} + for _, voterItem := range voter { + voterRule = append(voterRule, voterItem) + } + + logs, sub, err := _ARMContract.contract.FilterLogs(opts, "AlreadyVotedToBless", configVersionRule, voterRule) + if err != nil { + return nil, err + } + return &ARMContractAlreadyVotedToBlessIterator{contract: _ARMContract.contract, event: "AlreadyVotedToBless", logs: logs, sub: sub}, nil +} + +func (_ARMContract *ARMContractFilterer) WatchAlreadyVotedToBless(opts *bind.WatchOpts, sink chan<- *ARMContractAlreadyVotedToBless, configVersion []uint32, voter []common.Address) (event.Subscription, error) { + + var configVersionRule []interface{} + for _, configVersionItem := range configVersion { + configVersionRule = append(configVersionRule, configVersionItem) + } + var voterRule []interface{} + for _, voterItem := range voter { + voterRule = append(voterRule, voterItem) + } + + logs, sub, err := _ARMContract.contract.WatchLogs(opts, "AlreadyVotedToBless", configVersionRule, voterRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(ARMContractAlreadyVotedToBless) + if err := _ARMContract.contract.UnpackLog(event, "AlreadyVotedToBless", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_ARMContract *ARMContractFilterer) ParseAlreadyVotedToBless(log types.Log) (*ARMContractAlreadyVotedToBless, error) { + event := new(ARMContractAlreadyVotedToBless) + if err := _ARMContract.contract.UnpackLog(event, "AlreadyVotedToBless", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type ARMContractConfigSetIterator struct { + Event *ARMContractConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *ARMContractConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(ARMContractConfigSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(ARMContractConfigSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *ARMContractConfigSetIterator) Error() error { + return it.fail +} + +func (it *ARMContractConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type ARMContractConfigSet struct { + ConfigVersion uint32 + Config RMNConfig + Raw types.Log +} + +func (_ARMContract *ARMContractFilterer) FilterConfigSet(opts *bind.FilterOpts, configVersion []uint32) (*ARMContractConfigSetIterator, error) { + + var configVersionRule []interface{} + for _, configVersionItem := range configVersion { + configVersionRule = append(configVersionRule, configVersionItem) + } + + logs, sub, err := _ARMContract.contract.FilterLogs(opts, "ConfigSet", configVersionRule) + if err != nil { + return nil, err + } + return &ARMContractConfigSetIterator{contract: _ARMContract.contract, event: "ConfigSet", logs: logs, sub: sub}, nil +} + +func (_ARMContract *ARMContractFilterer) WatchConfigSet(opts *bind.WatchOpts, sink chan<- *ARMContractConfigSet, configVersion []uint32) (event.Subscription, error) { + + var configVersionRule []interface{} + for _, configVersionItem := range configVersion { + configVersionRule = append(configVersionRule, configVersionItem) + } + + logs, sub, err := _ARMContract.contract.WatchLogs(opts, "ConfigSet", configVersionRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(ARMContractConfigSet) + if err := _ARMContract.contract.UnpackLog(event, "ConfigSet", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_ARMContract *ARMContractFilterer) ParseConfigSet(log types.Log) (*ARMContractConfigSet, error) { + event := new(ARMContractConfigSet) + if err := _ARMContract.contract.UnpackLog(event, "ConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type ARMContractCurseLiftedIterator struct { + Event *ARMContractCurseLifted + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *ARMContractCurseLiftedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(ARMContractCurseLifted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(ARMContractCurseLifted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *ARMContractCurseLiftedIterator) Error() error { + return it.fail +} + +func (it *ARMContractCurseLiftedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type ARMContractCurseLifted struct { + Subject [16]byte + Raw types.Log +} + +func (_ARMContract *ARMContractFilterer) FilterCurseLifted(opts *bind.FilterOpts) (*ARMContractCurseLiftedIterator, error) { + + logs, sub, err := _ARMContract.contract.FilterLogs(opts, "CurseLifted") + if err != nil { + return nil, err + } + return &ARMContractCurseLiftedIterator{contract: _ARMContract.contract, event: "CurseLifted", logs: logs, sub: sub}, nil +} + +func (_ARMContract *ARMContractFilterer) WatchCurseLifted(opts *bind.WatchOpts, sink chan<- *ARMContractCurseLifted) (event.Subscription, error) { + + logs, sub, err := _ARMContract.contract.WatchLogs(opts, "CurseLifted") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(ARMContractCurseLifted) + if err := _ARMContract.contract.UnpackLog(event, "CurseLifted", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_ARMContract *ARMContractFilterer) ParseCurseLifted(log types.Log) (*ARMContractCurseLifted, error) { + event := new(ARMContractCurseLifted) + if err := _ARMContract.contract.UnpackLog(event, "CurseLifted", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type ARMContractCursedIterator struct { + Event *ARMContractCursed + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *ARMContractCursedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(ARMContractCursed) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(ARMContractCursed) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *ARMContractCursedIterator) Error() error { + return it.fail +} + +func (it *ARMContractCursedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type ARMContractCursed struct { + ConfigVersion uint32 + Subject [16]byte + BlockTimestamp uint64 + Raw types.Log +} + +func (_ARMContract *ARMContractFilterer) FilterCursed(opts *bind.FilterOpts, configVersion []uint32) (*ARMContractCursedIterator, error) { + + var configVersionRule []interface{} + for _, configVersionItem := range configVersion { + configVersionRule = append(configVersionRule, configVersionItem) + } + + logs, sub, err := _ARMContract.contract.FilterLogs(opts, "Cursed", configVersionRule) + if err != nil { + return nil, err + } + return &ARMContractCursedIterator{contract: _ARMContract.contract, event: "Cursed", logs: logs, sub: sub}, nil +} + +func (_ARMContract *ARMContractFilterer) WatchCursed(opts *bind.WatchOpts, sink chan<- *ARMContractCursed, configVersion []uint32) (event.Subscription, error) { + + var configVersionRule []interface{} + for _, configVersionItem := range configVersion { + configVersionRule = append(configVersionRule, configVersionItem) + } + + logs, sub, err := _ARMContract.contract.WatchLogs(opts, "Cursed", configVersionRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(ARMContractCursed) + if err := _ARMContract.contract.UnpackLog(event, "Cursed", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_ARMContract *ARMContractFilterer) ParseCursed(log types.Log) (*ARMContractCursed, error) { + event := new(ARMContractCursed) + if err := _ARMContract.contract.UnpackLog(event, "Cursed", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type ARMContractOwnershipTransferRequestedIterator struct { + Event *ARMContractOwnershipTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *ARMContractOwnershipTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(ARMContractOwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(ARMContractOwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *ARMContractOwnershipTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *ARMContractOwnershipTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type ARMContractOwnershipTransferRequested struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_ARMContract *ARMContractFilterer) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*ARMContractOwnershipTransferRequestedIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _ARMContract.contract.FilterLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return &ARMContractOwnershipTransferRequestedIterator{contract: _ARMContract.contract, event: "OwnershipTransferRequested", logs: logs, sub: sub}, nil +} + +func (_ARMContract *ARMContractFilterer) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *ARMContractOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _ARMContract.contract.WatchLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(ARMContractOwnershipTransferRequested) + if err := _ARMContract.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_ARMContract *ARMContractFilterer) ParseOwnershipTransferRequested(log types.Log) (*ARMContractOwnershipTransferRequested, error) { + event := new(ARMContractOwnershipTransferRequested) + if err := _ARMContract.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type ARMContractOwnershipTransferredIterator struct { + Event *ARMContractOwnershipTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *ARMContractOwnershipTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(ARMContractOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(ARMContractOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *ARMContractOwnershipTransferredIterator) Error() error { + return it.fail +} + +func (it *ARMContractOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type ARMContractOwnershipTransferred struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_ARMContract *ARMContractFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*ARMContractOwnershipTransferredIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _ARMContract.contract.FilterLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return &ARMContractOwnershipTransferredIterator{contract: _ARMContract.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +func (_ARMContract *ARMContractFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *ARMContractOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _ARMContract.contract.WatchLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(ARMContractOwnershipTransferred) + if err := _ARMContract.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_ARMContract *ARMContractFilterer) ParseOwnershipTransferred(log types.Log) (*ARMContractOwnershipTransferred, error) { + event := new(ARMContractOwnershipTransferred) + if err := _ARMContract.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type ARMContractPermaBlessedCommitStoreAddedIterator struct { + Event *ARMContractPermaBlessedCommitStoreAdded + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *ARMContractPermaBlessedCommitStoreAddedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(ARMContractPermaBlessedCommitStoreAdded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(ARMContractPermaBlessedCommitStoreAdded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *ARMContractPermaBlessedCommitStoreAddedIterator) Error() error { + return it.fail +} + +func (it *ARMContractPermaBlessedCommitStoreAddedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type ARMContractPermaBlessedCommitStoreAdded struct { + CommitStore common.Address + Raw types.Log +} + +func (_ARMContract *ARMContractFilterer) FilterPermaBlessedCommitStoreAdded(opts *bind.FilterOpts) (*ARMContractPermaBlessedCommitStoreAddedIterator, error) { + + logs, sub, err := _ARMContract.contract.FilterLogs(opts, "PermaBlessedCommitStoreAdded") + if err != nil { + return nil, err + } + return &ARMContractPermaBlessedCommitStoreAddedIterator{contract: _ARMContract.contract, event: "PermaBlessedCommitStoreAdded", logs: logs, sub: sub}, nil +} + +func (_ARMContract *ARMContractFilterer) WatchPermaBlessedCommitStoreAdded(opts *bind.WatchOpts, sink chan<- *ARMContractPermaBlessedCommitStoreAdded) (event.Subscription, error) { + + logs, sub, err := _ARMContract.contract.WatchLogs(opts, "PermaBlessedCommitStoreAdded") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(ARMContractPermaBlessedCommitStoreAdded) + if err := _ARMContract.contract.UnpackLog(event, "PermaBlessedCommitStoreAdded", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_ARMContract *ARMContractFilterer) ParsePermaBlessedCommitStoreAdded(log types.Log) (*ARMContractPermaBlessedCommitStoreAdded, error) { + event := new(ARMContractPermaBlessedCommitStoreAdded) + if err := _ARMContract.contract.UnpackLog(event, "PermaBlessedCommitStoreAdded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type ARMContractPermaBlessedCommitStoreRemovedIterator struct { + Event *ARMContractPermaBlessedCommitStoreRemoved + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *ARMContractPermaBlessedCommitStoreRemovedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(ARMContractPermaBlessedCommitStoreRemoved) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(ARMContractPermaBlessedCommitStoreRemoved) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *ARMContractPermaBlessedCommitStoreRemovedIterator) Error() error { + return it.fail +} + +func (it *ARMContractPermaBlessedCommitStoreRemovedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type ARMContractPermaBlessedCommitStoreRemoved struct { + CommitStore common.Address + Raw types.Log +} + +func (_ARMContract *ARMContractFilterer) FilterPermaBlessedCommitStoreRemoved(opts *bind.FilterOpts) (*ARMContractPermaBlessedCommitStoreRemovedIterator, error) { + + logs, sub, err := _ARMContract.contract.FilterLogs(opts, "PermaBlessedCommitStoreRemoved") + if err != nil { + return nil, err + } + return &ARMContractPermaBlessedCommitStoreRemovedIterator{contract: _ARMContract.contract, event: "PermaBlessedCommitStoreRemoved", logs: logs, sub: sub}, nil +} + +func (_ARMContract *ARMContractFilterer) WatchPermaBlessedCommitStoreRemoved(opts *bind.WatchOpts, sink chan<- *ARMContractPermaBlessedCommitStoreRemoved) (event.Subscription, error) { + + logs, sub, err := _ARMContract.contract.WatchLogs(opts, "PermaBlessedCommitStoreRemoved") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(ARMContractPermaBlessedCommitStoreRemoved) + if err := _ARMContract.contract.UnpackLog(event, "PermaBlessedCommitStoreRemoved", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_ARMContract *ARMContractFilterer) ParsePermaBlessedCommitStoreRemoved(log types.Log) (*ARMContractPermaBlessedCommitStoreRemoved, error) { + event := new(ARMContractPermaBlessedCommitStoreRemoved) + if err := _ARMContract.contract.UnpackLog(event, "PermaBlessedCommitStoreRemoved", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type ARMContractSkippedUnvoteToCurseIterator struct { + Event *ARMContractSkippedUnvoteToCurse + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *ARMContractSkippedUnvoteToCurseIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(ARMContractSkippedUnvoteToCurse) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(ARMContractSkippedUnvoteToCurse) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *ARMContractSkippedUnvoteToCurseIterator) Error() error { + return it.fail +} + +func (it *ARMContractSkippedUnvoteToCurseIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type ARMContractSkippedUnvoteToCurse struct { + Voter common.Address + Subject [16]byte + OnchainCursesHash [28]byte + CursesHash [28]byte + Raw types.Log +} + +func (_ARMContract *ARMContractFilterer) FilterSkippedUnvoteToCurse(opts *bind.FilterOpts, voter []common.Address) (*ARMContractSkippedUnvoteToCurseIterator, error) { + + var voterRule []interface{} + for _, voterItem := range voter { + voterRule = append(voterRule, voterItem) + } + + logs, sub, err := _ARMContract.contract.FilterLogs(opts, "SkippedUnvoteToCurse", voterRule) + if err != nil { + return nil, err + } + return &ARMContractSkippedUnvoteToCurseIterator{contract: _ARMContract.contract, event: "SkippedUnvoteToCurse", logs: logs, sub: sub}, nil +} + +func (_ARMContract *ARMContractFilterer) WatchSkippedUnvoteToCurse(opts *bind.WatchOpts, sink chan<- *ARMContractSkippedUnvoteToCurse, voter []common.Address) (event.Subscription, error) { + + var voterRule []interface{} + for _, voterItem := range voter { + voterRule = append(voterRule, voterItem) + } + + logs, sub, err := _ARMContract.contract.WatchLogs(opts, "SkippedUnvoteToCurse", voterRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(ARMContractSkippedUnvoteToCurse) + if err := _ARMContract.contract.UnpackLog(event, "SkippedUnvoteToCurse", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_ARMContract *ARMContractFilterer) ParseSkippedUnvoteToCurse(log types.Log) (*ARMContractSkippedUnvoteToCurse, error) { + event := new(ARMContractSkippedUnvoteToCurse) + if err := _ARMContract.contract.UnpackLog(event, "SkippedUnvoteToCurse", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type ARMContractTaggedRootBlessVotesResetIterator struct { + Event *ARMContractTaggedRootBlessVotesReset + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *ARMContractTaggedRootBlessVotesResetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(ARMContractTaggedRootBlessVotesReset) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(ARMContractTaggedRootBlessVotesReset) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *ARMContractTaggedRootBlessVotesResetIterator) Error() error { + return it.fail +} + +func (it *ARMContractTaggedRootBlessVotesResetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type ARMContractTaggedRootBlessVotesReset struct { + ConfigVersion uint32 + TaggedRoot IRMNTaggedRoot + WasBlessed bool + Raw types.Log +} + +func (_ARMContract *ARMContractFilterer) FilterTaggedRootBlessVotesReset(opts *bind.FilterOpts, configVersion []uint32) (*ARMContractTaggedRootBlessVotesResetIterator, error) { + + var configVersionRule []interface{} + for _, configVersionItem := range configVersion { + configVersionRule = append(configVersionRule, configVersionItem) + } + + logs, sub, err := _ARMContract.contract.FilterLogs(opts, "TaggedRootBlessVotesReset", configVersionRule) + if err != nil { + return nil, err + } + return &ARMContractTaggedRootBlessVotesResetIterator{contract: _ARMContract.contract, event: "TaggedRootBlessVotesReset", logs: logs, sub: sub}, nil +} + +func (_ARMContract *ARMContractFilterer) WatchTaggedRootBlessVotesReset(opts *bind.WatchOpts, sink chan<- *ARMContractTaggedRootBlessVotesReset, configVersion []uint32) (event.Subscription, error) { + + var configVersionRule []interface{} + for _, configVersionItem := range configVersion { + configVersionRule = append(configVersionRule, configVersionItem) + } + + logs, sub, err := _ARMContract.contract.WatchLogs(opts, "TaggedRootBlessVotesReset", configVersionRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(ARMContractTaggedRootBlessVotesReset) + if err := _ARMContract.contract.UnpackLog(event, "TaggedRootBlessVotesReset", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_ARMContract *ARMContractFilterer) ParseTaggedRootBlessVotesReset(log types.Log) (*ARMContractTaggedRootBlessVotesReset, error) { + event := new(ARMContractTaggedRootBlessVotesReset) + if err := _ARMContract.contract.UnpackLog(event, "TaggedRootBlessVotesReset", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type ARMContractTaggedRootBlessedIterator struct { + Event *ARMContractTaggedRootBlessed + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *ARMContractTaggedRootBlessedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(ARMContractTaggedRootBlessed) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(ARMContractTaggedRootBlessed) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *ARMContractTaggedRootBlessedIterator) Error() error { + return it.fail +} + +func (it *ARMContractTaggedRootBlessedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type ARMContractTaggedRootBlessed struct { + ConfigVersion uint32 + TaggedRoot IRMNTaggedRoot + AccumulatedWeight uint16 + Raw types.Log +} + +func (_ARMContract *ARMContractFilterer) FilterTaggedRootBlessed(opts *bind.FilterOpts, configVersion []uint32) (*ARMContractTaggedRootBlessedIterator, error) { + + var configVersionRule []interface{} + for _, configVersionItem := range configVersion { + configVersionRule = append(configVersionRule, configVersionItem) + } + + logs, sub, err := _ARMContract.contract.FilterLogs(opts, "TaggedRootBlessed", configVersionRule) + if err != nil { + return nil, err + } + return &ARMContractTaggedRootBlessedIterator{contract: _ARMContract.contract, event: "TaggedRootBlessed", logs: logs, sub: sub}, nil +} + +func (_ARMContract *ARMContractFilterer) WatchTaggedRootBlessed(opts *bind.WatchOpts, sink chan<- *ARMContractTaggedRootBlessed, configVersion []uint32) (event.Subscription, error) { + + var configVersionRule []interface{} + for _, configVersionItem := range configVersion { + configVersionRule = append(configVersionRule, configVersionItem) + } + + logs, sub, err := _ARMContract.contract.WatchLogs(opts, "TaggedRootBlessed", configVersionRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(ARMContractTaggedRootBlessed) + if err := _ARMContract.contract.UnpackLog(event, "TaggedRootBlessed", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_ARMContract *ARMContractFilterer) ParseTaggedRootBlessed(log types.Log) (*ARMContractTaggedRootBlessed, error) { + event := new(ARMContractTaggedRootBlessed) + if err := _ARMContract.contract.UnpackLog(event, "TaggedRootBlessed", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type ARMContractUnvotedToCurseIterator struct { + Event *ARMContractUnvotedToCurse + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *ARMContractUnvotedToCurseIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(ARMContractUnvotedToCurse) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(ARMContractUnvotedToCurse) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *ARMContractUnvotedToCurseIterator) Error() error { + return it.fail +} + +func (it *ARMContractUnvotedToCurseIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type ARMContractUnvotedToCurse struct { + ConfigVersion uint32 + Voter common.Address + Subject [16]byte + Weight uint8 + CursesHash [28]byte + RemainingAccumulatedWeight uint16 + Raw types.Log +} + +func (_ARMContract *ARMContractFilterer) FilterUnvotedToCurse(opts *bind.FilterOpts, configVersion []uint32, voter []common.Address) (*ARMContractUnvotedToCurseIterator, error) { + + var configVersionRule []interface{} + for _, configVersionItem := range configVersion { + configVersionRule = append(configVersionRule, configVersionItem) + } + var voterRule []interface{} + for _, voterItem := range voter { + voterRule = append(voterRule, voterItem) + } + + logs, sub, err := _ARMContract.contract.FilterLogs(opts, "UnvotedToCurse", configVersionRule, voterRule) + if err != nil { + return nil, err + } + return &ARMContractUnvotedToCurseIterator{contract: _ARMContract.contract, event: "UnvotedToCurse", logs: logs, sub: sub}, nil +} + +func (_ARMContract *ARMContractFilterer) WatchUnvotedToCurse(opts *bind.WatchOpts, sink chan<- *ARMContractUnvotedToCurse, configVersion []uint32, voter []common.Address) (event.Subscription, error) { + + var configVersionRule []interface{} + for _, configVersionItem := range configVersion { + configVersionRule = append(configVersionRule, configVersionItem) + } + var voterRule []interface{} + for _, voterItem := range voter { + voterRule = append(voterRule, voterItem) + } + + logs, sub, err := _ARMContract.contract.WatchLogs(opts, "UnvotedToCurse", configVersionRule, voterRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(ARMContractUnvotedToCurse) + if err := _ARMContract.contract.UnpackLog(event, "UnvotedToCurse", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_ARMContract *ARMContractFilterer) ParseUnvotedToCurse(log types.Log) (*ARMContractUnvotedToCurse, error) { + event := new(ARMContractUnvotedToCurse) + if err := _ARMContract.contract.UnpackLog(event, "UnvotedToCurse", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type ARMContractVotedToBlessIterator struct { + Event *ARMContractVotedToBless + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *ARMContractVotedToBlessIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(ARMContractVotedToBless) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(ARMContractVotedToBless) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *ARMContractVotedToBlessIterator) Error() error { + return it.fail +} + +func (it *ARMContractVotedToBlessIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type ARMContractVotedToBless struct { + ConfigVersion uint32 + Voter common.Address + TaggedRoot IRMNTaggedRoot + Weight uint8 + Raw types.Log +} + +func (_ARMContract *ARMContractFilterer) FilterVotedToBless(opts *bind.FilterOpts, configVersion []uint32, voter []common.Address) (*ARMContractVotedToBlessIterator, error) { + + var configVersionRule []interface{} + for _, configVersionItem := range configVersion { + configVersionRule = append(configVersionRule, configVersionItem) + } + var voterRule []interface{} + for _, voterItem := range voter { + voterRule = append(voterRule, voterItem) + } + + logs, sub, err := _ARMContract.contract.FilterLogs(opts, "VotedToBless", configVersionRule, voterRule) + if err != nil { + return nil, err + } + return &ARMContractVotedToBlessIterator{contract: _ARMContract.contract, event: "VotedToBless", logs: logs, sub: sub}, nil +} + +func (_ARMContract *ARMContractFilterer) WatchVotedToBless(opts *bind.WatchOpts, sink chan<- *ARMContractVotedToBless, configVersion []uint32, voter []common.Address) (event.Subscription, error) { + + var configVersionRule []interface{} + for _, configVersionItem := range configVersion { + configVersionRule = append(configVersionRule, configVersionItem) + } + var voterRule []interface{} + for _, voterItem := range voter { + voterRule = append(voterRule, voterItem) + } + + logs, sub, err := _ARMContract.contract.WatchLogs(opts, "VotedToBless", configVersionRule, voterRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(ARMContractVotedToBless) + if err := _ARMContract.contract.UnpackLog(event, "VotedToBless", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_ARMContract *ARMContractFilterer) ParseVotedToBless(log types.Log) (*ARMContractVotedToBless, error) { + event := new(ARMContractVotedToBless) + if err := _ARMContract.contract.UnpackLog(event, "VotedToBless", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type ARMContractVotedToCurseIterator struct { + Event *ARMContractVotedToCurse + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *ARMContractVotedToCurseIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(ARMContractVotedToCurse) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(ARMContractVotedToCurse) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *ARMContractVotedToCurseIterator) Error() error { + return it.fail +} + +func (it *ARMContractVotedToCurseIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type ARMContractVotedToCurse struct { + ConfigVersion uint32 + Voter common.Address + Subject [16]byte + CurseId [16]byte + Weight uint8 + BlockTimestamp uint64 + CursesHash [28]byte + AccumulatedWeight uint16 + Raw types.Log +} + +func (_ARMContract *ARMContractFilterer) FilterVotedToCurse(opts *bind.FilterOpts, configVersion []uint32, voter []common.Address) (*ARMContractVotedToCurseIterator, error) { + + var configVersionRule []interface{} + for _, configVersionItem := range configVersion { + configVersionRule = append(configVersionRule, configVersionItem) + } + var voterRule []interface{} + for _, voterItem := range voter { + voterRule = append(voterRule, voterItem) + } + + logs, sub, err := _ARMContract.contract.FilterLogs(opts, "VotedToCurse", configVersionRule, voterRule) + if err != nil { + return nil, err + } + return &ARMContractVotedToCurseIterator{contract: _ARMContract.contract, event: "VotedToCurse", logs: logs, sub: sub}, nil +} + +func (_ARMContract *ARMContractFilterer) WatchVotedToCurse(opts *bind.WatchOpts, sink chan<- *ARMContractVotedToCurse, configVersion []uint32, voter []common.Address) (event.Subscription, error) { + + var configVersionRule []interface{} + for _, configVersionItem := range configVersion { + configVersionRule = append(configVersionRule, configVersionItem) + } + var voterRule []interface{} + for _, voterItem := range voter { + voterRule = append(voterRule, voterItem) + } + + logs, sub, err := _ARMContract.contract.WatchLogs(opts, "VotedToCurse", configVersionRule, voterRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(ARMContractVotedToCurse) + if err := _ARMContract.contract.UnpackLog(event, "VotedToCurse", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_ARMContract *ARMContractFilterer) ParseVotedToCurse(log types.Log) (*ARMContractVotedToCurse, error) { + event := new(ARMContractVotedToCurse) + if err := _ARMContract.contract.UnpackLog(event, "VotedToCurse", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type GetBlessProgress struct { + BlessVoteAddrs []common.Address + AccumulatedWeight uint16 + Blessed bool +} +type GetConfigDetails struct { + Version uint32 + BlockNumber uint32 + Config RMNConfig +} +type GetCurseProgress struct { + CurseVoteAddrs []common.Address + CursesHashes [][28]byte + AccumulatedWeight uint16 + Cursed bool +} + +func (_ARMContract *ARMContract) ParseLog(log types.Log) (generated.AbigenLog, error) { + switch log.Topics[0] { + case _ARMContract.abi.Events["AlreadyBlessed"].ID: + return _ARMContract.ParseAlreadyBlessed(log) + case _ARMContract.abi.Events["AlreadyVotedToBless"].ID: + return _ARMContract.ParseAlreadyVotedToBless(log) + case _ARMContract.abi.Events["ConfigSet"].ID: + return _ARMContract.ParseConfigSet(log) + case _ARMContract.abi.Events["CurseLifted"].ID: + return _ARMContract.ParseCurseLifted(log) + case _ARMContract.abi.Events["Cursed"].ID: + return _ARMContract.ParseCursed(log) + case _ARMContract.abi.Events["OwnershipTransferRequested"].ID: + return _ARMContract.ParseOwnershipTransferRequested(log) + case _ARMContract.abi.Events["OwnershipTransferred"].ID: + return _ARMContract.ParseOwnershipTransferred(log) + case _ARMContract.abi.Events["PermaBlessedCommitStoreAdded"].ID: + return _ARMContract.ParsePermaBlessedCommitStoreAdded(log) + case _ARMContract.abi.Events["PermaBlessedCommitStoreRemoved"].ID: + return _ARMContract.ParsePermaBlessedCommitStoreRemoved(log) + case _ARMContract.abi.Events["SkippedUnvoteToCurse"].ID: + return _ARMContract.ParseSkippedUnvoteToCurse(log) + case _ARMContract.abi.Events["TaggedRootBlessVotesReset"].ID: + return _ARMContract.ParseTaggedRootBlessVotesReset(log) + case _ARMContract.abi.Events["TaggedRootBlessed"].ID: + return _ARMContract.ParseTaggedRootBlessed(log) + case _ARMContract.abi.Events["UnvotedToCurse"].ID: + return _ARMContract.ParseUnvotedToCurse(log) + case _ARMContract.abi.Events["VotedToBless"].ID: + return _ARMContract.ParseVotedToBless(log) + case _ARMContract.abi.Events["VotedToCurse"].ID: + return _ARMContract.ParseVotedToCurse(log) + + default: + return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) + } +} + +func (ARMContractAlreadyBlessed) Topic() common.Hash { + return common.HexToHash("0x274d6d5b916b0a53974b7ab86c844b97a2e03a60f658cd9a4b1c028b604d7bf1") +} + +func (ARMContractAlreadyVotedToBless) Topic() common.Hash { + return common.HexToHash("0x6dfbb745226fa630aeb1b9557d17d508ddb789a04f0cb873ec16e58beb8beead") +} + +func (ARMContractConfigSet) Topic() common.Hash { + return common.HexToHash("0x8c49fda8177c5c8c768eb39634bc6773695c7181711537b822451c12b2efd2a9") +} + +func (ARMContractCurseLifted) Topic() common.Hash { + return common.HexToHash("0x65d0e78c3625f0956f58610cf0fb157eaf627683258875ef29af2f71d25ac8fd") +} + +func (ARMContractCursed) Topic() common.Hash { + return common.HexToHash("0xcfdbfd8ce9a56b5f7c202c0e102184d24f47ca87121dc165063fc4c290957bde") +} + +func (ARMContractOwnershipTransferRequested) Topic() common.Hash { + return common.HexToHash("0xed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278") +} + +func (ARMContractOwnershipTransferred) Topic() common.Hash { + return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") +} + +func (ARMContractPermaBlessedCommitStoreAdded) Topic() common.Hash { + return common.HexToHash("0x66b4b4752c65ae8cd2f3a0a48c7dc8b2118c60d5ea15514992eb2ddf56c9cb15") +} + +func (ARMContractPermaBlessedCommitStoreRemoved) Topic() common.Hash { + return common.HexToHash("0xdca892154bbc36d0c05ccd01b3d0411875cb1b841fcdeebb384e5d0d6eb06b44") +} + +func (ARMContractSkippedUnvoteToCurse) Topic() common.Hash { + return common.HexToHash("0xbabb0d7099e6ca14a29fad2a2cfb4fda2bd30f97cb3c27e546174bfb4277c1cc") +} + +func (ARMContractTaggedRootBlessVotesReset) Topic() common.Hash { + return common.HexToHash("0x7d15a6eebaa019ea7d5b7d38937c51ebd3befbfdf51bb630a694fd28635bbcba") +} + +func (ARMContractTaggedRootBlessed) Topic() common.Hash { + return common.HexToHash("0x8257378aa73bf8e4ada848713526584a3dcee0fd3db3beed7397f7a7f5067cc9") +} + +func (ARMContractUnvotedToCurse) Topic() common.Hash { + return common.HexToHash("0xa96a155bd67c927a6c056befbd979b78465e2b2f1276bf7d4e90a31d4f430aa8") +} + +func (ARMContractVotedToBless) Topic() common.Hash { + return common.HexToHash("0x2a08a2bd2798f0aae9a843f0f4ad4de488c1b3d5f04049940cfed736ad69fb97") +} + +func (ARMContractVotedToCurse) Topic() common.Hash { + return common.HexToHash("0x8137bc8a8d712aaa27bfc6506d5566ac405618bd53f9831b8ca6b6fe5442ee7a") +} + +func (_ARMContract *ARMContract) Address() common.Address { + return _ARMContract.address +} + +type ARMContractInterface interface { + GetBlessProgress(opts *bind.CallOpts, taggedRoot IRMNTaggedRoot) (GetBlessProgress, + + error) + + GetConfigDetails(opts *bind.CallOpts) (GetConfigDetails, + + error) + + GetCurseProgress(opts *bind.CallOpts, subject [16]byte) (GetCurseProgress, + + error) + + GetCursedSubjectsCount(opts *bind.CallOpts) (*big.Int, error) + + GetPermaBlessedCommitStores(opts *bind.CallOpts) ([]common.Address, error) + + GetRecordedCurseRelatedOps(opts *bind.CallOpts, offset *big.Int, limit *big.Int) ([]RMNRecordedCurseRelatedOp, error) + + GetRecordedCurseRelatedOpsCount(opts *bind.CallOpts) (*big.Int, error) + + IsBlessed(opts *bind.CallOpts, taggedRoot IRMNTaggedRoot) (bool, error) + + IsCursed(opts *bind.CallOpts, subject [16]byte) (bool, error) + + IsCursed0(opts *bind.CallOpts) (bool, error) + + Owner(opts *bind.CallOpts) (common.Address, error) + + TypeAndVersion(opts *bind.CallOpts) (string, error) + + AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) + + OwnerCurse(opts *bind.TransactOpts, curseId [16]byte, subjects [][16]byte) (*types.Transaction, error) + + OwnerRemoveThenAddPermaBlessedCommitStores(opts *bind.TransactOpts, removes []common.Address, adds []common.Address) (*types.Transaction, error) + + OwnerResetBlessVotes(opts *bind.TransactOpts, taggedRoots []IRMNTaggedRoot) (*types.Transaction, error) + + OwnerUnvoteToCurse(opts *bind.TransactOpts, ownerUnvoteToCurseRequests []RMNOwnerUnvoteToCurseRequest) (*types.Transaction, error) + + SetConfig(opts *bind.TransactOpts, config RMNConfig) (*types.Transaction, error) + + TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + + UnvoteToCurse(opts *bind.TransactOpts, unvoteToCurseRequests []RMNUnvoteToCurseRequest) (*types.Transaction, error) + + VoteToBless(opts *bind.TransactOpts, taggedRoots []IRMNTaggedRoot) (*types.Transaction, error) + + VoteToCurse(opts *bind.TransactOpts, curseId [16]byte, subjects [][16]byte) (*types.Transaction, error) + + FilterAlreadyBlessed(opts *bind.FilterOpts, configVersion []uint32, voter []common.Address) (*ARMContractAlreadyBlessedIterator, error) + + WatchAlreadyBlessed(opts *bind.WatchOpts, sink chan<- *ARMContractAlreadyBlessed, configVersion []uint32, voter []common.Address) (event.Subscription, error) + + ParseAlreadyBlessed(log types.Log) (*ARMContractAlreadyBlessed, error) + + FilterAlreadyVotedToBless(opts *bind.FilterOpts, configVersion []uint32, voter []common.Address) (*ARMContractAlreadyVotedToBlessIterator, error) + + WatchAlreadyVotedToBless(opts *bind.WatchOpts, sink chan<- *ARMContractAlreadyVotedToBless, configVersion []uint32, voter []common.Address) (event.Subscription, error) + + ParseAlreadyVotedToBless(log types.Log) (*ARMContractAlreadyVotedToBless, error) + + FilterConfigSet(opts *bind.FilterOpts, configVersion []uint32) (*ARMContractConfigSetIterator, error) + + WatchConfigSet(opts *bind.WatchOpts, sink chan<- *ARMContractConfigSet, configVersion []uint32) (event.Subscription, error) + + ParseConfigSet(log types.Log) (*ARMContractConfigSet, error) + + FilterCurseLifted(opts *bind.FilterOpts) (*ARMContractCurseLiftedIterator, error) + + WatchCurseLifted(opts *bind.WatchOpts, sink chan<- *ARMContractCurseLifted) (event.Subscription, error) + + ParseCurseLifted(log types.Log) (*ARMContractCurseLifted, error) + + FilterCursed(opts *bind.FilterOpts, configVersion []uint32) (*ARMContractCursedIterator, error) + + WatchCursed(opts *bind.WatchOpts, sink chan<- *ARMContractCursed, configVersion []uint32) (event.Subscription, error) + + ParseCursed(log types.Log) (*ARMContractCursed, error) + + FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*ARMContractOwnershipTransferRequestedIterator, error) + + WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *ARMContractOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferRequested(log types.Log) (*ARMContractOwnershipTransferRequested, error) + + FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*ARMContractOwnershipTransferredIterator, error) + + WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *ARMContractOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferred(log types.Log) (*ARMContractOwnershipTransferred, error) + + FilterPermaBlessedCommitStoreAdded(opts *bind.FilterOpts) (*ARMContractPermaBlessedCommitStoreAddedIterator, error) + + WatchPermaBlessedCommitStoreAdded(opts *bind.WatchOpts, sink chan<- *ARMContractPermaBlessedCommitStoreAdded) (event.Subscription, error) + + ParsePermaBlessedCommitStoreAdded(log types.Log) (*ARMContractPermaBlessedCommitStoreAdded, error) + + FilterPermaBlessedCommitStoreRemoved(opts *bind.FilterOpts) (*ARMContractPermaBlessedCommitStoreRemovedIterator, error) + + WatchPermaBlessedCommitStoreRemoved(opts *bind.WatchOpts, sink chan<- *ARMContractPermaBlessedCommitStoreRemoved) (event.Subscription, error) + + ParsePermaBlessedCommitStoreRemoved(log types.Log) (*ARMContractPermaBlessedCommitStoreRemoved, error) + + FilterSkippedUnvoteToCurse(opts *bind.FilterOpts, voter []common.Address) (*ARMContractSkippedUnvoteToCurseIterator, error) + + WatchSkippedUnvoteToCurse(opts *bind.WatchOpts, sink chan<- *ARMContractSkippedUnvoteToCurse, voter []common.Address) (event.Subscription, error) + + ParseSkippedUnvoteToCurse(log types.Log) (*ARMContractSkippedUnvoteToCurse, error) + + FilterTaggedRootBlessVotesReset(opts *bind.FilterOpts, configVersion []uint32) (*ARMContractTaggedRootBlessVotesResetIterator, error) + + WatchTaggedRootBlessVotesReset(opts *bind.WatchOpts, sink chan<- *ARMContractTaggedRootBlessVotesReset, configVersion []uint32) (event.Subscription, error) + + ParseTaggedRootBlessVotesReset(log types.Log) (*ARMContractTaggedRootBlessVotesReset, error) + + FilterTaggedRootBlessed(opts *bind.FilterOpts, configVersion []uint32) (*ARMContractTaggedRootBlessedIterator, error) + + WatchTaggedRootBlessed(opts *bind.WatchOpts, sink chan<- *ARMContractTaggedRootBlessed, configVersion []uint32) (event.Subscription, error) + + ParseTaggedRootBlessed(log types.Log) (*ARMContractTaggedRootBlessed, error) + + FilterUnvotedToCurse(opts *bind.FilterOpts, configVersion []uint32, voter []common.Address) (*ARMContractUnvotedToCurseIterator, error) + + WatchUnvotedToCurse(opts *bind.WatchOpts, sink chan<- *ARMContractUnvotedToCurse, configVersion []uint32, voter []common.Address) (event.Subscription, error) + + ParseUnvotedToCurse(log types.Log) (*ARMContractUnvotedToCurse, error) + + FilterVotedToBless(opts *bind.FilterOpts, configVersion []uint32, voter []common.Address) (*ARMContractVotedToBlessIterator, error) + + WatchVotedToBless(opts *bind.WatchOpts, sink chan<- *ARMContractVotedToBless, configVersion []uint32, voter []common.Address) (event.Subscription, error) + + ParseVotedToBless(log types.Log) (*ARMContractVotedToBless, error) + + FilterVotedToCurse(opts *bind.FilterOpts, configVersion []uint32, voter []common.Address) (*ARMContractVotedToCurseIterator, error) + + WatchVotedToCurse(opts *bind.WatchOpts, sink chan<- *ARMContractVotedToCurse, configVersion []uint32, voter []common.Address) (event.Subscription, error) + + ParseVotedToCurse(log types.Log) (*ARMContractVotedToCurse, error) + + ParseLog(log types.Log) (generated.AbigenLog, error) + + Address() common.Address +} diff --git a/core/gethwrappers/ccip/generated/arm_proxy_contract/arm_proxy_contract.go b/core/gethwrappers/ccip/generated/arm_proxy_contract/arm_proxy_contract.go new file mode 100644 index 0000000000..e2ba924621 --- /dev/null +++ b/core/gethwrappers/ccip/generated/arm_proxy_contract/arm_proxy_contract.go @@ -0,0 +1,743 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package arm_proxy_contract + +import ( + "errors" + "fmt" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" +) + +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +var ARMProxyContractMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"arm\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"arm\",\"type\":\"address\"}],\"name\":\"ARMSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"stateMutability\":\"nonpayable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getARM\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"arm\",\"type\":\"address\"}],\"name\":\"setARM\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b5060405161084138038061084183398101604081905261002f91610255565b33806000816100855760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b03848116919091179091558116156100b5576100b5816100cd565b5050506100c78161017660201b60201c565b50610285565b336001600160a01b038216036101255760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161007c565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b61017e6101f9565b6001600160a01b0381166101a5576040516342bcdf7f60e11b815260040160405180910390fd5b600280546001600160a01b0319166001600160a01b0383169081179091556040519081527fef31f568d741a833c6a9dc85a6e1c65e06fa772740d5dc94d1da21827a4e0cab9060200160405180910390a150565b6000546001600160a01b031633146102535760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640161007c565b565b60006020828403121561026757600080fd5b81516001600160a01b038116811461027e57600080fd5b9392505050565b6105ad806102946000396000f3fe608060405234801561001057600080fd5b50600436106100725760003560e01c806379ba50971161005057806379ba5097146101615780638da5cb5b14610169578063f2fde38b1461018757610072565b8063181f5a77146100bb5780632e90aa211461010d578063458fec3b1461014c575b60025473ffffffffffffffffffffffffffffffffffffffff16803b61009657600080fd5b366000803760008036600080855af13d6000803e80156100b5573d6000f35b503d6000fd5b6100f76040518060400160405280600e81526020017f41524d50726f787920312e302e3000000000000000000000000000000000000081525081565b60405161010491906104f6565b60405180910390f35b60025473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610104565b61015f61015a366004610563565b61019a565b005b61015f610268565b60005473ffffffffffffffffffffffffffffffffffffffff16610127565b61015f610195366004610563565b61036a565b6101a261037e565b73ffffffffffffffffffffffffffffffffffffffff81166101ef576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527fef31f568d741a833c6a9dc85a6e1c65e06fa772740d5dc94d1da21827a4e0cab9060200160405180910390a150565b60015473ffffffffffffffffffffffffffffffffffffffff1633146102ee576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b61037261037e565b61037b81610401565b50565b60005473ffffffffffffffffffffffffffffffffffffffff1633146103ff576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016102e5565b565b3373ffffffffffffffffffffffffffffffffffffffff821603610480576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016102e5565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60006020808352835180602085015260005b8181101561052457858101830151858201604001528201610508565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b60006020828403121561057557600080fd5b813573ffffffffffffffffffffffffffffffffffffffff8116811461059957600080fd5b939250505056fea164736f6c6343000818000a", +} + +var ARMProxyContractABI = ARMProxyContractMetaData.ABI + +var ARMProxyContractBin = ARMProxyContractMetaData.Bin + +func DeployARMProxyContract(auth *bind.TransactOpts, backend bind.ContractBackend, arm common.Address) (common.Address, *types.Transaction, *ARMProxyContract, error) { + parsed, err := ARMProxyContractMetaData.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(ARMProxyContractBin), backend, arm) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &ARMProxyContract{address: address, abi: *parsed, ARMProxyContractCaller: ARMProxyContractCaller{contract: contract}, ARMProxyContractTransactor: ARMProxyContractTransactor{contract: contract}, ARMProxyContractFilterer: ARMProxyContractFilterer{contract: contract}}, nil +} + +type ARMProxyContract struct { + address common.Address + abi abi.ABI + ARMProxyContractCaller + ARMProxyContractTransactor + ARMProxyContractFilterer +} + +type ARMProxyContractCaller struct { + contract *bind.BoundContract +} + +type ARMProxyContractTransactor struct { + contract *bind.BoundContract +} + +type ARMProxyContractFilterer struct { + contract *bind.BoundContract +} + +type ARMProxyContractSession struct { + Contract *ARMProxyContract + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type ARMProxyContractCallerSession struct { + Contract *ARMProxyContractCaller + CallOpts bind.CallOpts +} + +type ARMProxyContractTransactorSession struct { + Contract *ARMProxyContractTransactor + TransactOpts bind.TransactOpts +} + +type ARMProxyContractRaw struct { + Contract *ARMProxyContract +} + +type ARMProxyContractCallerRaw struct { + Contract *ARMProxyContractCaller +} + +type ARMProxyContractTransactorRaw struct { + Contract *ARMProxyContractTransactor +} + +func NewARMProxyContract(address common.Address, backend bind.ContractBackend) (*ARMProxyContract, error) { + abi, err := abi.JSON(strings.NewReader(ARMProxyContractABI)) + if err != nil { + return nil, err + } + contract, err := bindARMProxyContract(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &ARMProxyContract{address: address, abi: abi, ARMProxyContractCaller: ARMProxyContractCaller{contract: contract}, ARMProxyContractTransactor: ARMProxyContractTransactor{contract: contract}, ARMProxyContractFilterer: ARMProxyContractFilterer{contract: contract}}, nil +} + +func NewARMProxyContractCaller(address common.Address, caller bind.ContractCaller) (*ARMProxyContractCaller, error) { + contract, err := bindARMProxyContract(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &ARMProxyContractCaller{contract: contract}, nil +} + +func NewARMProxyContractTransactor(address common.Address, transactor bind.ContractTransactor) (*ARMProxyContractTransactor, error) { + contract, err := bindARMProxyContract(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &ARMProxyContractTransactor{contract: contract}, nil +} + +func NewARMProxyContractFilterer(address common.Address, filterer bind.ContractFilterer) (*ARMProxyContractFilterer, error) { + contract, err := bindARMProxyContract(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &ARMProxyContractFilterer{contract: contract}, nil +} + +func bindARMProxyContract(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := ARMProxyContractMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_ARMProxyContract *ARMProxyContractRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _ARMProxyContract.Contract.ARMProxyContractCaller.contract.Call(opts, result, method, params...) +} + +func (_ARMProxyContract *ARMProxyContractRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ARMProxyContract.Contract.ARMProxyContractTransactor.contract.Transfer(opts) +} + +func (_ARMProxyContract *ARMProxyContractRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ARMProxyContract.Contract.ARMProxyContractTransactor.contract.Transact(opts, method, params...) +} + +func (_ARMProxyContract *ARMProxyContractCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _ARMProxyContract.Contract.contract.Call(opts, result, method, params...) +} + +func (_ARMProxyContract *ARMProxyContractTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ARMProxyContract.Contract.contract.Transfer(opts) +} + +func (_ARMProxyContract *ARMProxyContractTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ARMProxyContract.Contract.contract.Transact(opts, method, params...) +} + +func (_ARMProxyContract *ARMProxyContractCaller) GetARM(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _ARMProxyContract.contract.Call(opts, &out, "getARM") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_ARMProxyContract *ARMProxyContractSession) GetARM() (common.Address, error) { + return _ARMProxyContract.Contract.GetARM(&_ARMProxyContract.CallOpts) +} + +func (_ARMProxyContract *ARMProxyContractCallerSession) GetARM() (common.Address, error) { + return _ARMProxyContract.Contract.GetARM(&_ARMProxyContract.CallOpts) +} + +func (_ARMProxyContract *ARMProxyContractCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _ARMProxyContract.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_ARMProxyContract *ARMProxyContractSession) Owner() (common.Address, error) { + return _ARMProxyContract.Contract.Owner(&_ARMProxyContract.CallOpts) +} + +func (_ARMProxyContract *ARMProxyContractCallerSession) Owner() (common.Address, error) { + return _ARMProxyContract.Contract.Owner(&_ARMProxyContract.CallOpts) +} + +func (_ARMProxyContract *ARMProxyContractCaller) TypeAndVersion(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _ARMProxyContract.contract.Call(opts, &out, "typeAndVersion") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +func (_ARMProxyContract *ARMProxyContractSession) TypeAndVersion() (string, error) { + return _ARMProxyContract.Contract.TypeAndVersion(&_ARMProxyContract.CallOpts) +} + +func (_ARMProxyContract *ARMProxyContractCallerSession) TypeAndVersion() (string, error) { + return _ARMProxyContract.Contract.TypeAndVersion(&_ARMProxyContract.CallOpts) +} + +func (_ARMProxyContract *ARMProxyContractTransactor) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ARMProxyContract.contract.Transact(opts, "acceptOwnership") +} + +func (_ARMProxyContract *ARMProxyContractSession) AcceptOwnership() (*types.Transaction, error) { + return _ARMProxyContract.Contract.AcceptOwnership(&_ARMProxyContract.TransactOpts) +} + +func (_ARMProxyContract *ARMProxyContractTransactorSession) AcceptOwnership() (*types.Transaction, error) { + return _ARMProxyContract.Contract.AcceptOwnership(&_ARMProxyContract.TransactOpts) +} + +func (_ARMProxyContract *ARMProxyContractTransactor) SetARM(opts *bind.TransactOpts, arm common.Address) (*types.Transaction, error) { + return _ARMProxyContract.contract.Transact(opts, "setARM", arm) +} + +func (_ARMProxyContract *ARMProxyContractSession) SetARM(arm common.Address) (*types.Transaction, error) { + return _ARMProxyContract.Contract.SetARM(&_ARMProxyContract.TransactOpts, arm) +} + +func (_ARMProxyContract *ARMProxyContractTransactorSession) SetARM(arm common.Address) (*types.Transaction, error) { + return _ARMProxyContract.Contract.SetARM(&_ARMProxyContract.TransactOpts, arm) +} + +func (_ARMProxyContract *ARMProxyContractTransactor) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { + return _ARMProxyContract.contract.Transact(opts, "transferOwnership", to) +} + +func (_ARMProxyContract *ARMProxyContractSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _ARMProxyContract.Contract.TransferOwnership(&_ARMProxyContract.TransactOpts, to) +} + +func (_ARMProxyContract *ARMProxyContractTransactorSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _ARMProxyContract.Contract.TransferOwnership(&_ARMProxyContract.TransactOpts, to) +} + +func (_ARMProxyContract *ARMProxyContractTransactor) Fallback(opts *bind.TransactOpts, calldata []byte) (*types.Transaction, error) { + return _ARMProxyContract.contract.RawTransact(opts, calldata) +} + +func (_ARMProxyContract *ARMProxyContractSession) Fallback(calldata []byte) (*types.Transaction, error) { + return _ARMProxyContract.Contract.Fallback(&_ARMProxyContract.TransactOpts, calldata) +} + +func (_ARMProxyContract *ARMProxyContractTransactorSession) Fallback(calldata []byte) (*types.Transaction, error) { + return _ARMProxyContract.Contract.Fallback(&_ARMProxyContract.TransactOpts, calldata) +} + +type ARMProxyContractARMSetIterator struct { + Event *ARMProxyContractARMSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *ARMProxyContractARMSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(ARMProxyContractARMSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(ARMProxyContractARMSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *ARMProxyContractARMSetIterator) Error() error { + return it.fail +} + +func (it *ARMProxyContractARMSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type ARMProxyContractARMSet struct { + Arm common.Address + Raw types.Log +} + +func (_ARMProxyContract *ARMProxyContractFilterer) FilterARMSet(opts *bind.FilterOpts) (*ARMProxyContractARMSetIterator, error) { + + logs, sub, err := _ARMProxyContract.contract.FilterLogs(opts, "ARMSet") + if err != nil { + return nil, err + } + return &ARMProxyContractARMSetIterator{contract: _ARMProxyContract.contract, event: "ARMSet", logs: logs, sub: sub}, nil +} + +func (_ARMProxyContract *ARMProxyContractFilterer) WatchARMSet(opts *bind.WatchOpts, sink chan<- *ARMProxyContractARMSet) (event.Subscription, error) { + + logs, sub, err := _ARMProxyContract.contract.WatchLogs(opts, "ARMSet") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(ARMProxyContractARMSet) + if err := _ARMProxyContract.contract.UnpackLog(event, "ARMSet", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_ARMProxyContract *ARMProxyContractFilterer) ParseARMSet(log types.Log) (*ARMProxyContractARMSet, error) { + event := new(ARMProxyContractARMSet) + if err := _ARMProxyContract.contract.UnpackLog(event, "ARMSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type ARMProxyContractOwnershipTransferRequestedIterator struct { + Event *ARMProxyContractOwnershipTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *ARMProxyContractOwnershipTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(ARMProxyContractOwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(ARMProxyContractOwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *ARMProxyContractOwnershipTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *ARMProxyContractOwnershipTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type ARMProxyContractOwnershipTransferRequested struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_ARMProxyContract *ARMProxyContractFilterer) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*ARMProxyContractOwnershipTransferRequestedIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _ARMProxyContract.contract.FilterLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return &ARMProxyContractOwnershipTransferRequestedIterator{contract: _ARMProxyContract.contract, event: "OwnershipTransferRequested", logs: logs, sub: sub}, nil +} + +func (_ARMProxyContract *ARMProxyContractFilterer) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *ARMProxyContractOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _ARMProxyContract.contract.WatchLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(ARMProxyContractOwnershipTransferRequested) + if err := _ARMProxyContract.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_ARMProxyContract *ARMProxyContractFilterer) ParseOwnershipTransferRequested(log types.Log) (*ARMProxyContractOwnershipTransferRequested, error) { + event := new(ARMProxyContractOwnershipTransferRequested) + if err := _ARMProxyContract.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type ARMProxyContractOwnershipTransferredIterator struct { + Event *ARMProxyContractOwnershipTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *ARMProxyContractOwnershipTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(ARMProxyContractOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(ARMProxyContractOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *ARMProxyContractOwnershipTransferredIterator) Error() error { + return it.fail +} + +func (it *ARMProxyContractOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type ARMProxyContractOwnershipTransferred struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_ARMProxyContract *ARMProxyContractFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*ARMProxyContractOwnershipTransferredIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _ARMProxyContract.contract.FilterLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return &ARMProxyContractOwnershipTransferredIterator{contract: _ARMProxyContract.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +func (_ARMProxyContract *ARMProxyContractFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *ARMProxyContractOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _ARMProxyContract.contract.WatchLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(ARMProxyContractOwnershipTransferred) + if err := _ARMProxyContract.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_ARMProxyContract *ARMProxyContractFilterer) ParseOwnershipTransferred(log types.Log) (*ARMProxyContractOwnershipTransferred, error) { + event := new(ARMProxyContractOwnershipTransferred) + if err := _ARMProxyContract.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +func (_ARMProxyContract *ARMProxyContract) ParseLog(log types.Log) (generated.AbigenLog, error) { + switch log.Topics[0] { + case _ARMProxyContract.abi.Events["ARMSet"].ID: + return _ARMProxyContract.ParseARMSet(log) + case _ARMProxyContract.abi.Events["OwnershipTransferRequested"].ID: + return _ARMProxyContract.ParseOwnershipTransferRequested(log) + case _ARMProxyContract.abi.Events["OwnershipTransferred"].ID: + return _ARMProxyContract.ParseOwnershipTransferred(log) + + default: + return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) + } +} + +func (ARMProxyContractARMSet) Topic() common.Hash { + return common.HexToHash("0xef31f568d741a833c6a9dc85a6e1c65e06fa772740d5dc94d1da21827a4e0cab") +} + +func (ARMProxyContractOwnershipTransferRequested) Topic() common.Hash { + return common.HexToHash("0xed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278") +} + +func (ARMProxyContractOwnershipTransferred) Topic() common.Hash { + return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") +} + +func (_ARMProxyContract *ARMProxyContract) Address() common.Address { + return _ARMProxyContract.address +} + +type ARMProxyContractInterface interface { + GetARM(opts *bind.CallOpts) (common.Address, error) + + Owner(opts *bind.CallOpts) (common.Address, error) + + TypeAndVersion(opts *bind.CallOpts) (string, error) + + AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) + + SetARM(opts *bind.TransactOpts, arm common.Address) (*types.Transaction, error) + + TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + + Fallback(opts *bind.TransactOpts, calldata []byte) (*types.Transaction, error) + + FilterARMSet(opts *bind.FilterOpts) (*ARMProxyContractARMSetIterator, error) + + WatchARMSet(opts *bind.WatchOpts, sink chan<- *ARMProxyContractARMSet) (event.Subscription, error) + + ParseARMSet(log types.Log) (*ARMProxyContractARMSet, error) + + FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*ARMProxyContractOwnershipTransferRequestedIterator, error) + + WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *ARMProxyContractOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferRequested(log types.Log) (*ARMProxyContractOwnershipTransferRequested, error) + + FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*ARMProxyContractOwnershipTransferredIterator, error) + + WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *ARMProxyContractOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferred(log types.Log) (*ARMProxyContractOwnershipTransferred, error) + + ParseLog(log types.Log) (generated.AbigenLog, error) + + Address() common.Address +} diff --git a/core/gethwrappers/ccip/generated/evm_2_evm_multi_offramp/evm_2_evm_multi_offramp.go b/core/gethwrappers/ccip/generated/evm_2_evm_multi_offramp/evm_2_evm_multi_offramp.go new file mode 100644 index 0000000000..2b7571d55e --- /dev/null +++ b/core/gethwrappers/ccip/generated/evm_2_evm_multi_offramp/evm_2_evm_multi_offramp.go @@ -0,0 +1,2498 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package evm_2_evm_multi_offramp + +import ( + "errors" + "fmt" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" +) + +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +type ClientAny2EVMMessage struct { + MessageId [32]byte + SourceChainSelector uint64 + Sender []byte + Data []byte + DestTokenAmounts []ClientEVMTokenAmount +} + +type ClientEVMTokenAmount struct { + Token common.Address + Amount *big.Int +} + +type EVM2EVMMultiOffRampCommitReport struct { + PriceUpdates InternalPriceUpdates + MerkleRoots []EVM2EVMMultiOffRampMerkleRoot +} + +type EVM2EVMMultiOffRampDynamicConfig struct { + PriceRegistry common.Address + PermissionLessExecutionThresholdSeconds uint32 + MaxTokenTransferGas uint32 + MaxPoolReleaseOrMintGas uint32 + MessageValidator common.Address +} + +type EVM2EVMMultiOffRampInterval struct { + Min uint64 + Max uint64 +} + +type EVM2EVMMultiOffRampMerkleRoot struct { + SourceChainSelector uint64 + Interval EVM2EVMMultiOffRampInterval + MerkleRoot [32]byte +} + +type EVM2EVMMultiOffRampSourceChainConfig struct { + Router common.Address + IsEnabled bool + MinSeqNr uint64 + OnRamp []byte +} + +type EVM2EVMMultiOffRampSourceChainConfigArgs struct { + Router common.Address + SourceChainSelector uint64 + IsEnabled bool + OnRamp []byte +} + +type EVM2EVMMultiOffRampStaticConfig struct { + ChainSelector uint64 + RmnProxy common.Address + TokenAdminRegistry common.Address + NonceManager common.Address +} + +type EVM2EVMMultiOffRampUnblessedRoot struct { + SourceChainSelector uint64 + MerkleRoot [32]byte +} + +type InternalAny2EVMRampMessage struct { + Header InternalRampMessageHeader + Sender []byte + Data []byte + Receiver common.Address + GasLimit *big.Int + TokenAmounts []InternalRampTokenAmount +} + +type InternalExecutionReportSingleChain struct { + SourceChainSelector uint64 + Messages []InternalAny2EVMRampMessage + OffchainTokenData [][][]byte + Proofs [][32]byte + ProofFlagBits *big.Int +} + +type InternalGasPriceUpdate struct { + DestChainSelector uint64 + UsdPerUnitGas *big.Int +} + +type InternalPriceUpdates struct { + TokenPriceUpdates []InternalTokenPriceUpdate + GasPriceUpdates []InternalGasPriceUpdate +} + +type InternalRampMessageHeader struct { + MessageId [32]byte + SourceChainSelector uint64 + DestChainSelector uint64 + SequenceNumber uint64 + Nonce uint64 +} + +type InternalRampTokenAmount struct { + SourcePoolAddress []byte + DestTokenAddress []byte + ExtraData []byte + Amount *big.Int +} + +type InternalTokenPriceUpdate struct { + SourceToken common.Address + UsdPerToken *big.Int +} + +type MultiOCR3BaseConfigInfo struct { + ConfigDigest [32]byte + F uint8 + N uint8 + IsSignatureVerificationEnabled bool +} + +type MultiOCR3BaseOCRConfig struct { + ConfigInfo MultiOCR3BaseConfigInfo + Signers []common.Address + Transmitters []common.Address +} + +type MultiOCR3BaseOCRConfigArgs struct { + ConfigDigest [32]byte + OcrPluginType uint8 + F uint8 + IsSignatureVerificationEnabled bool + Signers []common.Address + Transmitters []common.Address +} + +var EVM2EVMMultiOffRampMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.StaticConfig\",\"name\":\"staticConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxTokenTransferGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPoolReleaseOrMintGas\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"contractIRouter\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.SourceChainConfigArgs[]\",\"name\":\"sourceChainConfigs\",\"type\":\"tuple[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"CanOnlySelfCall\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"expected\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"actual\",\"type\":\"bytes32\"}],\"name\":\"ConfigDigestMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"CursedByRMN\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EmptyReport\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"err\",\"type\":\"bytes\"}],\"name\":\"ExecutionError\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actual\",\"type\":\"uint256\"}],\"name\":\"ForkedChain\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"enumMultiOCR3Base.InvalidConfigErrorType\",\"name\":\"errorType\",\"type\":\"uint8\"}],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"got\",\"type\":\"uint256\"}],\"name\":\"InvalidDataLength\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"encodedAddress\",\"type\":\"bytes\"}],\"name\":\"InvalidEVMAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"min\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"max\",\"type\":\"uint64\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.Interval\",\"name\":\"interval\",\"type\":\"tuple\"}],\"name\":\"InvalidInterval\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"newLimit\",\"type\":\"uint256\"}],\"name\":\"InvalidManualExecutionGasLimit\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"messageDestChainSelector\",\"type\":\"uint64\"}],\"name\":\"InvalidMessageDestChainSelector\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"enumInternal.MessageExecutionState\",\"name\":\"newState\",\"type\":\"uint8\"}],\"name\":\"InvalidNewState\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidProof\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRoot\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"InvalidStaticConfig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LeavesCannotBeEmpty\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ManualExecutionGasLimitMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"ManualExecutionNotYetEnabled\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"errorReason\",\"type\":\"bytes\"}],\"name\":\"MessageValidationError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NonUniqueSignatures\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"notPool\",\"type\":\"address\"}],\"name\":\"NotACompatiblePool\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OracleCannotBeZeroAddress\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"err\",\"type\":\"bytes\"}],\"name\":\"ReceiverError\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amountReleased\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"balancePre\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"balancePost\",\"type\":\"uint256\"}],\"name\":\"ReleaseOrMintBalanceMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"merkleRoot\",\"type\":\"bytes32\"}],\"name\":\"RootAlreadyCommitted\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"RootNotCommitted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SignaturesOutOfRegistration\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"SourceChainNotEnabled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"StaleCommitReport\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"}],\"name\":\"StaticConfigCannotBeChanged\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"TokenDataMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"err\",\"type\":\"bytes\"}],\"name\":\"TokenHandlingError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedTransmitter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnexpectedTokenData\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actual\",\"type\":\"uint256\"}],\"name\":\"WrongMessageLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"WrongNumberOfSignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroChainSelectorNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"AlreadyAttempted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"components\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sourceToken\",\"type\":\"address\"},{\"internalType\":\"uint224\",\"name\":\"usdPerToken\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.TokenPriceUpdate[]\",\"name\":\"tokenPriceUpdates\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint224\",\"name\":\"usdPerUnitGas\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.GasPriceUpdate[]\",\"name\":\"gasPriceUpdates\",\"type\":\"tuple[]\"}],\"internalType\":\"structInternal.PriceUpdates\",\"name\":\"priceUpdates\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"min\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"max\",\"type\":\"uint64\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.Interval\",\"name\":\"interval\",\"type\":\"tuple\"},{\"internalType\":\"bytes32\",\"name\":\"merkleRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.MerkleRoot[]\",\"name\":\"merkleRoots\",\"type\":\"tuple[]\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOffRamp.CommitReport\",\"name\":\"report\",\"type\":\"tuple\"}],\"name\":\"CommitReportAccepted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"F\",\"type\":\"uint8\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxTokenTransferGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPoolReleaseOrMintGas\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOffRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"DynamicConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"enumInternal.MessageExecutionState\",\"name\":\"state\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"name\":\"ExecutionStateChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"name\":\"RootRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"SkippedAlreadyExecutedMessage\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"contractIRouter\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOffRamp.SourceChainConfig\",\"name\":\"sourceConfig\",\"type\":\"tuple\"}],\"name\":\"SourceChainConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"SourceChainSelectorAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOffRamp.StaticConfig\",\"name\":\"staticConfig\",\"type\":\"tuple\"}],\"name\":\"StaticConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"Transmitted\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"contractIRouter\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.SourceChainConfigArgs[]\",\"name\":\"sourceChainConfigUpdates\",\"type\":\"tuple[]\"}],\"name\":\"applySourceChainConfigUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"destTokenAmounts\",\"type\":\"tuple[]\"}],\"internalType\":\"structClient.Any2EVMMessage\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"ccipReceive\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"report\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"rs\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"ss\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"rawVs\",\"type\":\"bytes32\"}],\"name\":\"commit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"report\",\"type\":\"bytes\"}],\"name\":\"execute\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"internalType\":\"structInternal.RampMessageHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"destTokenAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structInternal.RampTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"}],\"internalType\":\"structInternal.Any2EVMRampMessage\",\"name\":\"message\",\"type\":\"tuple\"},{\"internalType\":\"bytes[]\",\"name\":\"offchainTokenData\",\"type\":\"bytes[]\"}],\"name\":\"executeSingleMessage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDynamicConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxTokenTransferGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPoolReleaseOrMintGas\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.DynamicConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"}],\"name\":\"getExecutionState\",\"outputs\":[{\"internalType\":\"enumInternal.MessageExecutionState\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLatestPriceSequenceNumber\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"name\":\"getMerkleRoot\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"getSourceChainConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"contractIRouter\",\"name\":\"router\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"minSeqNr\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"onRamp\",\"type\":\"bytes\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.SourceChainConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStaticConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.StaticConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"name\":\"isBlessed\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"}],\"name\":\"latestConfigDetails\",\"outputs\":[{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"F\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"n\",\"type\":\"uint8\"},{\"internalType\":\"bool\",\"name\":\"isSignatureVerificationEnabled\",\"type\":\"bool\"}],\"internalType\":\"structMultiOCR3Base.ConfigInfo\",\"name\":\"configInfo\",\"type\":\"tuple\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"}],\"internalType\":\"structMultiOCR3Base.OCRConfig\",\"name\":\"ocrConfig\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"internalType\":\"structInternal.RampMessageHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"sender\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"destTokenAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structInternal.RampTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"}],\"internalType\":\"structInternal.Any2EVMRampMessage[]\",\"name\":\"messages\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes[][]\",\"name\":\"offchainTokenData\",\"type\":\"bytes[][]\"},{\"internalType\":\"bytes32[]\",\"name\":\"proofs\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256\",\"name\":\"proofFlagBits\",\"type\":\"uint256\"}],\"internalType\":\"structInternal.ExecutionReportSingleChain[]\",\"name\":\"reports\",\"type\":\"tuple[]\"},{\"internalType\":\"uint256[][]\",\"name\":\"gasLimitOverrides\",\"type\":\"uint256[][]\"}],\"name\":\"manuallyExecute\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"merkleRoot\",\"type\":\"bytes32\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.UnblessedRoot[]\",\"name\":\"rootToReset\",\"type\":\"tuple[]\"}],\"name\":\"resetUnblessedRoots\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"permissionLessExecutionThresholdSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxTokenTransferGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPoolReleaseOrMintGas\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOffRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"setDynamicConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint8\",\"name\":\"ocrPluginType\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"F\",\"type\":\"uint8\"},{\"internalType\":\"bool\",\"name\":\"isSignatureVerificationEnabled\",\"type\":\"bool\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"}],\"internalType\":\"structMultiOCR3Base.OCRConfigArgs[]\",\"name\":\"ocrConfigArgs\",\"type\":\"tuple[]\"}],\"name\":\"setOCR3Configs\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x6101206040523480156200001257600080fd5b5060405162006bbb38038062006bbb8339810160408190526200003591620008c7565b33806000816200008c5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000bf57620000bf81620001fa565b5050466080525060208301516001600160a01b03161580620000ec575060408301516001600160a01b0316155b8062000103575060608301516001600160a01b0316155b1562000122576040516342bcdf7f60e11b815260040160405180910390fd5b82516001600160401b03166000036200014e5760405163c656089560e01b815260040160405180910390fd5b82516001600160401b0390811660a052602080850180516001600160a01b0390811660c05260408088018051831660e0526060808a01805185166101005283518b519098168852945184169587019590955251821690850152905116908201527f683eb52ee924eb817377cfa8f41f238f4bb7a877da5267869dfffbad85f564d89060800160405180910390a1620001e682620002a5565b620001f181620003c1565b50505062000c67565b336001600160a01b03821603620002545760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000083565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b80516001600160a01b0316620002ce576040516342bcdf7f60e11b815260040160405180910390fd5b80516004805460208085018051604080880180516060808b0180516001600160a01b039b8c166001600160c01b0319909a168a17600160a01b63ffffffff98891602176001600160c01b0316600160c01b948816949094026001600160e01b031693909317600160e01b93871693909302929092179098556080808b018051600580546001600160a01b031916918d169190911790558451988952955185169688019690965290518316918601919091525116938301939093529151909216908201527fa55bd56595c45f517e5967a3067f3dca684445a3080e7c04a4e0d5a40cda627d9060a00160405180910390a150565b60005b815181101562000666576000828281518110620003e557620003e562000a1d565b60200260200101519050600081602001519050806001600160401b0316600003620004235760405163c656089560e01b815260040160405180910390fd5b81516001600160a01b03166200044c576040516342bcdf7f60e11b815260040160405180910390fd5b6001600160401b0381166000908152600660205260408120600181018054919291620004789062000a33565b80601f0160208091040260200160405190810160405280929190818152602001828054620004a69062000a33565b8015620004f75780601f10620004cb57610100808354040283529160200191620004f7565b820191906000526020600020905b815481529060010190602001808311620004d957829003601f168201915b5050505050905060008460600151905081516000036200059e57805160000362000534576040516342bcdf7f60e11b815260040160405180910390fd5b6001830162000544828262000ac4565b508254600160a81b600160e81b031916600160a81b1783556040516001600160401b03851681527ff4c1390c70e5c0f491ae1ccbc06f9117cbbadf2767b247b3bc203280f24c0fb99060200160405180910390a1620005d9565b8080519060200120828051906020012014620005d95760405163c39a620560e01b81526001600160401b038516600482015260240162000083565b604080860151845487516001600160a01b03166001600160a01b0319921515600160a01b02929092166001600160a81b031990911617178455516001600160401b038516907f49f51971edd25182e97182d6ea372a0488ce2ab639f6a3a7ab4df0d2636fe56b906200064d90869062000b90565b60405180910390a25050505050806001019050620003c4565b5050565b634e487b7160e01b600052604160045260246000fd5b604051608081016001600160401b0381118282101715620006a557620006a56200066a565b60405290565b604051601f8201601f191681016001600160401b0381118282101715620006d657620006d66200066a565b604052919050565b80516001600160401b0381168114620006f657600080fd5b919050565b6001600160a01b03811681146200071157600080fd5b50565b805163ffffffff81168114620006f657600080fd5b6000601f83601f8401126200073d57600080fd5b825160206001600160401b03808311156200075c576200075c6200066a565b8260051b6200076d838201620006ab565b93845286810183019383810190898611156200078857600080fd5b84890192505b85831015620008ba57825184811115620007a85760008081fd5b89016080601f19828d038101821315620007c25760008081fd5b620007cc62000680565b88840151620007db81620006fb565b81526040620007ec858201620006de565b8a8301526060808601518015158114620008065760008081fd5b838301529385015193898511156200081e5760008081fd5b84860195508f603f8701126200083657600094508485fd5b8a8601519450898511156200084f576200084f6200066a565b620008608b858f88011601620006ab565b93508484528f82868801011115620008785760008081fd5b60005b8581101562000898578681018301518582018d01528b016200087b565b5060009484018b0194909452509182015283525091840191908401906200078e565b9998505050505050505050565b6000806000838503610140811215620008df57600080fd5b6080811215620008ee57600080fd5b620008f862000680565b6200090386620006de565b815260208601516200091581620006fb565b602082015260408601516200092a81620006fb565b604082015260608601516200093f81620006fb565b6060820152935060a0607f19820112156200095957600080fd5b5060405160a081016001600160401b0380821183831017156200098057620009806200066a565b81604052608087015191506200099682620006fb565b818352620009a760a0880162000714565b6020840152620009ba60c0880162000714565b6040840152620009cd60e0880162000714565b60608401526101008701519150620009e582620006fb565b608083018290526101208701519294508083111562000a0357600080fd5b505062000a138682870162000729565b9150509250925092565b634e487b7160e01b600052603260045260246000fd5b600181811c9082168062000a4857607f821691505b60208210810362000a6957634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111562000abf576000816000526020600020601f850160051c8101602086101562000a9a5750805b601f850160051c820191505b8181101562000abb5782815560010162000aa6565b5050505b505050565b81516001600160401b0381111562000ae05762000ae06200066a565b62000af88162000af1845462000a33565b8462000a6f565b602080601f83116001811462000b30576000841562000b175750858301515b600019600386901b1c1916600185901b17855562000abb565b600085815260208120601f198616915b8281101562000b615788860151825594840194600190910190840162000b40565b508582101562000b805787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b602080825282546001600160a01b0381168383015260a081901c60ff161515604084015260a81c6001600160401b0316606083015260808083015260018084018054600093929190849062000be58162000a33565b8060a089015260c0600183166000811462000c09576001811462000c265762000c58565b60ff19841660c08b015260c083151560051b8b0101945062000c58565b85600052602060002060005b8481101562000c4f5781548c820185015290880190890162000c32565b8b0160c0019550505b50929998505050505050505050565b60805160a05160c05160e05161010051615ee562000cd6600039600081816102530152612c0c0152600081816102240152612ee60152600081816101f50152818161142f01526118400152600081816101c50152612801015260008181611dce0152611e1a0152615ee56000f3fe608060405234801561001057600080fd5b50600436106101775760003560e01c806385572ffb116100d8578063d2a15d351161008c578063f2fde38b11610066578063f2fde38b14610583578063f716f99f14610596578063ff888fb1146105a957600080fd5b8063d2a15d351461053d578063e9d68a8e14610550578063ece670b61461057057600080fd5b8063991a5018116100bd578063991a5018146104c5578063c673e584146104d8578063ccd37ba3146104f857600080fd5b806385572ffb1461049c5780638da5cb5b146104aa57600080fd5b80633f4b04aa1161012f5780637437ff9f116101145780637437ff9f1461038557806379ba5097146104815780637d4eef601461048957600080fd5b80633f4b04aa146103495780635e36480c1461036557600080fd5b8063181f5a7711610160578063181f5a77146102da5780632d04ab7614610323578063311cd5131461033657600080fd5b806304666f9c1461017c57806306285c6914610191575b600080fd5b61018f61018a3660046140a1565b6105cc565b005b61028360408051608081018252600080825260208201819052918101829052606081019190915260405180608001604052807f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316815250905090565b6040516102d19190815167ffffffffffffffff1681526020808301516001600160a01b0390811691830191909152604080840151821690830152606092830151169181019190915260800190565b60405180910390f35b6103166040518060400160405280601d81526020017f45564d3245564d4d756c74694f666652616d7020312e362e302d64657600000081525081565b6040516102d19190614210565b61018f6103313660046142bb565b6105e0565b61018f61034436600461436e565b6109c0565b60095460405167ffffffffffffffff90911681526020016102d1565b6103786103733660046143c2565b610a29565b6040516102d1919061441f565b6104246040805160a081018252600080825260208201819052918101829052606081018290526080810191909152506040805160a0810182526004546001600160a01b03808216835263ffffffff600160a01b83048116602085015278010000000000000000000000000000000000000000000000008304811694840194909452600160e01b9091049092166060820152600554909116608082015290565b6040516102d19190600060a0820190506001600160a01b03808451168352602084015163ffffffff808216602086015280604087015116604086015280606087015116606086015250508060808501511660808401525092915050565b61018f610a7f565b61018f610497366004614974565b610b3d565b61018f610177366004614a9f565b6000546040516001600160a01b0390911681526020016102d1565b61018f6104d3366004614aee565b610cdd565b6104eb6104e6366004614b73565b610cee565b6040516102d19190614bd3565b61052f610506366004614c48565b67ffffffffffffffff919091166000908152600860209081526040808320938352929052205490565b6040519081526020016102d1565b61018f61054b366004614c72565b610e4c565b61056361055e366004614ce7565b610f06565b6040516102d19190614d02565b61018f61057e366004614d50565b611013565b61018f610591366004614dac565b611386565b61018f6105a4366004614e31565b611397565b6105bc6105b7366004614f6f565b6113d9565b60405190151581526020016102d1565b6105d461149a565b6105dd816114f6565b50565b60006105ee878901896150f8565b8051515190915015158061060757508051602001515115155b156107075760095460208a01359067ffffffffffffffff808316911610156106c6576009805467ffffffffffffffff191667ffffffffffffffff83161790556004805483516040517f3937306f0000000000000000000000000000000000000000000000000000000081526001600160a01b0390921692633937306f9261068f929101615336565b600060405180830381600087803b1580156106a957600080fd5b505af11580156106bd573d6000803e3d6000fd5b50505050610705565b816020015151600003610705576040517f2261116700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b60005b8160200151518110156109095760008260200151828151811061072f5761072f615263565b6020026020010151905060008160000151905061074b816117f4565b6000610756826118f6565b602084015151815491925067ffffffffffffffff908116600160a81b9092041614158061079a575060208084015190810151905167ffffffffffffffff9182169116115b156107e357825160208401516040517feefb0cac0000000000000000000000000000000000000000000000000000000081526107da929190600401615349565b60405180910390fd5b60408301518061081f576040517f504570e300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b835167ffffffffffffffff166000908152600860209081526040808320848452909152902054156108925783516040517f32cf0cbf00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff9091166004820152602481018290526044016107da565b60208085015101516108a5906001615394565b82547fffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffff16600160a81b67ffffffffffffffff92831602179092559251166000908152600860209081526040808320948352939052919091204290555060010161070a565b507f3a3950e13dd607cc37980db0ef14266c40d2bba9c01b2e44bfe549808883095d8160405161093991906153bc565b60405180910390a16109b560008a8a8a8a8a8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808e0282810182019093528d82529093508d92508c9182918501908490808284376000920191909152508b925061195d915050565b505050505050505050565b610a006109cf82840184615459565b60408051600080825260208201909252906109fa565b60608152602001906001900390816109e55790505b50611cd4565b604080516000808252602082019092529050610a2360018585858586600061195d565b50505050565b6000610a376001600461548e565b6002610a446080856154b7565b67ffffffffffffffff16610a5891906154de565b610a628585611d84565b901c166003811115610a7657610a766143f5565b90505b92915050565b6001546001600160a01b03163314610ad95760405162461bcd60e51b815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016107da565b600080543373ffffffffffffffffffffffffffffffffffffffff19808316821784556001805490911690556040516001600160a01b0390921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610b45611dcb565b815181518114610b81576040517f83e3f56400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015610ccd576000848281518110610ba057610ba0615263565b60200260200101519050600081602001515190506000858481518110610bc857610bc8615263565b6020026020010151905080518214610c0c576040517f83e3f56400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b82811015610cbe576000828281518110610c2b57610c2b615263565b6020026020010151905080600014610cb55784602001518281518110610c5357610c53615263565b602002602001015160800151811015610cb55784516040517fc8e9605100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff909116600482015260248101839052604481018290526064016107da565b50600101610c0f565b50505050806001019050610b84565b50610cd88383611cd4565b505050565b610ce561149a565b6105dd81611e4c565b610d316040805160e081019091526000606082018181526080830182905260a0830182905260c08301919091528190815260200160608152602001606081525090565b60ff808316600090815260026020818152604092839020835160e081018552815460608201908152600183015480881660808401526101008104881660a0840152620100009004909616151560c082015294855291820180548451818402810184019095528085529293858301939092830182828015610dda57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610dbc575b5050505050815260200160038201805480602002602001604051908101604052809291908181526020018280548015610e3c57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610e1e575b5050505050815250509050919050565b610e5461149a565b60005b81811015610cd8576000838383818110610e7357610e73615263565b905060400201803603810190610e8991906154f5565b9050610e9881602001516113d9565b610efd57805167ffffffffffffffff1660009081526008602090815260408083208285018051855290835281842093909355915191519182527f202f1139a3e334b6056064c0e9b19fd07e44a88d8f6e5ded571b24cf8c371f12910160405180910390a15b50600101610e57565b604080516080808201835260008083526020808401829052838501829052606080850181905267ffffffffffffffff878116845260068352928690208651948501875280546001600160a01b0381168652600160a01b810460ff16151593860193909352600160a81b909204909216948301949094526001840180549394929391840191610f939061552e565b80601f0160208091040260200160405190810160405280929190818152602001828054610fbf9061552e565b8015610e3c5780601f10610fe157610100808354040283529160200191610e3c565b820191906000526020600020905b815481529060010190602001808311610fef57505050919092525091949350505050565b33301461104c576040517f371a732800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160008082526020820190925281611089565b60408051808201909152600080825260208201528152602001906001900390816110625790505b5060a085015151909150156110bd576110ba8460a00151856020015186606001518760000151602001518787611fb2565b90505b6040805160a0810182528551518152855160209081015167ffffffffffffffff16818301528087015183516000948401926110f9929101614210565b60408051601f19818403018152918152908252878101516020830152018390526005549091506001600160a01b03168015611206576040517f08d450a10000000000000000000000000000000000000000000000000000000081526001600160a01b038216906308d450a19061117390859060040161560a565b600060405180830381600087803b15801561118d57600080fd5b505af192505050801561119e575060015b611206573d8080156111cc576040519150601f19603f3d011682016040523d82523d6000602084013e6111d1565b606091505b50806040517f09c253250000000000000000000000000000000000000000000000000000000081526004016107da9190614210565b60408601515115801561121b57506080860151155b80611232575060608601516001600160a01b03163b155b8061127257506060860151611270906001600160a01b03167f85572ffb000000000000000000000000000000000000000000000000000000006120d1565b155b1561127f57505050505050565b855160209081015167ffffffffffffffff1660009081526006909152604080822054608089015160608a015192517f3cf9798300000000000000000000000000000000000000000000000000000000815284936001600160a01b0390931692633cf97983926112f7928992611388929160040161561d565b6000604051808303816000875af1158015611316573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261133e9190810190615659565b50915091508161137c57806040517f0a8d6e8c0000000000000000000000000000000000000000000000000000000081526004016107da9190614210565b5050505050505050565b61138e61149a565b6105dd816120ed565b61139f61149a565b60005b81518110156113d5576113cd8282815181106113c0576113c0615263565b60200260200101516121a3565b6001016113a2565b5050565b6040805180820182523081526020810183815291517f4d61677100000000000000000000000000000000000000000000000000000000815290516001600160a01b039081166004830152915160248201526000917f00000000000000000000000000000000000000000000000000000000000000001690634d61677190604401602060405180830381865afa158015611476573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a7991906156ef565b6000546001600160a01b031633146114f45760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016107da565b565b60005b81518110156113d557600082828151811061151657611516615263565b602002602001015190506000816020015190508067ffffffffffffffff1660000361156d576040517fc656089500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81516001600160a01b0316611595576040516342bcdf7f60e11b815260040160405180910390fd5b67ffffffffffffffff811660009081526006602052604081206001810180549192916115c09061552e565b80601f01602080910402602001604051908101604052809291908181526020018280546115ec9061552e565b80156116395780601f1061160e57610100808354040283529160200191611639565b820191906000526020600020905b81548152906001019060200180831161161c57829003601f168201915b5050505050905060008460600151905081516000036116f1578051600003611674576040516342bcdf7f60e11b815260040160405180910390fd5b600183016116828282615754565b5082547fffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffff16600160a81b17835560405167ffffffffffffffff851681527ff4c1390c70e5c0f491ae1ccbc06f9117cbbadf2767b247b3bc203280f24c0fb99060200160405180910390a1611744565b8080519060200120828051906020012014611744576040517fc39a620500000000000000000000000000000000000000000000000000000000815267ffffffffffffffff851660048201526024016107da565b604080860151845487516001600160a01b031673ffffffffffffffffffffffffffffffffffffffff19921515600160a01b02929092167fffffffffffffffffffffff000000000000000000000000000000000000000000909116171784555167ffffffffffffffff8516907f49f51971edd25182e97182d6ea372a0488ce2ab639f6a3a7ab4df0d2636fe56b906117dc908690615814565b60405180910390a250505050508060010190506114f9565b6040517f2cbc26bb000000000000000000000000000000000000000000000000000000008152608082901b77ffffffffffffffff000000000000000000000000000000001660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632cbc26bb90602401602060405180830381865afa15801561188f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118b391906156ef565b156105dd576040517ffdbd6a7200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff821660048201526024016107da565b67ffffffffffffffff811660009081526006602052604081208054600160a01b900460ff16610a79576040517fed053c5900000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024016107da565b60ff878116600090815260026020908152604080832081516080810183528154815260019091015480861693820193909352610100830485169181019190915262010000909104909216151560608301528735906119bc8760a46158e2565b9050826060015115611a045784516119d59060206154de565b86516119e29060206154de565b6119ed9060a06158e2565b6119f791906158e2565b611a0190826158e2565b90505b368114611a46576040517f8e1192e1000000000000000000000000000000000000000000000000000000008152600481018290523660248201526044016107da565b5081518114611a8e5781516040517f93df584c0000000000000000000000000000000000000000000000000000000081526004810191909152602481018290526044016107da565b611a96611dcb565b60ff808a1660009081526003602090815260408083203384528252808320815180830190925280548086168352939491939092840191610100909104166002811115611ae457611ae46143f5565b6002811115611af557611af56143f5565b9052509050600281602001516002811115611b1257611b126143f5565b148015611b665750600260008b60ff1660ff168152602001908152602001600020600301816000015160ff1681548110611b4e57611b4e615263565b6000918252602090912001546001600160a01b031633145b611b9c576040517fda0f08e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50816060015115611c7e576020820151611bb79060016158f5565b60ff16855114611bf3576040517f71253a2500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8351855114611c2e576040517fa75d88af00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008787604051611c4092919061590e565b604051908190038120611c57918b9060200161591e565b604051602081830303815290604052805190602001209050611c7c8a828888886124e7565b505b6040805182815260208a81013567ffffffffffffffff169082015260ff8b16917f198d6990ef96613a9026203077e422916918b03ff47f0be6bee7b02d8e139ef0910160405180910390a2505050505050505050565b8151600003611d0e576040517ebf199700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805160408051600080825260208201909252911591905b8451811015611d7d57611d75858281518110611d4357611d43615263565b602002602001015184611d6f57858381518110611d6257611d62615263565b60200260200101516126f4565b836126f4565b600101611d25565b5050505050565b67ffffffffffffffff8216600090815260076020526040812081611da9608085615932565b67ffffffffffffffff1681526020810191909152604001600020549392505050565b467f0000000000000000000000000000000000000000000000000000000000000000146114f4576040517f0f01ce850000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000060048201524660248201526044016107da565b80516001600160a01b0316611e74576040516342bcdf7f60e11b815260040160405180910390fd5b80516004805460208085018051604080880180516060808b0180516001600160a01b039b8c167fffffffffffffffff000000000000000000000000000000000000000000000000909a168a17600160a01b63ffffffff988916021777ffffffffffffffffffffffffffffffffffffffffffffffff167801000000000000000000000000000000000000000000000000948816949094026001600160e01b031693909317600160e01b93871693909302929092179098556080808b0180516005805473ffffffffffffffffffffffffffffffffffffffff1916918d169190911790558451988952955185169688019690965290518316918601919091525116938301939093529151909216908201527fa55bd56595c45f517e5967a3067f3dca684445a3080e7c04a4e0d5a40cda627d9060a00160405180910390a150565b6060865167ffffffffffffffff811115611fce57611fce613eb8565b60405190808252806020026020018201604052801561201357816020015b6040805180820190915260008082526020820152815260200190600190039081611fec5790505b50905060005b87518110156120c5576120a088828151811061203757612037615263565b602002602001015188888888888781811061205457612054615263565b90506020028101906120669190615959565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612e8592505050565b8282815181106120b2576120b2615263565b6020908102919091010152600101612019565b505b9695505050505050565b60006120dc8361322a565b8015610a765750610a76838361328e565b336001600160a01b038216036121455760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016107da565b6001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b806040015160ff166000036121ce576000604051631b3fab5160e11b81526004016107da91906159be565b60208082015160ff8082166000908152600290935260408320600181015492939092839216900361223b57606084015160018201805491151562010000027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff909216919091179055612290565b6060840151600182015460ff6201000090910416151590151514612290576040517f87f6037c00000000000000000000000000000000000000000000000000000000815260ff841660048201526024016107da565b60a08401518051601f60ff821611156122bf576001604051631b3fab5160e11b81526004016107da91906159be565b612325858560030180548060200260200160405190810160405280929190818152602001828054801561231b57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116122fd575b5050505050613349565b85606001511561245457612393858560020180548060200260200160405190810160405280929190818152602001828054801561231b576020028201919060005260206000209081546001600160a01b031681526001909101906020018083116122fd575050505050613349565b608086015180516123ad9060028701906020840190613e12565b5080516001850180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010060ff841690810291909117909155601f101561240d576002604051631b3fab5160e11b81526004016107da91906159be565b604088015161241d9060036159d8565b60ff168160ff1611612445576003604051631b3fab5160e11b81526004016107da91906159be565b612451878360016133b2565b50505b612460858360026133b2565b81516124759060038601906020850190613e12565b5060408681015160018501805460ff191660ff8316179055875180865560a089015192517fab8b1b57514019638d7b5ce9c638fe71366fe8e2be1c40a7a80f1733d0e9f547936124ce938a939260028b019291906159f4565b60405180910390a16124df85613532565b505050505050565b6124ef613e84565b835160005b8181101561137c57600060018886846020811061251357612513615263565b61252091901a601b6158f5565b89858151811061253257612532615263565b602002602001015189868151811061254c5761254c615263565b60200260200101516040516000815260200160405260405161258a949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa1580156125ac573d6000803e3d6000fd5b505060408051601f1981015160ff808e166000908152600360209081528582206001600160a01b0385168352815285822085870190965285548084168652939750909550929392840191610100900416600281111561260d5761260d6143f5565b600281111561261e5761261e6143f5565b905250905060018160200151600281111561263b5761263b6143f5565b14612672576040517fca31867a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8051859060ff16601f811061268957612689615263565b6020020151156126c5576040517ff67bc7c400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600185826000015160ff16601f81106126e0576126e0615263565b9115156020909202015250506001016124f4565b81516126ff816117f4565b600061270a826118f6565b602085015151909150600081900361274d576040517ebf199700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b846040015151811461278b576040517f57e0e08300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008167ffffffffffffffff8111156127a6576127a6613eb8565b6040519080825280602002602001820160405280156127cf578160200160208202803683370190505b50905060005b82811015612944576000876020015182815181106127f5576127f5615263565b602002602001015190507f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1681600001516040015167ffffffffffffffff161461288857805160409081015190517f38432a2200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201526024016107da565b61291e8186600101805461289b9061552e565b80601f01602080910402602001604051908101604052809291908181526020018280546128c79061552e565b80156129145780601f106128e957610100808354040283529160200191612914565b820191906000526020600020905b8154815290600101906020018083116128f757829003601f168201915b505050505061354e565b83838151811061293057612930615263565b6020908102919091010152506001016127d5565b50600061295b858389606001518a60800151613670565b9050806000036129a3576040517f7dd17a7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff861660048201526024016107da565b8551151560005b848110156109b5576000896020015182815181106129ca576129ca615263565b6020026020010151905060006129e889836000015160600151610a29565b905060008160038111156129fe576129fe6143f5565b1480612a1b57506003816003811115612a1957612a196143f5565b145b612a72578151606001516040805167ffffffffffffffff808d16825290921660208301527f3b575419319662b2a6f5e2467d84521517a3382b908eb3d557bb3fdb0c50e23c91015b60405180910390a15050612e7d565b8315612b4257600454600090600160a01b900463ffffffff16612a95874261548e565b1190508080612ab557506003826003811115612ab357612ab36143f5565b145b612af7576040517fa9cfc86200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8b1660048201526024016107da565b8a8481518110612b0957612b09615263565b6020026020010151600014612b3c578a8481518110612b2a57612b2a615263565b60200260200101518360800181815250505b50612ba3565b6000816003811115612b5657612b566143f5565b14612ba3578151606001516040805167ffffffffffffffff808d16825290921660208301527f3ef2a99c550a751d4b0b261268f05a803dfb049ab43616a1ffb388f61fe651209101612a63565b81516080015167ffffffffffffffff1615612c91576000816003811115612bcc57612bcc6143f5565b03612c915781516080015160208301516040517fe0e03cae0000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169263e0e03cae92612c43928e929190600401615aa0565b6020604051808303816000875af1158015612c62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c8691906156ef565b612c91575050612e7d565b60008b604001518481518110612ca957612ca9615263565b6020026020010151905080518360a001515114612d0d578251606001516040517f1cfe6d8b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff808d16600483015290911660248201526044016107da565b612d218a84600001516060015160016136c6565b600080612d2e858461376e565b91509150612d458c866000015160600151846136c6565b8615612db5576003826003811115612d5f57612d5f6143f5565b03612db5576000846003811115612d7857612d786143f5565b14612db5578451516040517f2b11b8d90000000000000000000000000000000000000000000000000000000081526107da91908390600401615acd565b6002826003811115612dc957612dc96143f5565b14612e23576003826003811115612de257612de26143f5565b14612e23578451606001516040517f926c5a3e0000000000000000000000000000000000000000000000000000000081526107da918e918590600401615ae6565b8451805160609091015160405167ffffffffffffffff918216918f16907f8c324ce1367b83031769f6a813e3bb4c117aba2185789d66b98b791405be6df290612e6f9087908790615b0c565b60405180910390a450505050505b6001016129aa565b60408051808201909152600080825260208201526000612ea88760200151613838565b6040517fbbe4f6db0000000000000000000000000000000000000000000000000000000081526001600160a01b0380831660048301529192506000917f0000000000000000000000000000000000000000000000000000000000000000169063bbe4f6db90602401602060405180830381865afa158015612f2d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f519190615b2c565b90506001600160a01b0381161580612f995750612f976001600160a01b0382167faff2afbf000000000000000000000000000000000000000000000000000000006120d1565b155b15612fdb576040517fae9b4ce90000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024016107da565b6004546000908190612ffd9089908690600160e01b900463ffffffff166138de565b9150915060008060006130ca6040518061010001604052808e81526020018c67ffffffffffffffff1681526020018d6001600160a01b031681526020018f606001518152602001896001600160a01b031681526020018f6000015181526020018f6040015181526020018b81525060405160240161307b9190615b49565b60408051601f198184030181529190526020810180516001600160e01b03167f390775370000000000000000000000000000000000000000000000000000000017905287866113886084613a0c565b9250925092508261310957816040517fe1cd55090000000000000000000000000000000000000000000000000000000081526004016107da9190614210565b81516020146131515781516040517f78ef80240000000000000000000000000000000000000000000000000000000081526020600482015260248101919091526044016107da565b6000828060200190518101906131679190615c16565b9050866001600160a01b03168c6001600160a01b0316146131fc5760006131988d8a613193868a61548e565b6138de565b509050868110806131b25750816131af888361548e565b14155b156131fa576040517fa966e21f0000000000000000000000000000000000000000000000000000000081526004810183905260248101889052604481018290526064016107da565b505b604080518082019091526001600160a01b039098168852602088015250949550505050505095945050505050565b6000613256827f01ffc9a70000000000000000000000000000000000000000000000000000000061328e565b8015610a795750613287827fffffffff0000000000000000000000000000000000000000000000000000000061328e565b1592915050565b6040517fffffffff0000000000000000000000000000000000000000000000000000000082166024820152600090819060440160408051601f19818403018152919052602080820180516001600160e01b03167f01ffc9a700000000000000000000000000000000000000000000000000000000178152825192935060009283928392909183918a617530fa92503d91506000519050828015613332575060208210155b801561333e5750600081115b979650505050505050565b60005b8151811015610cd85760ff83166000908152600360205260408120835190919084908490811061337e5761337e615263565b6020908102919091018101516001600160a01b03168252810191909152604001600020805461ffff1916905560010161334c565b60005b82518160ff161015610a23576000838260ff16815181106133d8576133d8615263565b60200260200101519050600060028111156133f5576133f56143f5565b60ff80871660009081526003602090815260408083206001600160a01b03871684529091529020546101009004166002811115613434576134346143f5565b14613455576004604051631b3fab5160e11b81526004016107da91906159be565b6001600160a01b038116613495576040517fd6c62c9b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405180604001604052808360ff1681526020018460028111156134bb576134bb6143f5565b905260ff80871660009081526003602090815260408083206001600160a01b0387168452825290912083518154931660ff198416811782559184015190929091839161ffff191617610100836002811115613518576135186143f5565b0217905550905050508061352b90615c2f565b90506133b5565b60ff81166105dd576009805467ffffffffffffffff1916905550565b815160208082015160409283015192516000938493613594937f2425b0b9f9054c76ff151b0a175b18f37a4a4e82013a72e9f15c9caa095ed21f93909291889101615c4e565b60408051601f1981840301815290829052805160209182012086518051888401516060808b0151908401516080808d015195015195976135dd9794969395929491939101615c81565b604051602081830303815290604052805190602001208560400151805190602001208660a001516040516020016136149190615d78565b60408051601f198184030181528282528051602091820120908301969096528101939093526060830191909152608082015260a081019190915260c0015b60405160208183030381529060405280519060200120905092915050565b60008061367e858585613b32565b9050613689816113d9565b6136975760009150506136be565b67ffffffffffffffff86166000908152600860209081526040808320938352929052205490505b949350505050565b600060026136d56080856154b7565b67ffffffffffffffff166136e991906154de565b905060006136f78585611d84565b9050816137066001600461548e565b901b19168183600381111561371d5761371d6143f5565b67ffffffffffffffff871660009081526007602052604081209190921b9290921791829161374c608088615932565b67ffffffffffffffff1681526020810191909152604001600020555050505050565b6040517fece670b6000000000000000000000000000000000000000000000000000000008152600090606090309063ece670b6906137b29087908790600401615dd8565b600060405180830381600087803b1580156137cc57600080fd5b505af19250505080156137dd575060015b61381c573d80801561380b576040519150601f19603f3d011682016040523d82523d6000602084013e613810565b606091505b50600392509050613831565b50506040805160208101909152600081526002905b9250929050565b6000815160201461387757816040517f8d666f600000000000000000000000000000000000000000000000000000000081526004016107da9190614210565b60008280602001905181019061388d9190615c16565b90506001600160a01b038111806138a5575061040081105b15610a7957826040517f8d666f600000000000000000000000000000000000000000000000000000000081526004016107da9190614210565b60008060008060006139588860405160240161390991906001600160a01b0391909116815260200190565b60408051601f198184030181529190526020810180516001600160e01b03167f70a082310000000000000000000000000000000000000000000000000000000017905288886113886084613a0c565b9250925092508261399757816040517fe1cd55090000000000000000000000000000000000000000000000000000000081526004016107da9190614210565b60208251146139df5781516040517f78ef80240000000000000000000000000000000000000000000000000000000081526020600482015260248101919091526044016107da565b818060200190518101906139f39190615c16565b6139fd828861548e565b94509450505050935093915050565b6000606060008361ffff1667ffffffffffffffff811115613a2f57613a2f613eb8565b6040519080825280601f01601f191660200182016040528015613a59576020820181803683370190505b509150863b613a8c577f0c3b563c0000000000000000000000000000000000000000000000000000000060005260046000fd5b5a85811015613abf577fafa32a2c0000000000000000000000000000000000000000000000000000000060005260046000fd5b8590036040810481038710613af8577f37c3be290000000000000000000000000000000000000000000000000000000060005260046000fd5b505a6000808a5160208c0160008c8cf193505a900390503d84811115613b1b5750835b808352806000602085013e50955095509592505050565b8251825160009190818303613b73576040517f11a6b26400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6101018211801590613b8757506101018111155b613ba4576040516309bde33960e01b815260040160405180910390fd5b60001982820101610100811115613bce576040516309bde33960e01b815260040160405180910390fd5b80600003613bfb5786600081518110613be957613be9615263565b60200260200101519350505050613dca565b60008167ffffffffffffffff811115613c1657613c16613eb8565b604051908082528060200260200182016040528015613c3f578160200160208202803683370190505b50905060008080805b85811015613d695760006001821b8b811603613ca35788851015613c8c578c5160018601958e918110613c7d57613c7d615263565b60200260200101519050613cc5565b8551600185019487918110613c7d57613c7d615263565b8b5160018401938d918110613cba57613cba615263565b602002602001015190505b600089861015613cf5578d5160018701968f918110613ce657613ce6615263565b60200260200101519050613d17565b8651600186019588918110613d0c57613d0c615263565b602002602001015190505b82851115613d38576040516309bde33960e01b815260040160405180910390fd5b613d428282613dd1565b878481518110613d5457613d54615263565b60209081029190910101525050600101613c48565b506001850382148015613d7b57508683145b8015613d8657508581145b613da3576040516309bde33960e01b815260040160405180910390fd5b836001860381518110613db857613db8615263565b60200260200101519750505050505050505b9392505050565b6000818310613de957613de48284613def565b610a76565b610a7683835b604080516001602082015290810183905260608101829052600090608001613652565b828054828255906000526020600020908101928215613e74579160200282015b82811115613e74578251825473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03909116178255602090920191600190910190613e32565b50613e80929150613ea3565b5090565b604051806103e00160405280601f906020820280368337509192915050565b5b80821115613e805760008155600101613ea4565b634e487b7160e01b600052604160045260246000fd5b6040516080810167ffffffffffffffff81118282101715613ef157613ef1613eb8565b60405290565b60405160a0810167ffffffffffffffff81118282101715613ef157613ef1613eb8565b60405160c0810167ffffffffffffffff81118282101715613ef157613ef1613eb8565b6040805190810167ffffffffffffffff81118282101715613ef157613ef1613eb8565b6040516060810167ffffffffffffffff81118282101715613ef157613ef1613eb8565b604051601f8201601f1916810167ffffffffffffffff81118282101715613fac57613fac613eb8565b604052919050565b600067ffffffffffffffff821115613fce57613fce613eb8565b5060051b60200190565b6001600160a01b03811681146105dd57600080fd5b803567ffffffffffffffff8116811461400557600080fd5b919050565b80151581146105dd57600080fd5b80356140058161400a565b600067ffffffffffffffff82111561403d5761403d613eb8565b50601f01601f191660200190565b600082601f83011261405c57600080fd5b813561406f61406a82614023565b613f83565b81815284602083860101111561408457600080fd5b816020850160208301376000918101602001919091529392505050565b600060208083850312156140b457600080fd5b823567ffffffffffffffff808211156140cc57600080fd5b818501915085601f8301126140e057600080fd5b81356140ee61406a82613fb4565b81815260059190911b8301840190848101908883111561410d57600080fd5b8585015b838110156141b3578035858111156141295760008081fd5b86016080818c03601f19018113156141415760008081fd5b614149613ece565b8983013561415681613fd8565b81526040614165848201613fed565b8b8301526060808501356141788161400a565b8383015292840135928984111561419157600091508182fd5b61419f8f8d8688010161404b565b908301525085525050918601918601614111565b5098975050505050505050565b60005b838110156141db5781810151838201526020016141c3565b50506000910152565b600081518084526141fc8160208601602086016141c0565b601f01601f19169290920160200192915050565b602081526000610a7660208301846141e4565b8060608101831015610a7957600080fd5b60008083601f84011261424657600080fd5b50813567ffffffffffffffff81111561425e57600080fd5b60208301915083602082850101111561383157600080fd5b60008083601f84011261428857600080fd5b50813567ffffffffffffffff8111156142a057600080fd5b6020830191508360208260051b850101111561383157600080fd5b60008060008060008060008060e0898b0312156142d757600080fd5b6142e18a8a614223565b9750606089013567ffffffffffffffff808211156142fe57600080fd5b61430a8c838d01614234565b909950975060808b013591508082111561432357600080fd5b61432f8c838d01614276565b909750955060a08b013591508082111561434857600080fd5b506143558b828c01614276565b999c989b50969995989497949560c00135949350505050565b60008060006080848603121561438357600080fd5b61438d8585614223565b9250606084013567ffffffffffffffff8111156143a957600080fd5b6143b586828701614234565b9497909650939450505050565b600080604083850312156143d557600080fd5b6143de83613fed565b91506143ec60208401613fed565b90509250929050565b634e487b7160e01b600052602160045260246000fd5b6004811061441b5761441b6143f5565b9052565b60208101610a79828461440b565b600060a0828403121561443f57600080fd5b614447613ef7565b90508135815261445960208301613fed565b602082015261446a60408301613fed565b604082015261447b60608301613fed565b606082015261448c60808301613fed565b608082015292915050565b803561400581613fd8565b600082601f8301126144b357600080fd5b813560206144c361406a83613fb4565b82815260059290921b840181019181810190868411156144e257600080fd5b8286015b848110156120c557803567ffffffffffffffff808211156145075760008081fd5b8189019150608080601f19848d030112156145225760008081fd5b61452a613ece565b878401358381111561453c5760008081fd5b61454a8d8a8388010161404b565b825250604080850135848111156145615760008081fd5b61456f8e8b8389010161404b565b8a84015250606080860135858111156145885760008081fd5b6145968f8c838a010161404b565b92840192909252949092013593810193909352505083529183019183016144e6565b600061014082840312156145cb57600080fd5b6145d3613f1a565b90506145df838361442d565b815260a082013567ffffffffffffffff808211156145fc57600080fd5b6146088583860161404b565b602084015260c084013591508082111561462157600080fd5b61462d8583860161404b565b604084015261463e60e08501614497565b6060840152610100840135608084015261012084013591508082111561466357600080fd5b50614670848285016144a2565b60a08301525092915050565b600082601f83011261468d57600080fd5b8135602061469d61406a83613fb4565b82815260059290921b840181019181810190868411156146bc57600080fd5b8286015b848110156120c557803567ffffffffffffffff8111156146e05760008081fd5b6146ee8986838b01016145b8565b8452509183019183016146c0565b600082601f83011261470d57600080fd5b8135602061471d61406a83613fb4565b82815260059290921b8401810191818101908684111561473c57600080fd5b8286015b848110156120c557803567ffffffffffffffff8082111561476057600080fd5b818901915089603f83011261477457600080fd5b8582013561478461406a82613fb4565b81815260059190911b830160400190878101908c8311156147a457600080fd5b604085015b838110156147dd578035858111156147c057600080fd5b6147cf8f6040838a010161404b565b8452509189019189016147a9565b50875250505092840192508301614740565b600082601f83011261480057600080fd5b8135602061481061406a83613fb4565b8083825260208201915060208460051b87010193508684111561483257600080fd5b602086015b848110156120c55780358352918301918301614837565b600082601f83011261485f57600080fd5b8135602061486f61406a83613fb4565b82815260059290921b8401810191818101908684111561488e57600080fd5b8286015b848110156120c557803567ffffffffffffffff808211156148b35760008081fd5b818901915060a080601f19848d030112156148ce5760008081fd5b6148d6613ef7565b6148e1888501613fed565b8152604080850135848111156148f75760008081fd5b6149058e8b8389010161467c565b8a840152506060808601358581111561491e5760008081fd5b61492c8f8c838a01016146fc565b83850152506080915081860135858111156149475760008081fd5b6149558f8c838a01016147ef565b9184019190915250919093013590830152508352918301918301614892565b600080604080848603121561498857600080fd5b833567ffffffffffffffff808211156149a057600080fd5b6149ac8783880161484e565b94506020915081860135818111156149c357600080fd5b8601601f810188136149d457600080fd5b80356149e261406a82613fb4565b81815260059190911b8201840190848101908a831115614a0157600080fd5b8584015b83811015614a8d57803586811115614a1d5760008081fd5b8501603f81018d13614a2f5760008081fd5b87810135614a3f61406a82613fb4565b81815260059190911b82018a0190898101908f831115614a5f5760008081fd5b928b01925b82841015614a7d5783358252928a0192908a0190614a64565b8652505050918601918601614a05565b50809750505050505050509250929050565b600060208284031215614ab157600080fd5b813567ffffffffffffffff811115614ac857600080fd5b820160a08185031215613dca57600080fd5b803563ffffffff8116811461400557600080fd5b600060a08284031215614b0057600080fd5b614b08613ef7565b8235614b1381613fd8565b8152614b2160208401614ada565b6020820152614b3260408401614ada565b6040820152614b4360608401614ada565b60608201526080830135614b5681613fd8565b60808201529392505050565b803560ff8116811461400557600080fd5b600060208284031215614b8557600080fd5b610a7682614b62565b60008151808452602080850194506020840160005b83811015614bc85781516001600160a01b031687529582019590820190600101614ba3565b509495945050505050565b60208152600082518051602084015260ff602082015116604084015260ff604082015116606084015260608101511515608084015250602083015160c060a0840152614c2260e0840182614b8e565b90506040840151601f198483030160c0850152614c3f8282614b8e565b95945050505050565b60008060408385031215614c5b57600080fd5b614c6483613fed565b946020939093013593505050565b60008060208385031215614c8557600080fd5b823567ffffffffffffffff80821115614c9d57600080fd5b818501915085601f830112614cb157600080fd5b813581811115614cc057600080fd5b8660208260061b8501011115614cd557600080fd5b60209290920196919550909350505050565b600060208284031215614cf957600080fd5b610a7682613fed565b602081526001600160a01b03825116602082015260208201511515604082015267ffffffffffffffff6040830151166060820152600060608301516080808401526136be60a08401826141e4565b600080600060408486031215614d6557600080fd5b833567ffffffffffffffff80821115614d7d57600080fd5b614d89878388016145b8565b94506020860135915080821115614d9f57600080fd5b506143b586828701614276565b600060208284031215614dbe57600080fd5b8135613dca81613fd8565b600082601f830112614dda57600080fd5b81356020614dea61406a83613fb4565b8083825260208201915060208460051b870101935086841115614e0c57600080fd5b602086015b848110156120c5578035614e2481613fd8565b8352918301918301614e11565b60006020808385031215614e4457600080fd5b823567ffffffffffffffff80821115614e5c57600080fd5b818501915085601f830112614e7057600080fd5b8135614e7e61406a82613fb4565b81815260059190911b83018401908481019088831115614e9d57600080fd5b8585015b838110156141b357803585811115614eb857600080fd5b860160c0818c03601f19011215614ecf5760008081fd5b614ed7613f1a565b8882013581526040614eea818401614b62565b8a8301526060614efb818501614b62565b8284015260809150614f0e828501614018565b9083015260a08381013589811115614f265760008081fd5b614f348f8d83880101614dc9565b838501525060c0840135915088821115614f4e5760008081fd5b614f5c8e8c84870101614dc9565b9083015250845250918601918601614ea1565b600060208284031215614f8157600080fd5b5035919050565b80356001600160e01b038116811461400557600080fd5b600082601f830112614fb057600080fd5b81356020614fc061406a83613fb4565b82815260069290921b84018101918181019086841115614fdf57600080fd5b8286015b848110156120c55760408189031215614ffc5760008081fd5b615004613f3d565b61500d82613fed565b815261501a858301614f88565b81860152835291830191604001614fe3565b600082601f83011261503d57600080fd5b8135602061504d61406a83613fb4565b82815260079290921b8401810191818101908684111561506c57600080fd5b8286015b848110156120c557808803608081121561508a5760008081fd5b615092613f60565b61509b83613fed565b8152604080601f19840112156150b15760008081fd5b6150b9613f3d565b92506150c6878501613fed565b83526150d3818501613fed565b8388015281870192909252606083013591810191909152835291830191608001615070565b6000602080838503121561510b57600080fd5b823567ffffffffffffffff8082111561512357600080fd5b8185019150604080838803121561513957600080fd5b615141613f3d565b83358381111561515057600080fd5b84016040818a03121561516257600080fd5b61516a613f3d565b81358581111561517957600080fd5b8201601f81018b1361518a57600080fd5b803561519861406a82613fb4565b81815260069190911b8201890190898101908d8311156151b757600080fd5b928a01925b828410156152075787848f0312156151d45760008081fd5b6151dc613f3d565b84356151e781613fd8565b81526151f4858d01614f88565b818d0152825292870192908a01906151bc565b84525050508187013593508484111561521f57600080fd5b61522b8a858401614f9f565b818801528252508385013591508282111561524557600080fd5b6152518883860161502c565b85820152809550505050505092915050565b634e487b7160e01b600052603260045260246000fd5b805160408084528151848201819052600092602091908201906060870190855b818110156152d057835180516001600160a01b031684528501516001600160e01b0316858401529284019291850191600101615299565b50508583015187820388850152805180835290840192506000918401905b8083101561532a578351805167ffffffffffffffff1683528501516001600160e01b0316858301529284019260019290920191908501906152ee565b50979650505050505050565b602081526000610a766020830184615279565b67ffffffffffffffff8316815260608101613dca6020830184805167ffffffffffffffff908116835260209182015116910152565b634e487b7160e01b600052601160045260246000fd5b67ffffffffffffffff8181168382160190808211156153b5576153b561537e565b5092915050565b6000602080835260608451604080848701526153db6060870183615279565b87850151878203601f19016040890152805180835290860193506000918601905b808310156141b357845167ffffffffffffffff81511683528781015161543b89850182805167ffffffffffffffff908116835260209182015116910152565b508401518287015293860193600192909201916080909101906153fc565b60006020828403121561546b57600080fd5b813567ffffffffffffffff81111561548257600080fd5b6136be8482850161484e565b81810381811115610a7957610a7961537e565b634e487b7160e01b600052601260045260246000fd5b600067ffffffffffffffff808416806154d2576154d26154a1565b92169190910692915050565b8082028115828204841417610a7957610a7961537e565b60006040828403121561550757600080fd5b61550f613f3d565b61551883613fed565b8152602083013560208201528091505092915050565b600181811c9082168061554257607f821691505b60208210810361556257634e487b7160e01b600052602260045260246000fd5b50919050565b805182526000602067ffffffffffffffff81840151168185015260408084015160a0604087015261559c60a08701826141e4565b9050606085015186820360608801526155b582826141e4565b608087810151898303918a01919091528051808352908601935060009250908501905b8083101561532a57835180516001600160a01b03168352860151868301529285019260019290920191908401906155d8565b602081526000610a766020830184615568565b6080815260006156306080830187615568565b61ffff9590951660208301525060408101929092526001600160a01b0316606090910152919050565b60008060006060848603121561566e57600080fd5b83516156798161400a565b602085015190935067ffffffffffffffff81111561569657600080fd5b8401601f810186136156a757600080fd5b80516156b561406a82614023565b8181528760208385010111156156ca57600080fd5b6156db8260208301602086016141c0565b809450505050604084015190509250925092565b60006020828403121561570157600080fd5b8151613dca8161400a565b601f821115610cd8576000816000526020600020601f850160051c810160208610156157355750805b601f850160051c820191505b818110156124df57828155600101615741565b815167ffffffffffffffff81111561576e5761576e613eb8565b6157828161577c845461552e565b8461570c565b602080601f8311600181146157b7576000841561579f5750858301515b600019600386901b1c1916600185901b1785556124df565b600085815260208120601f198616915b828110156157e6578886015182559484019460019091019084016157c7565b50858210156158045787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6000602080835283546001600160a01b038116602085015260ff8160a01c161515604085015267ffffffffffffffff8160a81c166060850152506001808501608080860152600081546158668161552e565b8060a089015260c0600183166000811461588757600181146158a3576158d3565b60ff19841660c08b015260c083151560051b8b010194506158d3565b85600052602060002060005b848110156158ca5781548c82018501529088019089016158af565b8b0160c0019550505b50929998505050505050505050565b80820180821115610a7957610a7961537e565b60ff8181168382160190811115610a7957610a7961537e565b8183823760009101908152919050565b828152606082602083013760800192915050565b600067ffffffffffffffff8084168061594d5761594d6154a1565b92169190910492915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261598e57600080fd5b83018035915067ffffffffffffffff8211156159a957600080fd5b60200191503681900382131561383157600080fd5b60208101600583106159d2576159d26143f5565b91905290565b60ff81811683821602908116908181146153b5576153b561537e565b600060a0820160ff881683526020878185015260a0604085015281875480845260c0860191508860005282600020935060005b81811015615a4c5784546001600160a01b031683526001948501949284019201615a27565b50508481036060860152865180825290820192508187019060005b81811015615a8c5782516001600160a01b031685529383019391830191600101615a67565b50505060ff851660808501525090506120c7565b600067ffffffffffffffff808616835280851660208401525060606040830152614c3f60608301846141e4565b8281526040602082015260006136be60408301846141e4565b67ffffffffffffffff848116825283166020820152606081016136be604083018461440b565b615b16818461440b565b6040602082015260006136be60408301846141e4565b600060208284031215615b3e57600080fd5b8151613dca81613fd8565b6020815260008251610100806020850152615b686101208501836141e4565b91506020850151615b85604086018267ffffffffffffffff169052565b5060408501516001600160a01b038116606086015250606085015160808501526080850151615bbf60a08601826001600160a01b03169052565b5060a0850151601f19808685030160c0870152615bdc84836141e4565b935060c08701519150808685030160e0870152615bf984836141e4565b935060e08701519150808685030183870152506120c783826141e4565b600060208284031215615c2857600080fd5b5051919050565b600060ff821660ff8103615c4557615c4561537e565b60010192915050565b848152600067ffffffffffffffff8086166020840152808516604084015250608060608301526120c760808301846141e4565b86815260c060208201526000615c9a60c08301886141e4565b6001600160a01b039690961660408301525067ffffffffffffffff9384166060820152608081019290925290911660a09091015292915050565b600082825180855260208086019550808260051b84010181860160005b84811015615d6b57601f19868403018952815160808151818652615d17828701826141e4565b9150508582015185820387870152615d2f82826141e4565b91505060408083015186830382880152615d4983826141e4565b6060948501519790940196909652505098840198925090830190600101615cf1565b5090979650505050505050565b602081526000610a766020830184615cd4565b60008282518085526020808601955060208260051b8401016020860160005b84811015615d6b57601f19868403018952615dc68383516141e4565b98840198925090830190600101615daa565b604081526000835180516040840152602081015167ffffffffffffffff80821660608601528060408401511660808601528060608401511660a08601528060808401511660c086015250505060208401516101408060e0850152615e406101808501836141e4565b915060408601517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08086850301610100870152615e7d84836141e4565b935060608801519150615e9c6101208701836001600160a01b03169052565b60808801518387015260a0880151925080868503016101608701525050615ec38282615cd4565b9150508281036020840152614c3f8185615d8b56fea164736f6c6343000818000a", +} + +var EVM2EVMMultiOffRampABI = EVM2EVMMultiOffRampMetaData.ABI + +var EVM2EVMMultiOffRampBin = EVM2EVMMultiOffRampMetaData.Bin + +func DeployEVM2EVMMultiOffRamp(auth *bind.TransactOpts, backend bind.ContractBackend, staticConfig EVM2EVMMultiOffRampStaticConfig, dynamicConfig EVM2EVMMultiOffRampDynamicConfig, sourceChainConfigs []EVM2EVMMultiOffRampSourceChainConfigArgs) (common.Address, *types.Transaction, *EVM2EVMMultiOffRamp, error) { + parsed, err := EVM2EVMMultiOffRampMetaData.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(EVM2EVMMultiOffRampBin), backend, staticConfig, dynamicConfig, sourceChainConfigs) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &EVM2EVMMultiOffRamp{address: address, abi: *parsed, EVM2EVMMultiOffRampCaller: EVM2EVMMultiOffRampCaller{contract: contract}, EVM2EVMMultiOffRampTransactor: EVM2EVMMultiOffRampTransactor{contract: contract}, EVM2EVMMultiOffRampFilterer: EVM2EVMMultiOffRampFilterer{contract: contract}}, nil +} + +type EVM2EVMMultiOffRamp struct { + address common.Address + abi abi.ABI + EVM2EVMMultiOffRampCaller + EVM2EVMMultiOffRampTransactor + EVM2EVMMultiOffRampFilterer +} + +type EVM2EVMMultiOffRampCaller struct { + contract *bind.BoundContract +} + +type EVM2EVMMultiOffRampTransactor struct { + contract *bind.BoundContract +} + +type EVM2EVMMultiOffRampFilterer struct { + contract *bind.BoundContract +} + +type EVM2EVMMultiOffRampSession struct { + Contract *EVM2EVMMultiOffRamp + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type EVM2EVMMultiOffRampCallerSession struct { + Contract *EVM2EVMMultiOffRampCaller + CallOpts bind.CallOpts +} + +type EVM2EVMMultiOffRampTransactorSession struct { + Contract *EVM2EVMMultiOffRampTransactor + TransactOpts bind.TransactOpts +} + +type EVM2EVMMultiOffRampRaw struct { + Contract *EVM2EVMMultiOffRamp +} + +type EVM2EVMMultiOffRampCallerRaw struct { + Contract *EVM2EVMMultiOffRampCaller +} + +type EVM2EVMMultiOffRampTransactorRaw struct { + Contract *EVM2EVMMultiOffRampTransactor +} + +func NewEVM2EVMMultiOffRamp(address common.Address, backend bind.ContractBackend) (*EVM2EVMMultiOffRamp, error) { + abi, err := abi.JSON(strings.NewReader(EVM2EVMMultiOffRampABI)) + if err != nil { + return nil, err + } + contract, err := bindEVM2EVMMultiOffRamp(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &EVM2EVMMultiOffRamp{address: address, abi: abi, EVM2EVMMultiOffRampCaller: EVM2EVMMultiOffRampCaller{contract: contract}, EVM2EVMMultiOffRampTransactor: EVM2EVMMultiOffRampTransactor{contract: contract}, EVM2EVMMultiOffRampFilterer: EVM2EVMMultiOffRampFilterer{contract: contract}}, nil +} + +func NewEVM2EVMMultiOffRampCaller(address common.Address, caller bind.ContractCaller) (*EVM2EVMMultiOffRampCaller, error) { + contract, err := bindEVM2EVMMultiOffRamp(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &EVM2EVMMultiOffRampCaller{contract: contract}, nil +} + +func NewEVM2EVMMultiOffRampTransactor(address common.Address, transactor bind.ContractTransactor) (*EVM2EVMMultiOffRampTransactor, error) { + contract, err := bindEVM2EVMMultiOffRamp(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &EVM2EVMMultiOffRampTransactor{contract: contract}, nil +} + +func NewEVM2EVMMultiOffRampFilterer(address common.Address, filterer bind.ContractFilterer) (*EVM2EVMMultiOffRampFilterer, error) { + contract, err := bindEVM2EVMMultiOffRamp(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &EVM2EVMMultiOffRampFilterer{contract: contract}, nil +} + +func bindEVM2EVMMultiOffRamp(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := EVM2EVMMultiOffRampMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _EVM2EVMMultiOffRamp.Contract.EVM2EVMMultiOffRampCaller.contract.Call(opts, result, method, params...) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.Contract.EVM2EVMMultiOffRampTransactor.contract.Transfer(opts) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.Contract.EVM2EVMMultiOffRampTransactor.contract.Transact(opts, method, params...) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _EVM2EVMMultiOffRamp.Contract.contract.Call(opts, result, method, params...) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.Contract.contract.Transfer(opts) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.Contract.contract.Transact(opts, method, params...) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampCaller) CcipReceive(opts *bind.CallOpts, arg0 ClientAny2EVMMessage) error { + var out []interface{} + err := _EVM2EVMMultiOffRamp.contract.Call(opts, &out, "ccipReceive", arg0) + + if err != nil { + return err + } + + return err + +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampSession) CcipReceive(arg0 ClientAny2EVMMessage) error { + return _EVM2EVMMultiOffRamp.Contract.CcipReceive(&_EVM2EVMMultiOffRamp.CallOpts, arg0) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampCallerSession) CcipReceive(arg0 ClientAny2EVMMessage) error { + return _EVM2EVMMultiOffRamp.Contract.CcipReceive(&_EVM2EVMMultiOffRamp.CallOpts, arg0) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampCaller) GetDynamicConfig(opts *bind.CallOpts) (EVM2EVMMultiOffRampDynamicConfig, error) { + var out []interface{} + err := _EVM2EVMMultiOffRamp.contract.Call(opts, &out, "getDynamicConfig") + + if err != nil { + return *new(EVM2EVMMultiOffRampDynamicConfig), err + } + + out0 := *abi.ConvertType(out[0], new(EVM2EVMMultiOffRampDynamicConfig)).(*EVM2EVMMultiOffRampDynamicConfig) + + return out0, err + +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampSession) GetDynamicConfig() (EVM2EVMMultiOffRampDynamicConfig, error) { + return _EVM2EVMMultiOffRamp.Contract.GetDynamicConfig(&_EVM2EVMMultiOffRamp.CallOpts) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampCallerSession) GetDynamicConfig() (EVM2EVMMultiOffRampDynamicConfig, error) { + return _EVM2EVMMultiOffRamp.Contract.GetDynamicConfig(&_EVM2EVMMultiOffRamp.CallOpts) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampCaller) GetExecutionState(opts *bind.CallOpts, sourceChainSelector uint64, sequenceNumber uint64) (uint8, error) { + var out []interface{} + err := _EVM2EVMMultiOffRamp.contract.Call(opts, &out, "getExecutionState", sourceChainSelector, sequenceNumber) + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampSession) GetExecutionState(sourceChainSelector uint64, sequenceNumber uint64) (uint8, error) { + return _EVM2EVMMultiOffRamp.Contract.GetExecutionState(&_EVM2EVMMultiOffRamp.CallOpts, sourceChainSelector, sequenceNumber) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampCallerSession) GetExecutionState(sourceChainSelector uint64, sequenceNumber uint64) (uint8, error) { + return _EVM2EVMMultiOffRamp.Contract.GetExecutionState(&_EVM2EVMMultiOffRamp.CallOpts, sourceChainSelector, sequenceNumber) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampCaller) GetLatestPriceSequenceNumber(opts *bind.CallOpts) (uint64, error) { + var out []interface{} + err := _EVM2EVMMultiOffRamp.contract.Call(opts, &out, "getLatestPriceSequenceNumber") + + if err != nil { + return *new(uint64), err + } + + out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) + + return out0, err + +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampSession) GetLatestPriceSequenceNumber() (uint64, error) { + return _EVM2EVMMultiOffRamp.Contract.GetLatestPriceSequenceNumber(&_EVM2EVMMultiOffRamp.CallOpts) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampCallerSession) GetLatestPriceSequenceNumber() (uint64, error) { + return _EVM2EVMMultiOffRamp.Contract.GetLatestPriceSequenceNumber(&_EVM2EVMMultiOffRamp.CallOpts) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampCaller) GetMerkleRoot(opts *bind.CallOpts, sourceChainSelector uint64, root [32]byte) (*big.Int, error) { + var out []interface{} + err := _EVM2EVMMultiOffRamp.contract.Call(opts, &out, "getMerkleRoot", sourceChainSelector, root) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampSession) GetMerkleRoot(sourceChainSelector uint64, root [32]byte) (*big.Int, error) { + return _EVM2EVMMultiOffRamp.Contract.GetMerkleRoot(&_EVM2EVMMultiOffRamp.CallOpts, sourceChainSelector, root) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampCallerSession) GetMerkleRoot(sourceChainSelector uint64, root [32]byte) (*big.Int, error) { + return _EVM2EVMMultiOffRamp.Contract.GetMerkleRoot(&_EVM2EVMMultiOffRamp.CallOpts, sourceChainSelector, root) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampCaller) GetSourceChainConfig(opts *bind.CallOpts, sourceChainSelector uint64) (EVM2EVMMultiOffRampSourceChainConfig, error) { + var out []interface{} + err := _EVM2EVMMultiOffRamp.contract.Call(opts, &out, "getSourceChainConfig", sourceChainSelector) + + if err != nil { + return *new(EVM2EVMMultiOffRampSourceChainConfig), err + } + + out0 := *abi.ConvertType(out[0], new(EVM2EVMMultiOffRampSourceChainConfig)).(*EVM2EVMMultiOffRampSourceChainConfig) + + return out0, err + +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampSession) GetSourceChainConfig(sourceChainSelector uint64) (EVM2EVMMultiOffRampSourceChainConfig, error) { + return _EVM2EVMMultiOffRamp.Contract.GetSourceChainConfig(&_EVM2EVMMultiOffRamp.CallOpts, sourceChainSelector) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampCallerSession) GetSourceChainConfig(sourceChainSelector uint64) (EVM2EVMMultiOffRampSourceChainConfig, error) { + return _EVM2EVMMultiOffRamp.Contract.GetSourceChainConfig(&_EVM2EVMMultiOffRamp.CallOpts, sourceChainSelector) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampCaller) GetStaticConfig(opts *bind.CallOpts) (EVM2EVMMultiOffRampStaticConfig, error) { + var out []interface{} + err := _EVM2EVMMultiOffRamp.contract.Call(opts, &out, "getStaticConfig") + + if err != nil { + return *new(EVM2EVMMultiOffRampStaticConfig), err + } + + out0 := *abi.ConvertType(out[0], new(EVM2EVMMultiOffRampStaticConfig)).(*EVM2EVMMultiOffRampStaticConfig) + + return out0, err + +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampSession) GetStaticConfig() (EVM2EVMMultiOffRampStaticConfig, error) { + return _EVM2EVMMultiOffRamp.Contract.GetStaticConfig(&_EVM2EVMMultiOffRamp.CallOpts) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampCallerSession) GetStaticConfig() (EVM2EVMMultiOffRampStaticConfig, error) { + return _EVM2EVMMultiOffRamp.Contract.GetStaticConfig(&_EVM2EVMMultiOffRamp.CallOpts) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampCaller) IsBlessed(opts *bind.CallOpts, root [32]byte) (bool, error) { + var out []interface{} + err := _EVM2EVMMultiOffRamp.contract.Call(opts, &out, "isBlessed", root) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampSession) IsBlessed(root [32]byte) (bool, error) { + return _EVM2EVMMultiOffRamp.Contract.IsBlessed(&_EVM2EVMMultiOffRamp.CallOpts, root) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampCallerSession) IsBlessed(root [32]byte) (bool, error) { + return _EVM2EVMMultiOffRamp.Contract.IsBlessed(&_EVM2EVMMultiOffRamp.CallOpts, root) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampCaller) LatestConfigDetails(opts *bind.CallOpts, ocrPluginType uint8) (MultiOCR3BaseOCRConfig, error) { + var out []interface{} + err := _EVM2EVMMultiOffRamp.contract.Call(opts, &out, "latestConfigDetails", ocrPluginType) + + if err != nil { + return *new(MultiOCR3BaseOCRConfig), err + } + + out0 := *abi.ConvertType(out[0], new(MultiOCR3BaseOCRConfig)).(*MultiOCR3BaseOCRConfig) + + return out0, err + +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampSession) LatestConfigDetails(ocrPluginType uint8) (MultiOCR3BaseOCRConfig, error) { + return _EVM2EVMMultiOffRamp.Contract.LatestConfigDetails(&_EVM2EVMMultiOffRamp.CallOpts, ocrPluginType) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampCallerSession) LatestConfigDetails(ocrPluginType uint8) (MultiOCR3BaseOCRConfig, error) { + return _EVM2EVMMultiOffRamp.Contract.LatestConfigDetails(&_EVM2EVMMultiOffRamp.CallOpts, ocrPluginType) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _EVM2EVMMultiOffRamp.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampSession) Owner() (common.Address, error) { + return _EVM2EVMMultiOffRamp.Contract.Owner(&_EVM2EVMMultiOffRamp.CallOpts) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampCallerSession) Owner() (common.Address, error) { + return _EVM2EVMMultiOffRamp.Contract.Owner(&_EVM2EVMMultiOffRamp.CallOpts) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampCaller) TypeAndVersion(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _EVM2EVMMultiOffRamp.contract.Call(opts, &out, "typeAndVersion") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampSession) TypeAndVersion() (string, error) { + return _EVM2EVMMultiOffRamp.Contract.TypeAndVersion(&_EVM2EVMMultiOffRamp.CallOpts) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampCallerSession) TypeAndVersion() (string, error) { + return _EVM2EVMMultiOffRamp.Contract.TypeAndVersion(&_EVM2EVMMultiOffRamp.CallOpts) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampTransactor) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.contract.Transact(opts, "acceptOwnership") +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampSession) AcceptOwnership() (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.Contract.AcceptOwnership(&_EVM2EVMMultiOffRamp.TransactOpts) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampTransactorSession) AcceptOwnership() (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.Contract.AcceptOwnership(&_EVM2EVMMultiOffRamp.TransactOpts) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampTransactor) ApplySourceChainConfigUpdates(opts *bind.TransactOpts, sourceChainConfigUpdates []EVM2EVMMultiOffRampSourceChainConfigArgs) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.contract.Transact(opts, "applySourceChainConfigUpdates", sourceChainConfigUpdates) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampSession) ApplySourceChainConfigUpdates(sourceChainConfigUpdates []EVM2EVMMultiOffRampSourceChainConfigArgs) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.Contract.ApplySourceChainConfigUpdates(&_EVM2EVMMultiOffRamp.TransactOpts, sourceChainConfigUpdates) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampTransactorSession) ApplySourceChainConfigUpdates(sourceChainConfigUpdates []EVM2EVMMultiOffRampSourceChainConfigArgs) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.Contract.ApplySourceChainConfigUpdates(&_EVM2EVMMultiOffRamp.TransactOpts, sourceChainConfigUpdates) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampTransactor) Commit(opts *bind.TransactOpts, reportContext [3][32]byte, report []byte, rs [][32]byte, ss [][32]byte, rawVs [32]byte) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.contract.Transact(opts, "commit", reportContext, report, rs, ss, rawVs) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampSession) Commit(reportContext [3][32]byte, report []byte, rs [][32]byte, ss [][32]byte, rawVs [32]byte) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.Contract.Commit(&_EVM2EVMMultiOffRamp.TransactOpts, reportContext, report, rs, ss, rawVs) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampTransactorSession) Commit(reportContext [3][32]byte, report []byte, rs [][32]byte, ss [][32]byte, rawVs [32]byte) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.Contract.Commit(&_EVM2EVMMultiOffRamp.TransactOpts, reportContext, report, rs, ss, rawVs) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampTransactor) Execute(opts *bind.TransactOpts, reportContext [3][32]byte, report []byte) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.contract.Transact(opts, "execute", reportContext, report) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampSession) Execute(reportContext [3][32]byte, report []byte) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.Contract.Execute(&_EVM2EVMMultiOffRamp.TransactOpts, reportContext, report) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampTransactorSession) Execute(reportContext [3][32]byte, report []byte) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.Contract.Execute(&_EVM2EVMMultiOffRamp.TransactOpts, reportContext, report) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampTransactor) ExecuteSingleMessage(opts *bind.TransactOpts, message InternalAny2EVMRampMessage, offchainTokenData [][]byte) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.contract.Transact(opts, "executeSingleMessage", message, offchainTokenData) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampSession) ExecuteSingleMessage(message InternalAny2EVMRampMessage, offchainTokenData [][]byte) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.Contract.ExecuteSingleMessage(&_EVM2EVMMultiOffRamp.TransactOpts, message, offchainTokenData) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampTransactorSession) ExecuteSingleMessage(message InternalAny2EVMRampMessage, offchainTokenData [][]byte) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.Contract.ExecuteSingleMessage(&_EVM2EVMMultiOffRamp.TransactOpts, message, offchainTokenData) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampTransactor) ManuallyExecute(opts *bind.TransactOpts, reports []InternalExecutionReportSingleChain, gasLimitOverrides [][]*big.Int) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.contract.Transact(opts, "manuallyExecute", reports, gasLimitOverrides) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampSession) ManuallyExecute(reports []InternalExecutionReportSingleChain, gasLimitOverrides [][]*big.Int) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.Contract.ManuallyExecute(&_EVM2EVMMultiOffRamp.TransactOpts, reports, gasLimitOverrides) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampTransactorSession) ManuallyExecute(reports []InternalExecutionReportSingleChain, gasLimitOverrides [][]*big.Int) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.Contract.ManuallyExecute(&_EVM2EVMMultiOffRamp.TransactOpts, reports, gasLimitOverrides) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampTransactor) ResetUnblessedRoots(opts *bind.TransactOpts, rootToReset []EVM2EVMMultiOffRampUnblessedRoot) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.contract.Transact(opts, "resetUnblessedRoots", rootToReset) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampSession) ResetUnblessedRoots(rootToReset []EVM2EVMMultiOffRampUnblessedRoot) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.Contract.ResetUnblessedRoots(&_EVM2EVMMultiOffRamp.TransactOpts, rootToReset) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampTransactorSession) ResetUnblessedRoots(rootToReset []EVM2EVMMultiOffRampUnblessedRoot) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.Contract.ResetUnblessedRoots(&_EVM2EVMMultiOffRamp.TransactOpts, rootToReset) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampTransactor) SetDynamicConfig(opts *bind.TransactOpts, dynamicConfig EVM2EVMMultiOffRampDynamicConfig) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.contract.Transact(opts, "setDynamicConfig", dynamicConfig) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampSession) SetDynamicConfig(dynamicConfig EVM2EVMMultiOffRampDynamicConfig) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.Contract.SetDynamicConfig(&_EVM2EVMMultiOffRamp.TransactOpts, dynamicConfig) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampTransactorSession) SetDynamicConfig(dynamicConfig EVM2EVMMultiOffRampDynamicConfig) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.Contract.SetDynamicConfig(&_EVM2EVMMultiOffRamp.TransactOpts, dynamicConfig) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampTransactor) SetOCR3Configs(opts *bind.TransactOpts, ocrConfigArgs []MultiOCR3BaseOCRConfigArgs) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.contract.Transact(opts, "setOCR3Configs", ocrConfigArgs) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampSession) SetOCR3Configs(ocrConfigArgs []MultiOCR3BaseOCRConfigArgs) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.Contract.SetOCR3Configs(&_EVM2EVMMultiOffRamp.TransactOpts, ocrConfigArgs) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampTransactorSession) SetOCR3Configs(ocrConfigArgs []MultiOCR3BaseOCRConfigArgs) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.Contract.SetOCR3Configs(&_EVM2EVMMultiOffRamp.TransactOpts, ocrConfigArgs) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampTransactor) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.contract.Transact(opts, "transferOwnership", to) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.Contract.TransferOwnership(&_EVM2EVMMultiOffRamp.TransactOpts, to) +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampTransactorSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _EVM2EVMMultiOffRamp.Contract.TransferOwnership(&_EVM2EVMMultiOffRamp.TransactOpts, to) +} + +type EVM2EVMMultiOffRampAlreadyAttemptedIterator struct { + Event *EVM2EVMMultiOffRampAlreadyAttempted + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *EVM2EVMMultiOffRampAlreadyAttemptedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOffRampAlreadyAttempted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOffRampAlreadyAttempted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *EVM2EVMMultiOffRampAlreadyAttemptedIterator) Error() error { + return it.fail +} + +func (it *EVM2EVMMultiOffRampAlreadyAttemptedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type EVM2EVMMultiOffRampAlreadyAttempted struct { + SourceChainSelector uint64 + SequenceNumber uint64 + Raw types.Log +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) FilterAlreadyAttempted(opts *bind.FilterOpts) (*EVM2EVMMultiOffRampAlreadyAttemptedIterator, error) { + + logs, sub, err := _EVM2EVMMultiOffRamp.contract.FilterLogs(opts, "AlreadyAttempted") + if err != nil { + return nil, err + } + return &EVM2EVMMultiOffRampAlreadyAttemptedIterator{contract: _EVM2EVMMultiOffRamp.contract, event: "AlreadyAttempted", logs: logs, sub: sub}, nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) WatchAlreadyAttempted(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampAlreadyAttempted) (event.Subscription, error) { + + logs, sub, err := _EVM2EVMMultiOffRamp.contract.WatchLogs(opts, "AlreadyAttempted") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(EVM2EVMMultiOffRampAlreadyAttempted) + if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "AlreadyAttempted", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) ParseAlreadyAttempted(log types.Log) (*EVM2EVMMultiOffRampAlreadyAttempted, error) { + event := new(EVM2EVMMultiOffRampAlreadyAttempted) + if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "AlreadyAttempted", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type EVM2EVMMultiOffRampCommitReportAcceptedIterator struct { + Event *EVM2EVMMultiOffRampCommitReportAccepted + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *EVM2EVMMultiOffRampCommitReportAcceptedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOffRampCommitReportAccepted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOffRampCommitReportAccepted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *EVM2EVMMultiOffRampCommitReportAcceptedIterator) Error() error { + return it.fail +} + +func (it *EVM2EVMMultiOffRampCommitReportAcceptedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type EVM2EVMMultiOffRampCommitReportAccepted struct { + Report EVM2EVMMultiOffRampCommitReport + Raw types.Log +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) FilterCommitReportAccepted(opts *bind.FilterOpts) (*EVM2EVMMultiOffRampCommitReportAcceptedIterator, error) { + + logs, sub, err := _EVM2EVMMultiOffRamp.contract.FilterLogs(opts, "CommitReportAccepted") + if err != nil { + return nil, err + } + return &EVM2EVMMultiOffRampCommitReportAcceptedIterator{contract: _EVM2EVMMultiOffRamp.contract, event: "CommitReportAccepted", logs: logs, sub: sub}, nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) WatchCommitReportAccepted(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampCommitReportAccepted) (event.Subscription, error) { + + logs, sub, err := _EVM2EVMMultiOffRamp.contract.WatchLogs(opts, "CommitReportAccepted") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(EVM2EVMMultiOffRampCommitReportAccepted) + if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "CommitReportAccepted", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) ParseCommitReportAccepted(log types.Log) (*EVM2EVMMultiOffRampCommitReportAccepted, error) { + event := new(EVM2EVMMultiOffRampCommitReportAccepted) + if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "CommitReportAccepted", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type EVM2EVMMultiOffRampConfigSetIterator struct { + Event *EVM2EVMMultiOffRampConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *EVM2EVMMultiOffRampConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOffRampConfigSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOffRampConfigSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *EVM2EVMMultiOffRampConfigSetIterator) Error() error { + return it.fail +} + +func (it *EVM2EVMMultiOffRampConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type EVM2EVMMultiOffRampConfigSet struct { + OcrPluginType uint8 + ConfigDigest [32]byte + Signers []common.Address + Transmitters []common.Address + F uint8 + Raw types.Log +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) FilterConfigSet(opts *bind.FilterOpts) (*EVM2EVMMultiOffRampConfigSetIterator, error) { + + logs, sub, err := _EVM2EVMMultiOffRamp.contract.FilterLogs(opts, "ConfigSet") + if err != nil { + return nil, err + } + return &EVM2EVMMultiOffRampConfigSetIterator{contract: _EVM2EVMMultiOffRamp.contract, event: "ConfigSet", logs: logs, sub: sub}, nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) WatchConfigSet(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampConfigSet) (event.Subscription, error) { + + logs, sub, err := _EVM2EVMMultiOffRamp.contract.WatchLogs(opts, "ConfigSet") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(EVM2EVMMultiOffRampConfigSet) + if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "ConfigSet", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) ParseConfigSet(log types.Log) (*EVM2EVMMultiOffRampConfigSet, error) { + event := new(EVM2EVMMultiOffRampConfigSet) + if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "ConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type EVM2EVMMultiOffRampDynamicConfigSetIterator struct { + Event *EVM2EVMMultiOffRampDynamicConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *EVM2EVMMultiOffRampDynamicConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOffRampDynamicConfigSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOffRampDynamicConfigSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *EVM2EVMMultiOffRampDynamicConfigSetIterator) Error() error { + return it.fail +} + +func (it *EVM2EVMMultiOffRampDynamicConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type EVM2EVMMultiOffRampDynamicConfigSet struct { + DynamicConfig EVM2EVMMultiOffRampDynamicConfig + Raw types.Log +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) FilterDynamicConfigSet(opts *bind.FilterOpts) (*EVM2EVMMultiOffRampDynamicConfigSetIterator, error) { + + logs, sub, err := _EVM2EVMMultiOffRamp.contract.FilterLogs(opts, "DynamicConfigSet") + if err != nil { + return nil, err + } + return &EVM2EVMMultiOffRampDynamicConfigSetIterator{contract: _EVM2EVMMultiOffRamp.contract, event: "DynamicConfigSet", logs: logs, sub: sub}, nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) WatchDynamicConfigSet(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampDynamicConfigSet) (event.Subscription, error) { + + logs, sub, err := _EVM2EVMMultiOffRamp.contract.WatchLogs(opts, "DynamicConfigSet") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(EVM2EVMMultiOffRampDynamicConfigSet) + if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "DynamicConfigSet", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) ParseDynamicConfigSet(log types.Log) (*EVM2EVMMultiOffRampDynamicConfigSet, error) { + event := new(EVM2EVMMultiOffRampDynamicConfigSet) + if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "DynamicConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type EVM2EVMMultiOffRampExecutionStateChangedIterator struct { + Event *EVM2EVMMultiOffRampExecutionStateChanged + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *EVM2EVMMultiOffRampExecutionStateChangedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOffRampExecutionStateChanged) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOffRampExecutionStateChanged) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *EVM2EVMMultiOffRampExecutionStateChangedIterator) Error() error { + return it.fail +} + +func (it *EVM2EVMMultiOffRampExecutionStateChangedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type EVM2EVMMultiOffRampExecutionStateChanged struct { + SourceChainSelector uint64 + SequenceNumber uint64 + MessageId [32]byte + State uint8 + ReturnData []byte + Raw types.Log +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) FilterExecutionStateChanged(opts *bind.FilterOpts, sourceChainSelector []uint64, sequenceNumber []uint64, messageId [][32]byte) (*EVM2EVMMultiOffRampExecutionStateChangedIterator, error) { + + var sourceChainSelectorRule []interface{} + for _, sourceChainSelectorItem := range sourceChainSelector { + sourceChainSelectorRule = append(sourceChainSelectorRule, sourceChainSelectorItem) + } + var sequenceNumberRule []interface{} + for _, sequenceNumberItem := range sequenceNumber { + sequenceNumberRule = append(sequenceNumberRule, sequenceNumberItem) + } + var messageIdRule []interface{} + for _, messageIdItem := range messageId { + messageIdRule = append(messageIdRule, messageIdItem) + } + + logs, sub, err := _EVM2EVMMultiOffRamp.contract.FilterLogs(opts, "ExecutionStateChanged", sourceChainSelectorRule, sequenceNumberRule, messageIdRule) + if err != nil { + return nil, err + } + return &EVM2EVMMultiOffRampExecutionStateChangedIterator{contract: _EVM2EVMMultiOffRamp.contract, event: "ExecutionStateChanged", logs: logs, sub: sub}, nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) WatchExecutionStateChanged(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampExecutionStateChanged, sourceChainSelector []uint64, sequenceNumber []uint64, messageId [][32]byte) (event.Subscription, error) { + + var sourceChainSelectorRule []interface{} + for _, sourceChainSelectorItem := range sourceChainSelector { + sourceChainSelectorRule = append(sourceChainSelectorRule, sourceChainSelectorItem) + } + var sequenceNumberRule []interface{} + for _, sequenceNumberItem := range sequenceNumber { + sequenceNumberRule = append(sequenceNumberRule, sequenceNumberItem) + } + var messageIdRule []interface{} + for _, messageIdItem := range messageId { + messageIdRule = append(messageIdRule, messageIdItem) + } + + logs, sub, err := _EVM2EVMMultiOffRamp.contract.WatchLogs(opts, "ExecutionStateChanged", sourceChainSelectorRule, sequenceNumberRule, messageIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(EVM2EVMMultiOffRampExecutionStateChanged) + if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "ExecutionStateChanged", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) ParseExecutionStateChanged(log types.Log) (*EVM2EVMMultiOffRampExecutionStateChanged, error) { + event := new(EVM2EVMMultiOffRampExecutionStateChanged) + if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "ExecutionStateChanged", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type EVM2EVMMultiOffRampOwnershipTransferRequestedIterator struct { + Event *EVM2EVMMultiOffRampOwnershipTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *EVM2EVMMultiOffRampOwnershipTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOffRampOwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOffRampOwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *EVM2EVMMultiOffRampOwnershipTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *EVM2EVMMultiOffRampOwnershipTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type EVM2EVMMultiOffRampOwnershipTransferRequested struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*EVM2EVMMultiOffRampOwnershipTransferRequestedIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _EVM2EVMMultiOffRamp.contract.FilterLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return &EVM2EVMMultiOffRampOwnershipTransferRequestedIterator{contract: _EVM2EVMMultiOffRamp.contract, event: "OwnershipTransferRequested", logs: logs, sub: sub}, nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _EVM2EVMMultiOffRamp.contract.WatchLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(EVM2EVMMultiOffRampOwnershipTransferRequested) + if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) ParseOwnershipTransferRequested(log types.Log) (*EVM2EVMMultiOffRampOwnershipTransferRequested, error) { + event := new(EVM2EVMMultiOffRampOwnershipTransferRequested) + if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type EVM2EVMMultiOffRampOwnershipTransferredIterator struct { + Event *EVM2EVMMultiOffRampOwnershipTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *EVM2EVMMultiOffRampOwnershipTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOffRampOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOffRampOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *EVM2EVMMultiOffRampOwnershipTransferredIterator) Error() error { + return it.fail +} + +func (it *EVM2EVMMultiOffRampOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type EVM2EVMMultiOffRampOwnershipTransferred struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*EVM2EVMMultiOffRampOwnershipTransferredIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _EVM2EVMMultiOffRamp.contract.FilterLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return &EVM2EVMMultiOffRampOwnershipTransferredIterator{contract: _EVM2EVMMultiOffRamp.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _EVM2EVMMultiOffRamp.contract.WatchLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(EVM2EVMMultiOffRampOwnershipTransferred) + if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) ParseOwnershipTransferred(log types.Log) (*EVM2EVMMultiOffRampOwnershipTransferred, error) { + event := new(EVM2EVMMultiOffRampOwnershipTransferred) + if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type EVM2EVMMultiOffRampRootRemovedIterator struct { + Event *EVM2EVMMultiOffRampRootRemoved + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *EVM2EVMMultiOffRampRootRemovedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOffRampRootRemoved) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOffRampRootRemoved) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *EVM2EVMMultiOffRampRootRemovedIterator) Error() error { + return it.fail +} + +func (it *EVM2EVMMultiOffRampRootRemovedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type EVM2EVMMultiOffRampRootRemoved struct { + Root [32]byte + Raw types.Log +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) FilterRootRemoved(opts *bind.FilterOpts) (*EVM2EVMMultiOffRampRootRemovedIterator, error) { + + logs, sub, err := _EVM2EVMMultiOffRamp.contract.FilterLogs(opts, "RootRemoved") + if err != nil { + return nil, err + } + return &EVM2EVMMultiOffRampRootRemovedIterator{contract: _EVM2EVMMultiOffRamp.contract, event: "RootRemoved", logs: logs, sub: sub}, nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) WatchRootRemoved(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampRootRemoved) (event.Subscription, error) { + + logs, sub, err := _EVM2EVMMultiOffRamp.contract.WatchLogs(opts, "RootRemoved") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(EVM2EVMMultiOffRampRootRemoved) + if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "RootRemoved", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) ParseRootRemoved(log types.Log) (*EVM2EVMMultiOffRampRootRemoved, error) { + event := new(EVM2EVMMultiOffRampRootRemoved) + if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "RootRemoved", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type EVM2EVMMultiOffRampSkippedAlreadyExecutedMessageIterator struct { + Event *EVM2EVMMultiOffRampSkippedAlreadyExecutedMessage + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *EVM2EVMMultiOffRampSkippedAlreadyExecutedMessageIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOffRampSkippedAlreadyExecutedMessage) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOffRampSkippedAlreadyExecutedMessage) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *EVM2EVMMultiOffRampSkippedAlreadyExecutedMessageIterator) Error() error { + return it.fail +} + +func (it *EVM2EVMMultiOffRampSkippedAlreadyExecutedMessageIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type EVM2EVMMultiOffRampSkippedAlreadyExecutedMessage struct { + SourceChainSelector uint64 + SequenceNumber uint64 + Raw types.Log +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) FilterSkippedAlreadyExecutedMessage(opts *bind.FilterOpts) (*EVM2EVMMultiOffRampSkippedAlreadyExecutedMessageIterator, error) { + + logs, sub, err := _EVM2EVMMultiOffRamp.contract.FilterLogs(opts, "SkippedAlreadyExecutedMessage") + if err != nil { + return nil, err + } + return &EVM2EVMMultiOffRampSkippedAlreadyExecutedMessageIterator{contract: _EVM2EVMMultiOffRamp.contract, event: "SkippedAlreadyExecutedMessage", logs: logs, sub: sub}, nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) WatchSkippedAlreadyExecutedMessage(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampSkippedAlreadyExecutedMessage) (event.Subscription, error) { + + logs, sub, err := _EVM2EVMMultiOffRamp.contract.WatchLogs(opts, "SkippedAlreadyExecutedMessage") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(EVM2EVMMultiOffRampSkippedAlreadyExecutedMessage) + if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "SkippedAlreadyExecutedMessage", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) ParseSkippedAlreadyExecutedMessage(log types.Log) (*EVM2EVMMultiOffRampSkippedAlreadyExecutedMessage, error) { + event := new(EVM2EVMMultiOffRampSkippedAlreadyExecutedMessage) + if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "SkippedAlreadyExecutedMessage", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type EVM2EVMMultiOffRampSourceChainConfigSetIterator struct { + Event *EVM2EVMMultiOffRampSourceChainConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *EVM2EVMMultiOffRampSourceChainConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOffRampSourceChainConfigSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOffRampSourceChainConfigSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *EVM2EVMMultiOffRampSourceChainConfigSetIterator) Error() error { + return it.fail +} + +func (it *EVM2EVMMultiOffRampSourceChainConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type EVM2EVMMultiOffRampSourceChainConfigSet struct { + SourceChainSelector uint64 + SourceConfig EVM2EVMMultiOffRampSourceChainConfig + Raw types.Log +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) FilterSourceChainConfigSet(opts *bind.FilterOpts, sourceChainSelector []uint64) (*EVM2EVMMultiOffRampSourceChainConfigSetIterator, error) { + + var sourceChainSelectorRule []interface{} + for _, sourceChainSelectorItem := range sourceChainSelector { + sourceChainSelectorRule = append(sourceChainSelectorRule, sourceChainSelectorItem) + } + + logs, sub, err := _EVM2EVMMultiOffRamp.contract.FilterLogs(opts, "SourceChainConfigSet", sourceChainSelectorRule) + if err != nil { + return nil, err + } + return &EVM2EVMMultiOffRampSourceChainConfigSetIterator{contract: _EVM2EVMMultiOffRamp.contract, event: "SourceChainConfigSet", logs: logs, sub: sub}, nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) WatchSourceChainConfigSet(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampSourceChainConfigSet, sourceChainSelector []uint64) (event.Subscription, error) { + + var sourceChainSelectorRule []interface{} + for _, sourceChainSelectorItem := range sourceChainSelector { + sourceChainSelectorRule = append(sourceChainSelectorRule, sourceChainSelectorItem) + } + + logs, sub, err := _EVM2EVMMultiOffRamp.contract.WatchLogs(opts, "SourceChainConfigSet", sourceChainSelectorRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(EVM2EVMMultiOffRampSourceChainConfigSet) + if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "SourceChainConfigSet", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) ParseSourceChainConfigSet(log types.Log) (*EVM2EVMMultiOffRampSourceChainConfigSet, error) { + event := new(EVM2EVMMultiOffRampSourceChainConfigSet) + if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "SourceChainConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type EVM2EVMMultiOffRampSourceChainSelectorAddedIterator struct { + Event *EVM2EVMMultiOffRampSourceChainSelectorAdded + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *EVM2EVMMultiOffRampSourceChainSelectorAddedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOffRampSourceChainSelectorAdded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOffRampSourceChainSelectorAdded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *EVM2EVMMultiOffRampSourceChainSelectorAddedIterator) Error() error { + return it.fail +} + +func (it *EVM2EVMMultiOffRampSourceChainSelectorAddedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type EVM2EVMMultiOffRampSourceChainSelectorAdded struct { + SourceChainSelector uint64 + Raw types.Log +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) FilterSourceChainSelectorAdded(opts *bind.FilterOpts) (*EVM2EVMMultiOffRampSourceChainSelectorAddedIterator, error) { + + logs, sub, err := _EVM2EVMMultiOffRamp.contract.FilterLogs(opts, "SourceChainSelectorAdded") + if err != nil { + return nil, err + } + return &EVM2EVMMultiOffRampSourceChainSelectorAddedIterator{contract: _EVM2EVMMultiOffRamp.contract, event: "SourceChainSelectorAdded", logs: logs, sub: sub}, nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) WatchSourceChainSelectorAdded(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampSourceChainSelectorAdded) (event.Subscription, error) { + + logs, sub, err := _EVM2EVMMultiOffRamp.contract.WatchLogs(opts, "SourceChainSelectorAdded") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(EVM2EVMMultiOffRampSourceChainSelectorAdded) + if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "SourceChainSelectorAdded", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) ParseSourceChainSelectorAdded(log types.Log) (*EVM2EVMMultiOffRampSourceChainSelectorAdded, error) { + event := new(EVM2EVMMultiOffRampSourceChainSelectorAdded) + if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "SourceChainSelectorAdded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type EVM2EVMMultiOffRampStaticConfigSetIterator struct { + Event *EVM2EVMMultiOffRampStaticConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *EVM2EVMMultiOffRampStaticConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOffRampStaticConfigSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOffRampStaticConfigSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *EVM2EVMMultiOffRampStaticConfigSetIterator) Error() error { + return it.fail +} + +func (it *EVM2EVMMultiOffRampStaticConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type EVM2EVMMultiOffRampStaticConfigSet struct { + StaticConfig EVM2EVMMultiOffRampStaticConfig + Raw types.Log +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) FilterStaticConfigSet(opts *bind.FilterOpts) (*EVM2EVMMultiOffRampStaticConfigSetIterator, error) { + + logs, sub, err := _EVM2EVMMultiOffRamp.contract.FilterLogs(opts, "StaticConfigSet") + if err != nil { + return nil, err + } + return &EVM2EVMMultiOffRampStaticConfigSetIterator{contract: _EVM2EVMMultiOffRamp.contract, event: "StaticConfigSet", logs: logs, sub: sub}, nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) WatchStaticConfigSet(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampStaticConfigSet) (event.Subscription, error) { + + logs, sub, err := _EVM2EVMMultiOffRamp.contract.WatchLogs(opts, "StaticConfigSet") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(EVM2EVMMultiOffRampStaticConfigSet) + if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "StaticConfigSet", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) ParseStaticConfigSet(log types.Log) (*EVM2EVMMultiOffRampStaticConfigSet, error) { + event := new(EVM2EVMMultiOffRampStaticConfigSet) + if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "StaticConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type EVM2EVMMultiOffRampTransmittedIterator struct { + Event *EVM2EVMMultiOffRampTransmitted + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *EVM2EVMMultiOffRampTransmittedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOffRampTransmitted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOffRampTransmitted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *EVM2EVMMultiOffRampTransmittedIterator) Error() error { + return it.fail +} + +func (it *EVM2EVMMultiOffRampTransmittedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type EVM2EVMMultiOffRampTransmitted struct { + OcrPluginType uint8 + ConfigDigest [32]byte + SequenceNumber uint64 + Raw types.Log +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) FilterTransmitted(opts *bind.FilterOpts, ocrPluginType []uint8) (*EVM2EVMMultiOffRampTransmittedIterator, error) { + + var ocrPluginTypeRule []interface{} + for _, ocrPluginTypeItem := range ocrPluginType { + ocrPluginTypeRule = append(ocrPluginTypeRule, ocrPluginTypeItem) + } + + logs, sub, err := _EVM2EVMMultiOffRamp.contract.FilterLogs(opts, "Transmitted", ocrPluginTypeRule) + if err != nil { + return nil, err + } + return &EVM2EVMMultiOffRampTransmittedIterator{contract: _EVM2EVMMultiOffRamp.contract, event: "Transmitted", logs: logs, sub: sub}, nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) WatchTransmitted(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampTransmitted, ocrPluginType []uint8) (event.Subscription, error) { + + var ocrPluginTypeRule []interface{} + for _, ocrPluginTypeItem := range ocrPluginType { + ocrPluginTypeRule = append(ocrPluginTypeRule, ocrPluginTypeItem) + } + + logs, sub, err := _EVM2EVMMultiOffRamp.contract.WatchLogs(opts, "Transmitted", ocrPluginTypeRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(EVM2EVMMultiOffRampTransmitted) + if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "Transmitted", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRampFilterer) ParseTransmitted(log types.Log) (*EVM2EVMMultiOffRampTransmitted, error) { + event := new(EVM2EVMMultiOffRampTransmitted) + if err := _EVM2EVMMultiOffRamp.contract.UnpackLog(event, "Transmitted", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRamp) ParseLog(log types.Log) (generated.AbigenLog, error) { + switch log.Topics[0] { + case _EVM2EVMMultiOffRamp.abi.Events["AlreadyAttempted"].ID: + return _EVM2EVMMultiOffRamp.ParseAlreadyAttempted(log) + case _EVM2EVMMultiOffRamp.abi.Events["CommitReportAccepted"].ID: + return _EVM2EVMMultiOffRamp.ParseCommitReportAccepted(log) + case _EVM2EVMMultiOffRamp.abi.Events["ConfigSet"].ID: + return _EVM2EVMMultiOffRamp.ParseConfigSet(log) + case _EVM2EVMMultiOffRamp.abi.Events["DynamicConfigSet"].ID: + return _EVM2EVMMultiOffRamp.ParseDynamicConfigSet(log) + case _EVM2EVMMultiOffRamp.abi.Events["ExecutionStateChanged"].ID: + return _EVM2EVMMultiOffRamp.ParseExecutionStateChanged(log) + case _EVM2EVMMultiOffRamp.abi.Events["OwnershipTransferRequested"].ID: + return _EVM2EVMMultiOffRamp.ParseOwnershipTransferRequested(log) + case _EVM2EVMMultiOffRamp.abi.Events["OwnershipTransferred"].ID: + return _EVM2EVMMultiOffRamp.ParseOwnershipTransferred(log) + case _EVM2EVMMultiOffRamp.abi.Events["RootRemoved"].ID: + return _EVM2EVMMultiOffRamp.ParseRootRemoved(log) + case _EVM2EVMMultiOffRamp.abi.Events["SkippedAlreadyExecutedMessage"].ID: + return _EVM2EVMMultiOffRamp.ParseSkippedAlreadyExecutedMessage(log) + case _EVM2EVMMultiOffRamp.abi.Events["SourceChainConfigSet"].ID: + return _EVM2EVMMultiOffRamp.ParseSourceChainConfigSet(log) + case _EVM2EVMMultiOffRamp.abi.Events["SourceChainSelectorAdded"].ID: + return _EVM2EVMMultiOffRamp.ParseSourceChainSelectorAdded(log) + case _EVM2EVMMultiOffRamp.abi.Events["StaticConfigSet"].ID: + return _EVM2EVMMultiOffRamp.ParseStaticConfigSet(log) + case _EVM2EVMMultiOffRamp.abi.Events["Transmitted"].ID: + return _EVM2EVMMultiOffRamp.ParseTransmitted(log) + + default: + return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) + } +} + +func (EVM2EVMMultiOffRampAlreadyAttempted) Topic() common.Hash { + return common.HexToHash("0x3ef2a99c550a751d4b0b261268f05a803dfb049ab43616a1ffb388f61fe65120") +} + +func (EVM2EVMMultiOffRampCommitReportAccepted) Topic() common.Hash { + return common.HexToHash("0x3a3950e13dd607cc37980db0ef14266c40d2bba9c01b2e44bfe549808883095d") +} + +func (EVM2EVMMultiOffRampConfigSet) Topic() common.Hash { + return common.HexToHash("0xab8b1b57514019638d7b5ce9c638fe71366fe8e2be1c40a7a80f1733d0e9f547") +} + +func (EVM2EVMMultiOffRampDynamicConfigSet) Topic() common.Hash { + return common.HexToHash("0xa55bd56595c45f517e5967a3067f3dca684445a3080e7c04a4e0d5a40cda627d") +} + +func (EVM2EVMMultiOffRampExecutionStateChanged) Topic() common.Hash { + return common.HexToHash("0x8c324ce1367b83031769f6a813e3bb4c117aba2185789d66b98b791405be6df2") +} + +func (EVM2EVMMultiOffRampOwnershipTransferRequested) Topic() common.Hash { + return common.HexToHash("0xed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278") +} + +func (EVM2EVMMultiOffRampOwnershipTransferred) Topic() common.Hash { + return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") +} + +func (EVM2EVMMultiOffRampRootRemoved) Topic() common.Hash { + return common.HexToHash("0x202f1139a3e334b6056064c0e9b19fd07e44a88d8f6e5ded571b24cf8c371f12") +} + +func (EVM2EVMMultiOffRampSkippedAlreadyExecutedMessage) Topic() common.Hash { + return common.HexToHash("0x3b575419319662b2a6f5e2467d84521517a3382b908eb3d557bb3fdb0c50e23c") +} + +func (EVM2EVMMultiOffRampSourceChainConfigSet) Topic() common.Hash { + return common.HexToHash("0x49f51971edd25182e97182d6ea372a0488ce2ab639f6a3a7ab4df0d2636fe56b") +} + +func (EVM2EVMMultiOffRampSourceChainSelectorAdded) Topic() common.Hash { + return common.HexToHash("0xf4c1390c70e5c0f491ae1ccbc06f9117cbbadf2767b247b3bc203280f24c0fb9") +} + +func (EVM2EVMMultiOffRampStaticConfigSet) Topic() common.Hash { + return common.HexToHash("0x683eb52ee924eb817377cfa8f41f238f4bb7a877da5267869dfffbad85f564d8") +} + +func (EVM2EVMMultiOffRampTransmitted) Topic() common.Hash { + return common.HexToHash("0x198d6990ef96613a9026203077e422916918b03ff47f0be6bee7b02d8e139ef0") +} + +func (_EVM2EVMMultiOffRamp *EVM2EVMMultiOffRamp) Address() common.Address { + return _EVM2EVMMultiOffRamp.address +} + +type EVM2EVMMultiOffRampInterface interface { + CcipReceive(opts *bind.CallOpts, arg0 ClientAny2EVMMessage) error + + GetDynamicConfig(opts *bind.CallOpts) (EVM2EVMMultiOffRampDynamicConfig, error) + + GetExecutionState(opts *bind.CallOpts, sourceChainSelector uint64, sequenceNumber uint64) (uint8, error) + + GetLatestPriceSequenceNumber(opts *bind.CallOpts) (uint64, error) + + GetMerkleRoot(opts *bind.CallOpts, sourceChainSelector uint64, root [32]byte) (*big.Int, error) + + GetSourceChainConfig(opts *bind.CallOpts, sourceChainSelector uint64) (EVM2EVMMultiOffRampSourceChainConfig, error) + + GetStaticConfig(opts *bind.CallOpts) (EVM2EVMMultiOffRampStaticConfig, error) + + IsBlessed(opts *bind.CallOpts, root [32]byte) (bool, error) + + LatestConfigDetails(opts *bind.CallOpts, ocrPluginType uint8) (MultiOCR3BaseOCRConfig, error) + + Owner(opts *bind.CallOpts) (common.Address, error) + + TypeAndVersion(opts *bind.CallOpts) (string, error) + + AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) + + ApplySourceChainConfigUpdates(opts *bind.TransactOpts, sourceChainConfigUpdates []EVM2EVMMultiOffRampSourceChainConfigArgs) (*types.Transaction, error) + + Commit(opts *bind.TransactOpts, reportContext [3][32]byte, report []byte, rs [][32]byte, ss [][32]byte, rawVs [32]byte) (*types.Transaction, error) + + Execute(opts *bind.TransactOpts, reportContext [3][32]byte, report []byte) (*types.Transaction, error) + + ExecuteSingleMessage(opts *bind.TransactOpts, message InternalAny2EVMRampMessage, offchainTokenData [][]byte) (*types.Transaction, error) + + ManuallyExecute(opts *bind.TransactOpts, reports []InternalExecutionReportSingleChain, gasLimitOverrides [][]*big.Int) (*types.Transaction, error) + + ResetUnblessedRoots(opts *bind.TransactOpts, rootToReset []EVM2EVMMultiOffRampUnblessedRoot) (*types.Transaction, error) + + SetDynamicConfig(opts *bind.TransactOpts, dynamicConfig EVM2EVMMultiOffRampDynamicConfig) (*types.Transaction, error) + + SetOCR3Configs(opts *bind.TransactOpts, ocrConfigArgs []MultiOCR3BaseOCRConfigArgs) (*types.Transaction, error) + + TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + + FilterAlreadyAttempted(opts *bind.FilterOpts) (*EVM2EVMMultiOffRampAlreadyAttemptedIterator, error) + + WatchAlreadyAttempted(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampAlreadyAttempted) (event.Subscription, error) + + ParseAlreadyAttempted(log types.Log) (*EVM2EVMMultiOffRampAlreadyAttempted, error) + + FilterCommitReportAccepted(opts *bind.FilterOpts) (*EVM2EVMMultiOffRampCommitReportAcceptedIterator, error) + + WatchCommitReportAccepted(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampCommitReportAccepted) (event.Subscription, error) + + ParseCommitReportAccepted(log types.Log) (*EVM2EVMMultiOffRampCommitReportAccepted, error) + + FilterConfigSet(opts *bind.FilterOpts) (*EVM2EVMMultiOffRampConfigSetIterator, error) + + WatchConfigSet(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampConfigSet) (event.Subscription, error) + + ParseConfigSet(log types.Log) (*EVM2EVMMultiOffRampConfigSet, error) + + FilterDynamicConfigSet(opts *bind.FilterOpts) (*EVM2EVMMultiOffRampDynamicConfigSetIterator, error) + + WatchDynamicConfigSet(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampDynamicConfigSet) (event.Subscription, error) + + ParseDynamicConfigSet(log types.Log) (*EVM2EVMMultiOffRampDynamicConfigSet, error) + + FilterExecutionStateChanged(opts *bind.FilterOpts, sourceChainSelector []uint64, sequenceNumber []uint64, messageId [][32]byte) (*EVM2EVMMultiOffRampExecutionStateChangedIterator, error) + + WatchExecutionStateChanged(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampExecutionStateChanged, sourceChainSelector []uint64, sequenceNumber []uint64, messageId [][32]byte) (event.Subscription, error) + + ParseExecutionStateChanged(log types.Log) (*EVM2EVMMultiOffRampExecutionStateChanged, error) + + FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*EVM2EVMMultiOffRampOwnershipTransferRequestedIterator, error) + + WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferRequested(log types.Log) (*EVM2EVMMultiOffRampOwnershipTransferRequested, error) + + FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*EVM2EVMMultiOffRampOwnershipTransferredIterator, error) + + WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferred(log types.Log) (*EVM2EVMMultiOffRampOwnershipTransferred, error) + + FilterRootRemoved(opts *bind.FilterOpts) (*EVM2EVMMultiOffRampRootRemovedIterator, error) + + WatchRootRemoved(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampRootRemoved) (event.Subscription, error) + + ParseRootRemoved(log types.Log) (*EVM2EVMMultiOffRampRootRemoved, error) + + FilterSkippedAlreadyExecutedMessage(opts *bind.FilterOpts) (*EVM2EVMMultiOffRampSkippedAlreadyExecutedMessageIterator, error) + + WatchSkippedAlreadyExecutedMessage(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampSkippedAlreadyExecutedMessage) (event.Subscription, error) + + ParseSkippedAlreadyExecutedMessage(log types.Log) (*EVM2EVMMultiOffRampSkippedAlreadyExecutedMessage, error) + + FilterSourceChainConfigSet(opts *bind.FilterOpts, sourceChainSelector []uint64) (*EVM2EVMMultiOffRampSourceChainConfigSetIterator, error) + + WatchSourceChainConfigSet(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampSourceChainConfigSet, sourceChainSelector []uint64) (event.Subscription, error) + + ParseSourceChainConfigSet(log types.Log) (*EVM2EVMMultiOffRampSourceChainConfigSet, error) + + FilterSourceChainSelectorAdded(opts *bind.FilterOpts) (*EVM2EVMMultiOffRampSourceChainSelectorAddedIterator, error) + + WatchSourceChainSelectorAdded(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampSourceChainSelectorAdded) (event.Subscription, error) + + ParseSourceChainSelectorAdded(log types.Log) (*EVM2EVMMultiOffRampSourceChainSelectorAdded, error) + + FilterStaticConfigSet(opts *bind.FilterOpts) (*EVM2EVMMultiOffRampStaticConfigSetIterator, error) + + WatchStaticConfigSet(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampStaticConfigSet) (event.Subscription, error) + + ParseStaticConfigSet(log types.Log) (*EVM2EVMMultiOffRampStaticConfigSet, error) + + FilterTransmitted(opts *bind.FilterOpts, ocrPluginType []uint8) (*EVM2EVMMultiOffRampTransmittedIterator, error) + + WatchTransmitted(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOffRampTransmitted, ocrPluginType []uint8) (event.Subscription, error) + + ParseTransmitted(log types.Log) (*EVM2EVMMultiOffRampTransmitted, error) + + ParseLog(log types.Log) (generated.AbigenLog, error) + + Address() common.Address +} diff --git a/core/gethwrappers/ccip/generated/evm_2_evm_multi_onramp/evm_2_evm_multi_onramp.go b/core/gethwrappers/ccip/generated/evm_2_evm_multi_onramp/evm_2_evm_multi_onramp.go new file mode 100644 index 0000000000..b3cbed0be5 --- /dev/null +++ b/core/gethwrappers/ccip/generated/evm_2_evm_multi_onramp/evm_2_evm_multi_onramp.go @@ -0,0 +1,1547 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package evm_2_evm_multi_onramp + +import ( + "errors" + "fmt" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" +) + +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +type ClientEVM2AnyMessage struct { + Receiver []byte + Data []byte + TokenAmounts []ClientEVMTokenAmount + FeeToken common.Address + ExtraArgs []byte +} + +type ClientEVMTokenAmount struct { + Token common.Address + Amount *big.Int +} + +type EVM2EVMMultiOnRampDestChainConfig struct { + SequenceNumber uint64 + Router common.Address +} + +type EVM2EVMMultiOnRampDestChainConfigArgs struct { + DestChainSelector uint64 + Router common.Address +} + +type EVM2EVMMultiOnRampDynamicConfig struct { + PriceRegistry common.Address + MessageValidator common.Address + FeeAggregator common.Address +} + +type EVM2EVMMultiOnRampStaticConfig struct { + ChainSelector uint64 + RmnProxy common.Address + NonceManager common.Address + TokenAdminRegistry common.Address +} + +type InternalEVM2AnyRampMessage struct { + Header InternalRampMessageHeader + Sender common.Address + Data []byte + Receiver []byte + ExtraArgs []byte + FeeToken common.Address + FeeTokenAmount *big.Int + TokenAmounts []InternalRampTokenAmount +} + +type InternalRampMessageHeader struct { + MessageId [32]byte + SourceChainSelector uint64 + DestChainSelector uint64 + SequenceNumber uint64 + Nonce uint64 +} + +type InternalRampTokenAmount struct { + SourcePoolAddress []byte + DestTokenAddress []byte + ExtraData []byte + Amount *big.Int +} + +var EVM2EVMMultiOnRampMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.StaticConfig\",\"name\":\"staticConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeAggregator\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"contractIRouter\",\"name\":\"router\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainConfigArgs[]\",\"name\":\"destChainConfigArgs\",\"type\":\"tuple[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"CannotSendZeroTokens\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"CursedByRMN\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GetSupportedTokensFunctionalityRemovedCheckAdminRegistry\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"}],\"name\":\"InvalidDestChainConfig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MustBeCalledByRouter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RouterMustSetOriginalSender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"UnsupportedToken\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"messageId\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"sourceChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nonce\",\"type\":\"uint64\"}],\"internalType\":\"structInternal.RampMessageHeader\",\"name\":\"header\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"feeTokenAmount\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"destTokenAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structInternal.RampTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"}],\"indexed\":false,\"internalType\":\"structInternal.EVM2AnyRampMessage\",\"name\":\"message\",\"type\":\"tuple\"}],\"name\":\"CCIPSendRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOnRamp.StaticConfig\",\"name\":\"staticConfig\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeAggregator\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOnRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"sequenceNumber\",\"type\":\"uint64\"},{\"internalType\":\"contractIRouter\",\"name\":\"router\",\"type\":\"address\"}],\"indexed\":false,\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainConfig\",\"name\":\"destChainConfig\",\"type\":\"tuple\"}],\"name\":\"DestChainConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"feeValueJuels\",\"type\":\"uint256\"}],\"name\":\"FeePaid\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"feeAggregator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"FeeTokenWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"contractIRouter\",\"name\":\"router\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DestChainConfigArgs[]\",\"name\":\"destChainConfigArgs\",\"type\":\"tuple[]\"}],\"name\":\"applyDestChainConfigUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"},{\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"}],\"internalType\":\"structClient.EVM2AnyMessage\",\"name\":\"message\",\"type\":\"tuple\"},{\"internalType\":\"uint256\",\"name\":\"feeTokenAmount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"originalSender\",\"type\":\"address\"}],\"name\":\"forwardFromRouter\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDynamicConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeAggregator\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"}],\"name\":\"getExpectedNextSequenceNumber\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"},{\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"}],\"internalType\":\"structClient.EVM2AnyMessage\",\"name\":\"message\",\"type\":\"tuple\"}],\"name\":\"getFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"feeTokenAmount\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"},{\"internalType\":\"contractIERC20\",\"name\":\"sourceToken\",\"type\":\"address\"}],\"name\":\"getPoolBySourceToken\",\"outputs\":[{\"internalType\":\"contractIPoolV1\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"}],\"name\":\"getRouter\",\"outputs\":[{\"internalType\":\"contractIRouter\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStaticConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"rmnProxy\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nonceManager\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenAdminRegistry\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.StaticConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"name\":\"getSupportedTokens\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"priceRegistry\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"messageValidator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeAggregator\",\"type\":\"address\"}],\"internalType\":\"structEVM2EVMMultiOnRamp.DynamicConfig\",\"name\":\"dynamicConfig\",\"type\":\"tuple\"}],\"name\":\"setDynamicConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawFeeTokens\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x6101006040523480156200001257600080fd5b50604051620036af380380620036af83398101604081905262000035916200069c565b33806000816200008c5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000bf57620000bf8162000186565b505083516001600160401b031615905080620000e6575060208301516001600160a01b0316155b80620000fd575060408301516001600160a01b0316155b8062000114575060608301516001600160a01b0316155b1562000133576040516306b7c75960e31b815260040160405180910390fd5b82516001600160401b031660805260208301516001600160a01b0390811660a0526040840151811660c05260608401511660e052620001728262000231565b6200017d816200036d565b5050506200079c565b336001600160a01b03821603620001e05760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000083565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b80516001600160a01b0316158062000254575060408101516001600160a01b0316155b1562000273576040516306b7c75960e31b815260040160405180910390fd5b8051600280546001600160a01b03199081166001600160a01b03938416179091556020808401805160038054851691861691909117905560408086018051600480549096169087161790945580516080808201835280516001600160401b031680835260a08051891684880190815260c080518b1686880190815260e080518d166060988901908152895196875293518d169a86019a909a52518b169684019690965251891693820193909352885188169181019190915292518616908301529251909316918301919091527f2d8f19dc1cd01460c3367a09d2d424f2b1940ba7c886047edd078c7b77ea4558910160405180910390a150565b60005b8151811015620004aa57600082828151811062000391576200039162000786565b602002602001015190506000838381518110620003b257620003b262000786565b6020026020010151600001519050806001600160401b0316600003620003f75760405163c35aa79d60e01b81526001600160401b038216600482015260240162000083565b6040805180820182526001600160401b03838116600081815260056020818152868320805480871688528a8301516001600160a01b03908116848a019081529587905293835287518551851668010000000000000000026001600160e01b0319909216971696871717905586519485529151169083015291927f324d4a7950b57da00ce533ad6697039be6281726a73da959d0ab3ff795181ec6910160405180910390a250505080600101905062000370565b5050565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b0381118282101715620004e957620004e9620004ae565b60405290565b604051601f8201601f191681016001600160401b03811182821017156200051a576200051a620004ae565b604052919050565b80516001600160401b03811681146200053a57600080fd5b919050565b6001600160a01b03811681146200055557600080fd5b50565b6000606082840312156200056b57600080fd5b604051606081016001600160401b0381118282101715620005905762000590620004ae565b80604052508091508251620005a5816200053f565b81526020830151620005b7816200053f565b60208201526040830151620005cc816200053f565b6040919091015292915050565b600082601f830112620005eb57600080fd5b815160206001600160401b03821115620006095762000609620004ae565b62000619818360051b01620004ef565b82815260069290921b840181019181810190868411156200063957600080fd5b8286015b84811015620006915760408189031215620006585760008081fd5b62000662620004c4565b6200066d8262000522565b8152848201516200067e816200053f565b818601528352918301916040016200063d565b509695505050505050565b6000806000838503610100811215620006b457600080fd5b6080811215620006c357600080fd5b50604051608081016001600160401b038082118383101715620006ea57620006ea620004ae565b81604052620006f98762000522565b8352602087015191506200070d826200053f565b8160208401526040870151915062000725826200053f565b816040840152606087015191506200073d826200053f565b81606084015282955062000755886080890162000558565b945060e08701519250808311156200076c57600080fd5b50506200077c86828701620005d9565b9150509250925092565b634e487b7160e01b600052603260045260246000fd5b60805160a05160c05160e051612e9a62000815600039600081816101eb015281816108a201526115da0152600081816101af01528181610dc601526115b3015260008181610173015281816104c4015261158901526000818161014301528181610cec0152818161117c015261155c0152612e9a6000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c80637437ff9f11610097578063d77d5ed011610066578063d77d5ed0146103ba578063df0aa9e914610406578063f2fde38b14610419578063fbca3b741461042c57600080fd5b80637437ff9f146102fb57806379ba5097146103685780638da5cb5b146103705780639041be3d1461038e57600080fd5b806320487ded116100d357806320487ded1461028757806334d560e4146102a85780633a019940146102bb57806348a98aa4146102c357600080fd5b80630242cf60146100fa57806306285c691461010f578063181f5a771461023e575b600080fd5b61010d610108366004611fe6565b61044c565b005b61022860408051608081018252600080825260208201819052918101829052606081019190915260405180608001604052807f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1681526020017f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1681526020017f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1681526020017f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16815250905090565b60405161023591906120a9565b60405180910390f35b61027a6040518060400160405280601c81526020017f45564d3245564d4d756c74694f6e52616d7020312e362e302d6465760000000081525081565b604051610235919061216e565b61029a610295366004612199565b610460565b604051908152602001610235565b61010d6102b63660046121f9565b610619565b61010d61062a565b6102d66102d136600461226b565b61085a565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610235565b61035b6040805160608101825260008082526020820181905291810191909152506040805160608101825260025473ffffffffffffffffffffffffffffffffffffffff908116825260035481166020830152600454169181019190915290565b60405161023591906122a4565b61010d61090f565b60005473ffffffffffffffffffffffffffffffffffffffff166102d6565b6103a161039c3660046122e1565b610a0c565b60405167ffffffffffffffff9091168152602001610235565b6102d66103c83660046122e1565b67ffffffffffffffff1660009081526005602052604090205468010000000000000000900473ffffffffffffffffffffffffffffffffffffffff1690565b61029a6104143660046122fe565b610a35565b61010d61042736600461236a565b611230565b61043f61043a3660046122e1565b611241565b6040516102359190612387565b610454611275565b61045d816112f8565b50565b6040517f2cbc26bb00000000000000000000000000000000000000000000000000000000815277ffffffffffffffff00000000000000000000000000000000608084901b16600482015260009073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690632cbc26bb90602401602060405180830381865afa15801561050b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061052f91906123f1565b15610577576040517ffdbd6a7200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024015b60405180910390fd5b6002546040517fd8694ccd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091169063d8694ccd906105cf9086908690600401612520565b602060405180830381865afa1580156105ec573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106109190612669565b90505b92915050565b610621611275565b61045d81611470565b600254604080517fcdc73d51000000000000000000000000000000000000000000000000000000008152905160009273ffffffffffffffffffffffffffffffffffffffff169163cdc73d5191600480830192869291908290030181865afa158015610699573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526106df9190810190612682565b60045490915073ffffffffffffffffffffffffffffffffffffffff1660005b825181101561085557600083828151811061071b5761071b612711565b60209081029190910101516040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290915060009073ffffffffffffffffffffffffffffffffffffffff8316906370a0823190602401602060405180830381865afa158015610796573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107ba9190612669565b9050801561084b576107e373ffffffffffffffffffffffffffffffffffffffff8316858361163a565b8173ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f508d7d183612c18fc339b42618912b9fa3239f631dd7ec0671f950200a0fa66e8360405161084291815260200190565b60405180910390a35b50506001016106fe565b505050565b6040517fbbe4f6db00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063bbe4f6db90602401602060405180830381865afa1580156108eb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106109190612740565b60015473ffffffffffffffffffffffffffffffffffffffff163314610990576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e657200000000000000000000604482015260640161056e565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b67ffffffffffffffff80821660009081526005602052604081205490916106139116600161278c565b67ffffffffffffffff8416600090815260056020526040812073ffffffffffffffffffffffffffffffffffffffff8316610a9b576040517fa4ec747900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805468010000000000000000900473ffffffffffffffffffffffffffffffffffffffff163314610af7576040517f1c0a352900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60035473ffffffffffffffffffffffffffffffffffffffff168015610b9d576040517fe0a0e50600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82169063e0a0e50690610b6a908a908a90600401612520565b600060405180830381600087803b158015610b8457600080fd5b505af1158015610b98573d6000803e3d6000fd5b505050505b506002546000908190819073ffffffffffffffffffffffffffffffffffffffff1663c4276bfc8a610bd460808c0160608d0161236a565b8a610be260808e018e6127b4565b6040518663ffffffff1660e01b8152600401610c02959493929190612819565b600060405180830381865afa158015610c1f573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610c6591908101906128e1565b91945092509050610c7c6080890160608a0161236a565b73ffffffffffffffffffffffffffffffffffffffff167f075a2720282fdf622141dae0b048ef90a21a7e57c134c76912d19d006b3b3f6f84604051610cc391815260200190565b60405180910390a2604080516101a081019091526000610100820181815267ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000081166101208501528c811661014085015287549293928392916101608401918a918791610d389116612938565b91906101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905567ffffffffffffffff16815260200186610e38576040517fea458c0c00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8f16600482015273ffffffffffffffffffffffffffffffffffffffff8c811660248301527f0000000000000000000000000000000000000000000000000000000000000000169063ea458c0c906044016020604051808303816000875af1158015610e0f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e33919061295f565b610e3b565b60005b67ffffffffffffffff1681525081526020018873ffffffffffffffffffffffffffffffffffffffff1681526020018a8060200190610e7991906127b4565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250505090825250602001610ebd8b806127b4565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250505090825250602001610f0460808c018c6127b4565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250505090825250602001610f4e60808c0160608d0161236a565b73ffffffffffffffffffffffffffffffffffffffff1681526020018981526020018a8060400190610f7f919061297c565b905067ffffffffffffffff811115610f9957610f99611ee3565b604051908082528060200260200182016040528015610ff557816020015b610fe26040518060800160405280606081526020016060815260200160608152602001600081525090565b815260200190600190039081610fb75790505b509052905060005b61100a60408b018b61297c565b90508110156110b95761109061102360408c018c61297c565b8381811061103357611033612711565b90506040020180360381019061104991906129e4565b8c6110548d806127b4565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508e92506116c7915050565b8260e0015182815181106110a6576110a6612711565b6020908102919091010152600101610ffd565b5060025460e082015173ffffffffffffffffffffffffffffffffffffffff9091169063cc88924c908c906110f060408e018e61297c565b6040518563ffffffff1660e01b815260040161110f9493929190612ae0565b60006040518083038186803b15801561112757600080fd5b505afa15801561113b573d6000803e3d6000fd5b505050506080808201839052604080517f130ac867e79e2789f923760a88743d292acdf7002139a588206e2260f73f7321602082015267ffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811692820192909252908c16606082015230918101919091526111d890829060a001604051602081830303815290604052805190602001206119d1565b81515260405167ffffffffffffffff8b16907f0f07cd31e53232da9125e517f09550fdde74bf43d6a0a76ebd41674dafe2ab2990611217908490612b16565b60405180910390a251519450505050505b949350505050565b611238611275565b61045d81611ad1565b60606040517f9e7177c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005473ffffffffffffffffffffffffffffffffffffffff1633146112f6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640161056e565b565b60005b815181101561146c57600082828151811061131857611318612711565b60200260200101519050600083838151811061133657611336612711565b60200260200101516000015190508067ffffffffffffffff16600003611394576040517fc35aa79d00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8216600482015260240161056e565b60408051808201825267ffffffffffffffff838116600081815260056020818152868320805480871688528a83015173ffffffffffffffffffffffffffffffffffffffff908116848a019081529587905293835287518551851668010000000000000000027fffffffff00000000000000000000000000000000000000000000000000000000909216971696871717905586519485529151169083015291927f324d4a7950b57da00ce533ad6697039be6281726a73da959d0ab3ff795181ec6910160405180910390a25050508060010190506112fb565b5050565b805173ffffffffffffffffffffffffffffffffffffffff1615806114ac5750604081015173ffffffffffffffffffffffffffffffffffffffff16155b156114e3576040517f35be3ac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8051600280547fffffffffffffffffffffffff000000000000000000000000000000000000000090811673ffffffffffffffffffffffffffffffffffffffff93841617909155602080840151600380548416918516919091179055604080850151600480549094169085161790925581516080810183527f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1681527f00000000000000000000000000000000000000000000000000000000000000008416918101919091527f00000000000000000000000000000000000000000000000000000000000000008316818301527f00000000000000000000000000000000000000000000000000000000000000009092166060830152517f2d8f19dc1cd01460c3367a09d2d424f2b1940ba7c886047edd078c7b77ea45589161162f918490612c64565b60405180910390a150565b6040805173ffffffffffffffffffffffffffffffffffffffff8416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb00000000000000000000000000000000000000000000000000000000179052610855908490611bc6565b6116f26040518060800160405280606081526020016060815260200160608152602001600081525090565b8460200151600003611730576040517f5cf0444900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061174085876000015161085a565b905073ffffffffffffffffffffffffffffffffffffffff8116158061181057506040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527faff2afbf00000000000000000000000000000000000000000000000000000000600482015273ffffffffffffffffffffffffffffffffffffffff8216906301ffc9a790602401602060405180830381865afa1580156117ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061180e91906123f1565b155b156118625785516040517fbf16aab600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff909116600482015260240161056e565b60008173ffffffffffffffffffffffffffffffffffffffff16639a4575b96040518060a001604052808881526020018967ffffffffffffffff1681526020018773ffffffffffffffffffffffffffffffffffffffff1681526020018a6020015181526020018a6000015173ffffffffffffffffffffffffffffffffffffffff168152506040518263ffffffff1660e01b81526004016119019190612cf6565b6000604051808303816000875af1158015611920573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526119669190810190612d6c565b604080516080810190915273ffffffffffffffffffffffffffffffffffffffff841660a08201529091508060c0810160405160208183030381529060405281526020018260000151815260200182602001518152602001886020015181525092505050949350505050565b60008060001b82846020015185606001518660000151606001518760000151608001518860a001518960c00151604051602001611a1396959493929190612dfd565b604051602081830303815290604052805190602001208560400151805190602001208660e00151604051602001611a4a9190612e5e565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201206080808c0151805190840120928501989098529183019590955260608201939093529384015260a083015260c082015260e00160405160208183030381529060405280519060200120905092915050565b3373ffffffffffffffffffffffffffffffffffffffff821603611b50576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161056e565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000611c28826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff16611cd29092919063ffffffff16565b8051909150156108555780806020019051810190611c4691906123f1565b610855576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f74207375636365656400000000000000000000000000000000000000000000606482015260840161056e565b6060611ce18484600085611ceb565b90505b9392505050565b606082471015611d7d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c0000000000000000000000000000000000000000000000000000606482015260840161056e565b6000808673ffffffffffffffffffffffffffffffffffffffff168587604051611da69190612e71565b60006040518083038185875af1925050503d8060008114611de3576040519150601f19603f3d011682016040523d82523d6000602084013e611de8565b606091505b5091509150611df987838387611e04565b979650505050505050565b60608315611e9a578251600003611e935773ffffffffffffffffffffffffffffffffffffffff85163b611e93576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161056e565b5081611228565b6112288383815115611eaf5781518083602001fd5b806040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161056e919061216e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715611f3557611f35611ee3565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715611f8257611f82611ee3565b604052919050565b600067ffffffffffffffff821115611fa457611fa4611ee3565b5060051b60200190565b67ffffffffffffffff8116811461045d57600080fd5b73ffffffffffffffffffffffffffffffffffffffff8116811461045d57600080fd5b60006020808385031215611ff957600080fd5b823567ffffffffffffffff81111561201057600080fd5b8301601f8101851361202157600080fd5b803561203461202f82611f8a565b611f3b565b81815260069190911b8201830190838101908783111561205357600080fd5b928401925b82841015611df957604084890312156120715760008081fd5b612079611f12565b843561208481611fae565b81528486013561209381611fc4565b8187015282526040939093019290840190612058565b60808101610613828467ffffffffffffffff8151168252602081015173ffffffffffffffffffffffffffffffffffffffff808216602085015280604084015116604085015280606084015116606085015250505050565b60005b8381101561211b578181015183820152602001612103565b50506000910152565b6000815180845261213c816020860160208601612100565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006106106020830184612124565b600060a0828403121561219357600080fd5b50919050565b600080604083850312156121ac57600080fd5b82356121b781611fae565b9150602083013567ffffffffffffffff8111156121d357600080fd5b6121df85828601612181565b9150509250929050565b80356121f481611fc4565b919050565b60006060828403121561220b57600080fd5b6040516060810181811067ffffffffffffffff8211171561222e5761222e611ee3565b604052823561223c81611fc4565b8152602083013561224c81611fc4565b6020820152604083013561225f81611fc4565b60408201529392505050565b6000806040838503121561227e57600080fd5b823561228981611fae565b9150602083013561229981611fc4565b809150509250929050565b606081016106138284805173ffffffffffffffffffffffffffffffffffffffff908116835260208083015182169084015260409182015116910152565b6000602082840312156122f357600080fd5b8135611ce481611fae565b6000806000806080858703121561231457600080fd5b843561231f81611fae565b9350602085013567ffffffffffffffff81111561233b57600080fd5b61234787828801612181565b93505060408501359150606085013561235f81611fc4565b939692955090935050565b60006020828403121561237c57600080fd5b8135611ce481611fc4565b6020808252825182820181905260009190848201906040850190845b818110156123d557835173ffffffffffffffffffffffffffffffffffffffff16835292840192918401916001016123a3565b50909695505050505050565b805180151581146121f457600080fd5b60006020828403121561240357600080fd5b610610826123e1565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261244157600080fd5b830160208101925035905067ffffffffffffffff81111561246157600080fd5b80360382131561247057600080fd5b9250929050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b8183526000602080850194508260005b858110156125155781356124e381611fc4565b73ffffffffffffffffffffffffffffffffffffffff1687528183013583880152604096870196909101906001016124d0565b509495945050505050565b600067ffffffffffffffff808516835260406020840152612541848561240c565b60a0604086015261255660e086018284612477565b915050612566602086018661240c565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08087850301606088015261259c848385612477565b9350604088013592507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18836030183126125d557600080fd5b602092880192830192359150848211156125ee57600080fd5b8160061b360383131561260057600080fd5b808785030160808801526126158483856124c0565b9450612623606089016121e9565b73ffffffffffffffffffffffffffffffffffffffff811660a0890152935061264e608089018961240c565b94509250808786030160c08801525050611df9838383612477565b60006020828403121561267b57600080fd5b5051919050565b6000602080838503121561269557600080fd5b825167ffffffffffffffff8111156126ac57600080fd5b8301601f810185136126bd57600080fd5b80516126cb61202f82611f8a565b81815260059190911b820183019083810190878311156126ea57600080fd5b928401925b82841015611df957835161270281611fc4565b825292840192908401906126ef565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006020828403121561275257600080fd5b8151611ce481611fc4565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b67ffffffffffffffff8181168382160190808211156127ad576127ad61275d565b5092915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126127e957600080fd5b83018035915067ffffffffffffffff82111561280457600080fd5b60200191503681900382131561247057600080fd5b67ffffffffffffffff8616815273ffffffffffffffffffffffffffffffffffffffff85166020820152836040820152608060608201526000611df9608083018486612477565b600082601f83011261287057600080fd5b815167ffffffffffffffff81111561288a5761288a611ee3565b6128bb60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601611f3b565b8181528460208386010111156128d057600080fd5b611228826020830160208701612100565b6000806000606084860312156128f657600080fd5b83519250612906602085016123e1565b9150604084015167ffffffffffffffff81111561292257600080fd5b61292e8682870161285f565b9150509250925092565b600067ffffffffffffffff8083168181036129555761295561275d565b6001019392505050565b60006020828403121561297157600080fd5b8151611ce481611fae565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126129b157600080fd5b83018035915067ffffffffffffffff8211156129cc57600080fd5b6020019150600681901b360382131561247057600080fd5b6000604082840312156129f657600080fd5b6129fe611f12565b8235612a0981611fc4565b81526020928301359281019290925250919050565b600082825180855260208086019550808260051b84010181860160005b84811015612ad3577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0868403018952815160808151818652612a7f82870182612124565b9150508582015185820387870152612a978282612124565b91505060408083015186830382880152612ab18382612124565b6060948501519790940196909652505098840198925090830190600101612a3b565b5090979650505050505050565b67ffffffffffffffff85168152606060208201526000612b036060830186612a1e565b8281036040840152611df98185876124c0565b60208152612b6760208201835180518252602081015167ffffffffffffffff808216602085015280604084015116604085015280606084015116606085015280608084015116608085015250505050565b60006020830151612b9060c084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060408301516101808060e0850152612bad6101a0850183612124565b915060608501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08086850301610100870152612bea8483612124565b9350608087015191508086850301610120870152612c088483612124565b935060a08701519150612c3461014087018373ffffffffffffffffffffffffffffffffffffffff169052565b60c087015161016087015260e0870151915080868503018387015250612c5a8382612a1e565b9695505050505050565b60e08101612cbb828567ffffffffffffffff8151168252602081015173ffffffffffffffffffffffffffffffffffffffff808216602085015280604084015116604085015280606084015116606085015250505050565b825173ffffffffffffffffffffffffffffffffffffffff90811660808401526020840151811660a084015260408401511660c0830152611ce4565b602081526000825160a06020840152612d1260c0840182612124565b905067ffffffffffffffff6020850151166040840152604084015173ffffffffffffffffffffffffffffffffffffffff8082166060860152606086015160808601528060808701511660a086015250508091505092915050565b600060208284031215612d7e57600080fd5b815167ffffffffffffffff80821115612d9657600080fd5b9083019060408286031215612daa57600080fd5b612db2611f12565b825182811115612dc157600080fd5b612dcd8782860161285f565b825250602083015182811115612de257600080fd5b612dee8782860161285f565b60208301525095945050505050565b600073ffffffffffffffffffffffffffffffffffffffff808916835260c06020840152612e2d60c0840189612124565b67ffffffffffffffff97881660408501529590961660608301525091909316608082015260a0019190915292915050565b6020815260006106106020830184612a1e565b60008251612e83818460208701612100565b919091019291505056fea164736f6c6343000818000a", +} + +var EVM2EVMMultiOnRampABI = EVM2EVMMultiOnRampMetaData.ABI + +var EVM2EVMMultiOnRampBin = EVM2EVMMultiOnRampMetaData.Bin + +func DeployEVM2EVMMultiOnRamp(auth *bind.TransactOpts, backend bind.ContractBackend, staticConfig EVM2EVMMultiOnRampStaticConfig, dynamicConfig EVM2EVMMultiOnRampDynamicConfig, destChainConfigArgs []EVM2EVMMultiOnRampDestChainConfigArgs) (common.Address, *types.Transaction, *EVM2EVMMultiOnRamp, error) { + parsed, err := EVM2EVMMultiOnRampMetaData.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(EVM2EVMMultiOnRampBin), backend, staticConfig, dynamicConfig, destChainConfigArgs) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &EVM2EVMMultiOnRamp{address: address, abi: *parsed, EVM2EVMMultiOnRampCaller: EVM2EVMMultiOnRampCaller{contract: contract}, EVM2EVMMultiOnRampTransactor: EVM2EVMMultiOnRampTransactor{contract: contract}, EVM2EVMMultiOnRampFilterer: EVM2EVMMultiOnRampFilterer{contract: contract}}, nil +} + +type EVM2EVMMultiOnRamp struct { + address common.Address + abi abi.ABI + EVM2EVMMultiOnRampCaller + EVM2EVMMultiOnRampTransactor + EVM2EVMMultiOnRampFilterer +} + +type EVM2EVMMultiOnRampCaller struct { + contract *bind.BoundContract +} + +type EVM2EVMMultiOnRampTransactor struct { + contract *bind.BoundContract +} + +type EVM2EVMMultiOnRampFilterer struct { + contract *bind.BoundContract +} + +type EVM2EVMMultiOnRampSession struct { + Contract *EVM2EVMMultiOnRamp + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type EVM2EVMMultiOnRampCallerSession struct { + Contract *EVM2EVMMultiOnRampCaller + CallOpts bind.CallOpts +} + +type EVM2EVMMultiOnRampTransactorSession struct { + Contract *EVM2EVMMultiOnRampTransactor + TransactOpts bind.TransactOpts +} + +type EVM2EVMMultiOnRampRaw struct { + Contract *EVM2EVMMultiOnRamp +} + +type EVM2EVMMultiOnRampCallerRaw struct { + Contract *EVM2EVMMultiOnRampCaller +} + +type EVM2EVMMultiOnRampTransactorRaw struct { + Contract *EVM2EVMMultiOnRampTransactor +} + +func NewEVM2EVMMultiOnRamp(address common.Address, backend bind.ContractBackend) (*EVM2EVMMultiOnRamp, error) { + abi, err := abi.JSON(strings.NewReader(EVM2EVMMultiOnRampABI)) + if err != nil { + return nil, err + } + contract, err := bindEVM2EVMMultiOnRamp(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &EVM2EVMMultiOnRamp{address: address, abi: abi, EVM2EVMMultiOnRampCaller: EVM2EVMMultiOnRampCaller{contract: contract}, EVM2EVMMultiOnRampTransactor: EVM2EVMMultiOnRampTransactor{contract: contract}, EVM2EVMMultiOnRampFilterer: EVM2EVMMultiOnRampFilterer{contract: contract}}, nil +} + +func NewEVM2EVMMultiOnRampCaller(address common.Address, caller bind.ContractCaller) (*EVM2EVMMultiOnRampCaller, error) { + contract, err := bindEVM2EVMMultiOnRamp(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &EVM2EVMMultiOnRampCaller{contract: contract}, nil +} + +func NewEVM2EVMMultiOnRampTransactor(address common.Address, transactor bind.ContractTransactor) (*EVM2EVMMultiOnRampTransactor, error) { + contract, err := bindEVM2EVMMultiOnRamp(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &EVM2EVMMultiOnRampTransactor{contract: contract}, nil +} + +func NewEVM2EVMMultiOnRampFilterer(address common.Address, filterer bind.ContractFilterer) (*EVM2EVMMultiOnRampFilterer, error) { + contract, err := bindEVM2EVMMultiOnRamp(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &EVM2EVMMultiOnRampFilterer{contract: contract}, nil +} + +func bindEVM2EVMMultiOnRamp(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := EVM2EVMMultiOnRampMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _EVM2EVMMultiOnRamp.Contract.EVM2EVMMultiOnRampCaller.contract.Call(opts, result, method, params...) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _EVM2EVMMultiOnRamp.Contract.EVM2EVMMultiOnRampTransactor.contract.Transfer(opts) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _EVM2EVMMultiOnRamp.Contract.EVM2EVMMultiOnRampTransactor.contract.Transact(opts, method, params...) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _EVM2EVMMultiOnRamp.Contract.contract.Call(opts, result, method, params...) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _EVM2EVMMultiOnRamp.Contract.contract.Transfer(opts) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _EVM2EVMMultiOnRamp.Contract.contract.Transact(opts, method, params...) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampCaller) GetDynamicConfig(opts *bind.CallOpts) (EVM2EVMMultiOnRampDynamicConfig, error) { + var out []interface{} + err := _EVM2EVMMultiOnRamp.contract.Call(opts, &out, "getDynamicConfig") + + if err != nil { + return *new(EVM2EVMMultiOnRampDynamicConfig), err + } + + out0 := *abi.ConvertType(out[0], new(EVM2EVMMultiOnRampDynamicConfig)).(*EVM2EVMMultiOnRampDynamicConfig) + + return out0, err + +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampSession) GetDynamicConfig() (EVM2EVMMultiOnRampDynamicConfig, error) { + return _EVM2EVMMultiOnRamp.Contract.GetDynamicConfig(&_EVM2EVMMultiOnRamp.CallOpts) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampCallerSession) GetDynamicConfig() (EVM2EVMMultiOnRampDynamicConfig, error) { + return _EVM2EVMMultiOnRamp.Contract.GetDynamicConfig(&_EVM2EVMMultiOnRamp.CallOpts) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampCaller) GetExpectedNextSequenceNumber(opts *bind.CallOpts, destChainSelector uint64) (uint64, error) { + var out []interface{} + err := _EVM2EVMMultiOnRamp.contract.Call(opts, &out, "getExpectedNextSequenceNumber", destChainSelector) + + if err != nil { + return *new(uint64), err + } + + out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) + + return out0, err + +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampSession) GetExpectedNextSequenceNumber(destChainSelector uint64) (uint64, error) { + return _EVM2EVMMultiOnRamp.Contract.GetExpectedNextSequenceNumber(&_EVM2EVMMultiOnRamp.CallOpts, destChainSelector) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampCallerSession) GetExpectedNextSequenceNumber(destChainSelector uint64) (uint64, error) { + return _EVM2EVMMultiOnRamp.Contract.GetExpectedNextSequenceNumber(&_EVM2EVMMultiOnRamp.CallOpts, destChainSelector) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampCaller) GetFee(opts *bind.CallOpts, destChainSelector uint64, message ClientEVM2AnyMessage) (*big.Int, error) { + var out []interface{} + err := _EVM2EVMMultiOnRamp.contract.Call(opts, &out, "getFee", destChainSelector, message) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampSession) GetFee(destChainSelector uint64, message ClientEVM2AnyMessage) (*big.Int, error) { + return _EVM2EVMMultiOnRamp.Contract.GetFee(&_EVM2EVMMultiOnRamp.CallOpts, destChainSelector, message) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampCallerSession) GetFee(destChainSelector uint64, message ClientEVM2AnyMessage) (*big.Int, error) { + return _EVM2EVMMultiOnRamp.Contract.GetFee(&_EVM2EVMMultiOnRamp.CallOpts, destChainSelector, message) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampCaller) GetPoolBySourceToken(opts *bind.CallOpts, arg0 uint64, sourceToken common.Address) (common.Address, error) { + var out []interface{} + err := _EVM2EVMMultiOnRamp.contract.Call(opts, &out, "getPoolBySourceToken", arg0, sourceToken) + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampSession) GetPoolBySourceToken(arg0 uint64, sourceToken common.Address) (common.Address, error) { + return _EVM2EVMMultiOnRamp.Contract.GetPoolBySourceToken(&_EVM2EVMMultiOnRamp.CallOpts, arg0, sourceToken) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampCallerSession) GetPoolBySourceToken(arg0 uint64, sourceToken common.Address) (common.Address, error) { + return _EVM2EVMMultiOnRamp.Contract.GetPoolBySourceToken(&_EVM2EVMMultiOnRamp.CallOpts, arg0, sourceToken) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampCaller) GetRouter(opts *bind.CallOpts, destChainSelector uint64) (common.Address, error) { + var out []interface{} + err := _EVM2EVMMultiOnRamp.contract.Call(opts, &out, "getRouter", destChainSelector) + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampSession) GetRouter(destChainSelector uint64) (common.Address, error) { + return _EVM2EVMMultiOnRamp.Contract.GetRouter(&_EVM2EVMMultiOnRamp.CallOpts, destChainSelector) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampCallerSession) GetRouter(destChainSelector uint64) (common.Address, error) { + return _EVM2EVMMultiOnRamp.Contract.GetRouter(&_EVM2EVMMultiOnRamp.CallOpts, destChainSelector) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampCaller) GetStaticConfig(opts *bind.CallOpts) (EVM2EVMMultiOnRampStaticConfig, error) { + var out []interface{} + err := _EVM2EVMMultiOnRamp.contract.Call(opts, &out, "getStaticConfig") + + if err != nil { + return *new(EVM2EVMMultiOnRampStaticConfig), err + } + + out0 := *abi.ConvertType(out[0], new(EVM2EVMMultiOnRampStaticConfig)).(*EVM2EVMMultiOnRampStaticConfig) + + return out0, err + +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampSession) GetStaticConfig() (EVM2EVMMultiOnRampStaticConfig, error) { + return _EVM2EVMMultiOnRamp.Contract.GetStaticConfig(&_EVM2EVMMultiOnRamp.CallOpts) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampCallerSession) GetStaticConfig() (EVM2EVMMultiOnRampStaticConfig, error) { + return _EVM2EVMMultiOnRamp.Contract.GetStaticConfig(&_EVM2EVMMultiOnRamp.CallOpts) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampCaller) GetSupportedTokens(opts *bind.CallOpts, arg0 uint64) ([]common.Address, error) { + var out []interface{} + err := _EVM2EVMMultiOnRamp.contract.Call(opts, &out, "getSupportedTokens", arg0) + + if err != nil { + return *new([]common.Address), err + } + + out0 := *abi.ConvertType(out[0], new([]common.Address)).(*[]common.Address) + + return out0, err + +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampSession) GetSupportedTokens(arg0 uint64) ([]common.Address, error) { + return _EVM2EVMMultiOnRamp.Contract.GetSupportedTokens(&_EVM2EVMMultiOnRamp.CallOpts, arg0) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampCallerSession) GetSupportedTokens(arg0 uint64) ([]common.Address, error) { + return _EVM2EVMMultiOnRamp.Contract.GetSupportedTokens(&_EVM2EVMMultiOnRamp.CallOpts, arg0) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _EVM2EVMMultiOnRamp.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampSession) Owner() (common.Address, error) { + return _EVM2EVMMultiOnRamp.Contract.Owner(&_EVM2EVMMultiOnRamp.CallOpts) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampCallerSession) Owner() (common.Address, error) { + return _EVM2EVMMultiOnRamp.Contract.Owner(&_EVM2EVMMultiOnRamp.CallOpts) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampCaller) TypeAndVersion(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _EVM2EVMMultiOnRamp.contract.Call(opts, &out, "typeAndVersion") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampSession) TypeAndVersion() (string, error) { + return _EVM2EVMMultiOnRamp.Contract.TypeAndVersion(&_EVM2EVMMultiOnRamp.CallOpts) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampCallerSession) TypeAndVersion() (string, error) { + return _EVM2EVMMultiOnRamp.Contract.TypeAndVersion(&_EVM2EVMMultiOnRamp.CallOpts) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampTransactor) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _EVM2EVMMultiOnRamp.contract.Transact(opts, "acceptOwnership") +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampSession) AcceptOwnership() (*types.Transaction, error) { + return _EVM2EVMMultiOnRamp.Contract.AcceptOwnership(&_EVM2EVMMultiOnRamp.TransactOpts) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampTransactorSession) AcceptOwnership() (*types.Transaction, error) { + return _EVM2EVMMultiOnRamp.Contract.AcceptOwnership(&_EVM2EVMMultiOnRamp.TransactOpts) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampTransactor) ApplyDestChainConfigUpdates(opts *bind.TransactOpts, destChainConfigArgs []EVM2EVMMultiOnRampDestChainConfigArgs) (*types.Transaction, error) { + return _EVM2EVMMultiOnRamp.contract.Transact(opts, "applyDestChainConfigUpdates", destChainConfigArgs) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampSession) ApplyDestChainConfigUpdates(destChainConfigArgs []EVM2EVMMultiOnRampDestChainConfigArgs) (*types.Transaction, error) { + return _EVM2EVMMultiOnRamp.Contract.ApplyDestChainConfigUpdates(&_EVM2EVMMultiOnRamp.TransactOpts, destChainConfigArgs) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampTransactorSession) ApplyDestChainConfigUpdates(destChainConfigArgs []EVM2EVMMultiOnRampDestChainConfigArgs) (*types.Transaction, error) { + return _EVM2EVMMultiOnRamp.Contract.ApplyDestChainConfigUpdates(&_EVM2EVMMultiOnRamp.TransactOpts, destChainConfigArgs) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampTransactor) ForwardFromRouter(opts *bind.TransactOpts, destChainSelector uint64, message ClientEVM2AnyMessage, feeTokenAmount *big.Int, originalSender common.Address) (*types.Transaction, error) { + return _EVM2EVMMultiOnRamp.contract.Transact(opts, "forwardFromRouter", destChainSelector, message, feeTokenAmount, originalSender) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampSession) ForwardFromRouter(destChainSelector uint64, message ClientEVM2AnyMessage, feeTokenAmount *big.Int, originalSender common.Address) (*types.Transaction, error) { + return _EVM2EVMMultiOnRamp.Contract.ForwardFromRouter(&_EVM2EVMMultiOnRamp.TransactOpts, destChainSelector, message, feeTokenAmount, originalSender) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampTransactorSession) ForwardFromRouter(destChainSelector uint64, message ClientEVM2AnyMessage, feeTokenAmount *big.Int, originalSender common.Address) (*types.Transaction, error) { + return _EVM2EVMMultiOnRamp.Contract.ForwardFromRouter(&_EVM2EVMMultiOnRamp.TransactOpts, destChainSelector, message, feeTokenAmount, originalSender) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampTransactor) SetDynamicConfig(opts *bind.TransactOpts, dynamicConfig EVM2EVMMultiOnRampDynamicConfig) (*types.Transaction, error) { + return _EVM2EVMMultiOnRamp.contract.Transact(opts, "setDynamicConfig", dynamicConfig) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampSession) SetDynamicConfig(dynamicConfig EVM2EVMMultiOnRampDynamicConfig) (*types.Transaction, error) { + return _EVM2EVMMultiOnRamp.Contract.SetDynamicConfig(&_EVM2EVMMultiOnRamp.TransactOpts, dynamicConfig) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampTransactorSession) SetDynamicConfig(dynamicConfig EVM2EVMMultiOnRampDynamicConfig) (*types.Transaction, error) { + return _EVM2EVMMultiOnRamp.Contract.SetDynamicConfig(&_EVM2EVMMultiOnRamp.TransactOpts, dynamicConfig) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampTransactor) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { + return _EVM2EVMMultiOnRamp.contract.Transact(opts, "transferOwnership", to) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _EVM2EVMMultiOnRamp.Contract.TransferOwnership(&_EVM2EVMMultiOnRamp.TransactOpts, to) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampTransactorSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _EVM2EVMMultiOnRamp.Contract.TransferOwnership(&_EVM2EVMMultiOnRamp.TransactOpts, to) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampTransactor) WithdrawFeeTokens(opts *bind.TransactOpts) (*types.Transaction, error) { + return _EVM2EVMMultiOnRamp.contract.Transact(opts, "withdrawFeeTokens") +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampSession) WithdrawFeeTokens() (*types.Transaction, error) { + return _EVM2EVMMultiOnRamp.Contract.WithdrawFeeTokens(&_EVM2EVMMultiOnRamp.TransactOpts) +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampTransactorSession) WithdrawFeeTokens() (*types.Transaction, error) { + return _EVM2EVMMultiOnRamp.Contract.WithdrawFeeTokens(&_EVM2EVMMultiOnRamp.TransactOpts) +} + +type EVM2EVMMultiOnRampCCIPSendRequestedIterator struct { + Event *EVM2EVMMultiOnRampCCIPSendRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *EVM2EVMMultiOnRampCCIPSendRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOnRampCCIPSendRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOnRampCCIPSendRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *EVM2EVMMultiOnRampCCIPSendRequestedIterator) Error() error { + return it.fail +} + +func (it *EVM2EVMMultiOnRampCCIPSendRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type EVM2EVMMultiOnRampCCIPSendRequested struct { + DestChainSelector uint64 + Message InternalEVM2AnyRampMessage + Raw types.Log +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampFilterer) FilterCCIPSendRequested(opts *bind.FilterOpts, destChainSelector []uint64) (*EVM2EVMMultiOnRampCCIPSendRequestedIterator, error) { + + var destChainSelectorRule []interface{} + for _, destChainSelectorItem := range destChainSelector { + destChainSelectorRule = append(destChainSelectorRule, destChainSelectorItem) + } + + logs, sub, err := _EVM2EVMMultiOnRamp.contract.FilterLogs(opts, "CCIPSendRequested", destChainSelectorRule) + if err != nil { + return nil, err + } + return &EVM2EVMMultiOnRampCCIPSendRequestedIterator{contract: _EVM2EVMMultiOnRamp.contract, event: "CCIPSendRequested", logs: logs, sub: sub}, nil +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampFilterer) WatchCCIPSendRequested(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOnRampCCIPSendRequested, destChainSelector []uint64) (event.Subscription, error) { + + var destChainSelectorRule []interface{} + for _, destChainSelectorItem := range destChainSelector { + destChainSelectorRule = append(destChainSelectorRule, destChainSelectorItem) + } + + logs, sub, err := _EVM2EVMMultiOnRamp.contract.WatchLogs(opts, "CCIPSendRequested", destChainSelectorRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(EVM2EVMMultiOnRampCCIPSendRequested) + if err := _EVM2EVMMultiOnRamp.contract.UnpackLog(event, "CCIPSendRequested", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampFilterer) ParseCCIPSendRequested(log types.Log) (*EVM2EVMMultiOnRampCCIPSendRequested, error) { + event := new(EVM2EVMMultiOnRampCCIPSendRequested) + if err := _EVM2EVMMultiOnRamp.contract.UnpackLog(event, "CCIPSendRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type EVM2EVMMultiOnRampConfigSetIterator struct { + Event *EVM2EVMMultiOnRampConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *EVM2EVMMultiOnRampConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOnRampConfigSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOnRampConfigSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *EVM2EVMMultiOnRampConfigSetIterator) Error() error { + return it.fail +} + +func (it *EVM2EVMMultiOnRampConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type EVM2EVMMultiOnRampConfigSet struct { + StaticConfig EVM2EVMMultiOnRampStaticConfig + DynamicConfig EVM2EVMMultiOnRampDynamicConfig + Raw types.Log +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampFilterer) FilterConfigSet(opts *bind.FilterOpts) (*EVM2EVMMultiOnRampConfigSetIterator, error) { + + logs, sub, err := _EVM2EVMMultiOnRamp.contract.FilterLogs(opts, "ConfigSet") + if err != nil { + return nil, err + } + return &EVM2EVMMultiOnRampConfigSetIterator{contract: _EVM2EVMMultiOnRamp.contract, event: "ConfigSet", logs: logs, sub: sub}, nil +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampFilterer) WatchConfigSet(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOnRampConfigSet) (event.Subscription, error) { + + logs, sub, err := _EVM2EVMMultiOnRamp.contract.WatchLogs(opts, "ConfigSet") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(EVM2EVMMultiOnRampConfigSet) + if err := _EVM2EVMMultiOnRamp.contract.UnpackLog(event, "ConfigSet", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampFilterer) ParseConfigSet(log types.Log) (*EVM2EVMMultiOnRampConfigSet, error) { + event := new(EVM2EVMMultiOnRampConfigSet) + if err := _EVM2EVMMultiOnRamp.contract.UnpackLog(event, "ConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type EVM2EVMMultiOnRampDestChainConfigSetIterator struct { + Event *EVM2EVMMultiOnRampDestChainConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *EVM2EVMMultiOnRampDestChainConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOnRampDestChainConfigSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOnRampDestChainConfigSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *EVM2EVMMultiOnRampDestChainConfigSetIterator) Error() error { + return it.fail +} + +func (it *EVM2EVMMultiOnRampDestChainConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type EVM2EVMMultiOnRampDestChainConfigSet struct { + DestChainSelector uint64 + DestChainConfig EVM2EVMMultiOnRampDestChainConfig + Raw types.Log +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampFilterer) FilterDestChainConfigSet(opts *bind.FilterOpts, destChainSelector []uint64) (*EVM2EVMMultiOnRampDestChainConfigSetIterator, error) { + + var destChainSelectorRule []interface{} + for _, destChainSelectorItem := range destChainSelector { + destChainSelectorRule = append(destChainSelectorRule, destChainSelectorItem) + } + + logs, sub, err := _EVM2EVMMultiOnRamp.contract.FilterLogs(opts, "DestChainConfigSet", destChainSelectorRule) + if err != nil { + return nil, err + } + return &EVM2EVMMultiOnRampDestChainConfigSetIterator{contract: _EVM2EVMMultiOnRamp.contract, event: "DestChainConfigSet", logs: logs, sub: sub}, nil +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampFilterer) WatchDestChainConfigSet(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOnRampDestChainConfigSet, destChainSelector []uint64) (event.Subscription, error) { + + var destChainSelectorRule []interface{} + for _, destChainSelectorItem := range destChainSelector { + destChainSelectorRule = append(destChainSelectorRule, destChainSelectorItem) + } + + logs, sub, err := _EVM2EVMMultiOnRamp.contract.WatchLogs(opts, "DestChainConfigSet", destChainSelectorRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(EVM2EVMMultiOnRampDestChainConfigSet) + if err := _EVM2EVMMultiOnRamp.contract.UnpackLog(event, "DestChainConfigSet", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampFilterer) ParseDestChainConfigSet(log types.Log) (*EVM2EVMMultiOnRampDestChainConfigSet, error) { + event := new(EVM2EVMMultiOnRampDestChainConfigSet) + if err := _EVM2EVMMultiOnRamp.contract.UnpackLog(event, "DestChainConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type EVM2EVMMultiOnRampFeePaidIterator struct { + Event *EVM2EVMMultiOnRampFeePaid + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *EVM2EVMMultiOnRampFeePaidIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOnRampFeePaid) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOnRampFeePaid) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *EVM2EVMMultiOnRampFeePaidIterator) Error() error { + return it.fail +} + +func (it *EVM2EVMMultiOnRampFeePaidIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type EVM2EVMMultiOnRampFeePaid struct { + FeeToken common.Address + FeeValueJuels *big.Int + Raw types.Log +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampFilterer) FilterFeePaid(opts *bind.FilterOpts, feeToken []common.Address) (*EVM2EVMMultiOnRampFeePaidIterator, error) { + + var feeTokenRule []interface{} + for _, feeTokenItem := range feeToken { + feeTokenRule = append(feeTokenRule, feeTokenItem) + } + + logs, sub, err := _EVM2EVMMultiOnRamp.contract.FilterLogs(opts, "FeePaid", feeTokenRule) + if err != nil { + return nil, err + } + return &EVM2EVMMultiOnRampFeePaidIterator{contract: _EVM2EVMMultiOnRamp.contract, event: "FeePaid", logs: logs, sub: sub}, nil +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampFilterer) WatchFeePaid(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOnRampFeePaid, feeToken []common.Address) (event.Subscription, error) { + + var feeTokenRule []interface{} + for _, feeTokenItem := range feeToken { + feeTokenRule = append(feeTokenRule, feeTokenItem) + } + + logs, sub, err := _EVM2EVMMultiOnRamp.contract.WatchLogs(opts, "FeePaid", feeTokenRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(EVM2EVMMultiOnRampFeePaid) + if err := _EVM2EVMMultiOnRamp.contract.UnpackLog(event, "FeePaid", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampFilterer) ParseFeePaid(log types.Log) (*EVM2EVMMultiOnRampFeePaid, error) { + event := new(EVM2EVMMultiOnRampFeePaid) + if err := _EVM2EVMMultiOnRamp.contract.UnpackLog(event, "FeePaid", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type EVM2EVMMultiOnRampFeeTokenWithdrawnIterator struct { + Event *EVM2EVMMultiOnRampFeeTokenWithdrawn + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *EVM2EVMMultiOnRampFeeTokenWithdrawnIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOnRampFeeTokenWithdrawn) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOnRampFeeTokenWithdrawn) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *EVM2EVMMultiOnRampFeeTokenWithdrawnIterator) Error() error { + return it.fail +} + +func (it *EVM2EVMMultiOnRampFeeTokenWithdrawnIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type EVM2EVMMultiOnRampFeeTokenWithdrawn struct { + FeeAggregator common.Address + FeeToken common.Address + Amount *big.Int + Raw types.Log +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampFilterer) FilterFeeTokenWithdrawn(opts *bind.FilterOpts, feeAggregator []common.Address, feeToken []common.Address) (*EVM2EVMMultiOnRampFeeTokenWithdrawnIterator, error) { + + var feeAggregatorRule []interface{} + for _, feeAggregatorItem := range feeAggregator { + feeAggregatorRule = append(feeAggregatorRule, feeAggregatorItem) + } + var feeTokenRule []interface{} + for _, feeTokenItem := range feeToken { + feeTokenRule = append(feeTokenRule, feeTokenItem) + } + + logs, sub, err := _EVM2EVMMultiOnRamp.contract.FilterLogs(opts, "FeeTokenWithdrawn", feeAggregatorRule, feeTokenRule) + if err != nil { + return nil, err + } + return &EVM2EVMMultiOnRampFeeTokenWithdrawnIterator{contract: _EVM2EVMMultiOnRamp.contract, event: "FeeTokenWithdrawn", logs: logs, sub: sub}, nil +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampFilterer) WatchFeeTokenWithdrawn(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOnRampFeeTokenWithdrawn, feeAggregator []common.Address, feeToken []common.Address) (event.Subscription, error) { + + var feeAggregatorRule []interface{} + for _, feeAggregatorItem := range feeAggregator { + feeAggregatorRule = append(feeAggregatorRule, feeAggregatorItem) + } + var feeTokenRule []interface{} + for _, feeTokenItem := range feeToken { + feeTokenRule = append(feeTokenRule, feeTokenItem) + } + + logs, sub, err := _EVM2EVMMultiOnRamp.contract.WatchLogs(opts, "FeeTokenWithdrawn", feeAggregatorRule, feeTokenRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(EVM2EVMMultiOnRampFeeTokenWithdrawn) + if err := _EVM2EVMMultiOnRamp.contract.UnpackLog(event, "FeeTokenWithdrawn", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampFilterer) ParseFeeTokenWithdrawn(log types.Log) (*EVM2EVMMultiOnRampFeeTokenWithdrawn, error) { + event := new(EVM2EVMMultiOnRampFeeTokenWithdrawn) + if err := _EVM2EVMMultiOnRamp.contract.UnpackLog(event, "FeeTokenWithdrawn", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type EVM2EVMMultiOnRampOwnershipTransferRequestedIterator struct { + Event *EVM2EVMMultiOnRampOwnershipTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *EVM2EVMMultiOnRampOwnershipTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOnRampOwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOnRampOwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *EVM2EVMMultiOnRampOwnershipTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *EVM2EVMMultiOnRampOwnershipTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type EVM2EVMMultiOnRampOwnershipTransferRequested struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampFilterer) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*EVM2EVMMultiOnRampOwnershipTransferRequestedIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _EVM2EVMMultiOnRamp.contract.FilterLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return &EVM2EVMMultiOnRampOwnershipTransferRequestedIterator{contract: _EVM2EVMMultiOnRamp.contract, event: "OwnershipTransferRequested", logs: logs, sub: sub}, nil +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampFilterer) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOnRampOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _EVM2EVMMultiOnRamp.contract.WatchLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(EVM2EVMMultiOnRampOwnershipTransferRequested) + if err := _EVM2EVMMultiOnRamp.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampFilterer) ParseOwnershipTransferRequested(log types.Log) (*EVM2EVMMultiOnRampOwnershipTransferRequested, error) { + event := new(EVM2EVMMultiOnRampOwnershipTransferRequested) + if err := _EVM2EVMMultiOnRamp.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type EVM2EVMMultiOnRampOwnershipTransferredIterator struct { + Event *EVM2EVMMultiOnRampOwnershipTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *EVM2EVMMultiOnRampOwnershipTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOnRampOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(EVM2EVMMultiOnRampOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *EVM2EVMMultiOnRampOwnershipTransferredIterator) Error() error { + return it.fail +} + +func (it *EVM2EVMMultiOnRampOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type EVM2EVMMultiOnRampOwnershipTransferred struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*EVM2EVMMultiOnRampOwnershipTransferredIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _EVM2EVMMultiOnRamp.contract.FilterLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return &EVM2EVMMultiOnRampOwnershipTransferredIterator{contract: _EVM2EVMMultiOnRamp.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOnRampOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _EVM2EVMMultiOnRamp.contract.WatchLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(EVM2EVMMultiOnRampOwnershipTransferred) + if err := _EVM2EVMMultiOnRamp.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRampFilterer) ParseOwnershipTransferred(log types.Log) (*EVM2EVMMultiOnRampOwnershipTransferred, error) { + event := new(EVM2EVMMultiOnRampOwnershipTransferred) + if err := _EVM2EVMMultiOnRamp.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRamp) ParseLog(log types.Log) (generated.AbigenLog, error) { + switch log.Topics[0] { + case _EVM2EVMMultiOnRamp.abi.Events["CCIPSendRequested"].ID: + return _EVM2EVMMultiOnRamp.ParseCCIPSendRequested(log) + case _EVM2EVMMultiOnRamp.abi.Events["ConfigSet"].ID: + return _EVM2EVMMultiOnRamp.ParseConfigSet(log) + case _EVM2EVMMultiOnRamp.abi.Events["DestChainConfigSet"].ID: + return _EVM2EVMMultiOnRamp.ParseDestChainConfigSet(log) + case _EVM2EVMMultiOnRamp.abi.Events["FeePaid"].ID: + return _EVM2EVMMultiOnRamp.ParseFeePaid(log) + case _EVM2EVMMultiOnRamp.abi.Events["FeeTokenWithdrawn"].ID: + return _EVM2EVMMultiOnRamp.ParseFeeTokenWithdrawn(log) + case _EVM2EVMMultiOnRamp.abi.Events["OwnershipTransferRequested"].ID: + return _EVM2EVMMultiOnRamp.ParseOwnershipTransferRequested(log) + case _EVM2EVMMultiOnRamp.abi.Events["OwnershipTransferred"].ID: + return _EVM2EVMMultiOnRamp.ParseOwnershipTransferred(log) + + default: + return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) + } +} + +func (EVM2EVMMultiOnRampCCIPSendRequested) Topic() common.Hash { + return common.HexToHash("0x0f07cd31e53232da9125e517f09550fdde74bf43d6a0a76ebd41674dafe2ab29") +} + +func (EVM2EVMMultiOnRampConfigSet) Topic() common.Hash { + return common.HexToHash("0x2d8f19dc1cd01460c3367a09d2d424f2b1940ba7c886047edd078c7b77ea4558") +} + +func (EVM2EVMMultiOnRampDestChainConfigSet) Topic() common.Hash { + return common.HexToHash("0x324d4a7950b57da00ce533ad6697039be6281726a73da959d0ab3ff795181ec6") +} + +func (EVM2EVMMultiOnRampFeePaid) Topic() common.Hash { + return common.HexToHash("0x075a2720282fdf622141dae0b048ef90a21a7e57c134c76912d19d006b3b3f6f") +} + +func (EVM2EVMMultiOnRampFeeTokenWithdrawn) Topic() common.Hash { + return common.HexToHash("0x508d7d183612c18fc339b42618912b9fa3239f631dd7ec0671f950200a0fa66e") +} + +func (EVM2EVMMultiOnRampOwnershipTransferRequested) Topic() common.Hash { + return common.HexToHash("0xed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278") +} + +func (EVM2EVMMultiOnRampOwnershipTransferred) Topic() common.Hash { + return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") +} + +func (_EVM2EVMMultiOnRamp *EVM2EVMMultiOnRamp) Address() common.Address { + return _EVM2EVMMultiOnRamp.address +} + +type EVM2EVMMultiOnRampInterface interface { + GetDynamicConfig(opts *bind.CallOpts) (EVM2EVMMultiOnRampDynamicConfig, error) + + GetExpectedNextSequenceNumber(opts *bind.CallOpts, destChainSelector uint64) (uint64, error) + + GetFee(opts *bind.CallOpts, destChainSelector uint64, message ClientEVM2AnyMessage) (*big.Int, error) + + GetPoolBySourceToken(opts *bind.CallOpts, arg0 uint64, sourceToken common.Address) (common.Address, error) + + GetRouter(opts *bind.CallOpts, destChainSelector uint64) (common.Address, error) + + GetStaticConfig(opts *bind.CallOpts) (EVM2EVMMultiOnRampStaticConfig, error) + + GetSupportedTokens(opts *bind.CallOpts, arg0 uint64) ([]common.Address, error) + + Owner(opts *bind.CallOpts) (common.Address, error) + + TypeAndVersion(opts *bind.CallOpts) (string, error) + + AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) + + ApplyDestChainConfigUpdates(opts *bind.TransactOpts, destChainConfigArgs []EVM2EVMMultiOnRampDestChainConfigArgs) (*types.Transaction, error) + + ForwardFromRouter(opts *bind.TransactOpts, destChainSelector uint64, message ClientEVM2AnyMessage, feeTokenAmount *big.Int, originalSender common.Address) (*types.Transaction, error) + + SetDynamicConfig(opts *bind.TransactOpts, dynamicConfig EVM2EVMMultiOnRampDynamicConfig) (*types.Transaction, error) + + TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + + WithdrawFeeTokens(opts *bind.TransactOpts) (*types.Transaction, error) + + FilterCCIPSendRequested(opts *bind.FilterOpts, destChainSelector []uint64) (*EVM2EVMMultiOnRampCCIPSendRequestedIterator, error) + + WatchCCIPSendRequested(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOnRampCCIPSendRequested, destChainSelector []uint64) (event.Subscription, error) + + ParseCCIPSendRequested(log types.Log) (*EVM2EVMMultiOnRampCCIPSendRequested, error) + + FilterConfigSet(opts *bind.FilterOpts) (*EVM2EVMMultiOnRampConfigSetIterator, error) + + WatchConfigSet(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOnRampConfigSet) (event.Subscription, error) + + ParseConfigSet(log types.Log) (*EVM2EVMMultiOnRampConfigSet, error) + + FilterDestChainConfigSet(opts *bind.FilterOpts, destChainSelector []uint64) (*EVM2EVMMultiOnRampDestChainConfigSetIterator, error) + + WatchDestChainConfigSet(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOnRampDestChainConfigSet, destChainSelector []uint64) (event.Subscription, error) + + ParseDestChainConfigSet(log types.Log) (*EVM2EVMMultiOnRampDestChainConfigSet, error) + + FilterFeePaid(opts *bind.FilterOpts, feeToken []common.Address) (*EVM2EVMMultiOnRampFeePaidIterator, error) + + WatchFeePaid(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOnRampFeePaid, feeToken []common.Address) (event.Subscription, error) + + ParseFeePaid(log types.Log) (*EVM2EVMMultiOnRampFeePaid, error) + + FilterFeeTokenWithdrawn(opts *bind.FilterOpts, feeAggregator []common.Address, feeToken []common.Address) (*EVM2EVMMultiOnRampFeeTokenWithdrawnIterator, error) + + WatchFeeTokenWithdrawn(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOnRampFeeTokenWithdrawn, feeAggregator []common.Address, feeToken []common.Address) (event.Subscription, error) + + ParseFeeTokenWithdrawn(log types.Log) (*EVM2EVMMultiOnRampFeeTokenWithdrawn, error) + + FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*EVM2EVMMultiOnRampOwnershipTransferRequestedIterator, error) + + WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOnRampOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferRequested(log types.Log) (*EVM2EVMMultiOnRampOwnershipTransferRequested, error) + + FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*EVM2EVMMultiOnRampOwnershipTransferredIterator, error) + + WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *EVM2EVMMultiOnRampOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferred(log types.Log) (*EVM2EVMMultiOnRampOwnershipTransferred, error) + + ParseLog(log types.Log) (generated.AbigenLog, error) + + Address() common.Address +} diff --git a/core/gethwrappers/ccip/generated/mock_arm_contract/mock_arm_contract.go b/core/gethwrappers/ccip/generated/mock_arm_contract/mock_arm_contract.go new file mode 100644 index 0000000000..fff63bef80 --- /dev/null +++ b/core/gethwrappers/ccip/generated/mock_arm_contract/mock_arm_contract.go @@ -0,0 +1,746 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package mock_arm_contract + +import ( + "errors" + "fmt" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" +) + +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +type IRMNTaggedRoot struct { + CommitStore common.Address + Root [32]byte +} + +type RMNConfig struct { + Voters []RMNVoter + BlessWeightThreshold uint16 + CurseWeightThreshold uint16 +} + +type RMNUnvoteToCurseRecord struct { + CurseVoteAddr common.Address + CursesHash [32]byte + ForceUnvote bool +} + +type RMNVoter struct { + BlessVoteAddr common.Address + CurseVoteAddr common.Address + CurseUnvoteAddr common.Address + BlessWeight uint8 + CurseWeight uint8 +} + +var MockARMContractMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"err\",\"type\":\"bytes\"}],\"name\":\"CustomError\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfigDetails\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"version\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"blockNumber\",\"type\":\"uint32\"},{\"components\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"blessVoteAddr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"curseVoteAddr\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"curseUnvoteAddr\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"blessWeight\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"curseWeight\",\"type\":\"uint8\"}],\"internalType\":\"structRMN.Voter[]\",\"name\":\"voters\",\"type\":\"tuple[]\"},{\"internalType\":\"uint16\",\"name\":\"blessWeightThreshold\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"curseWeightThreshold\",\"type\":\"uint16\"}],\"internalType\":\"structRMN.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"commitStore\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"}],\"internalType\":\"structIRMN.TaggedRoot\",\"name\":\"\",\"type\":\"tuple\"}],\"name\":\"isBlessed\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes16\",\"name\":\"subject\",\"type\":\"bytes16\"}],\"name\":\"isCursed\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isCursed\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"curseVoteAddr\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"cursesHash\",\"type\":\"bytes32\"},{\"internalType\":\"bool\",\"name\":\"forceUnvote\",\"type\":\"bool\"}],\"internalType\":\"structRMN.UnvoteToCurseRecord[]\",\"name\":\"\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes16\",\"name\":\"subject\",\"type\":\"bytes16\"}],\"name\":\"ownerUnvoteToCurse\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"curseVoteAddr\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"cursesHash\",\"type\":\"bytes32\"},{\"internalType\":\"bool\",\"name\":\"forceUnvote\",\"type\":\"bool\"}],\"internalType\":\"structRMN.UnvoteToCurseRecord[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"name\":\"ownerUnvoteToCurse\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"err\",\"type\":\"bytes\"}],\"name\":\"setRevert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"voteToCurse\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"bytes16\",\"name\":\"subject\",\"type\":\"bytes16\"}],\"name\":\"voteToCurse\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b5033806000816100675760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615610097576100978161009f565b505050610148565b336001600160a01b038216036100f75760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161005e565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b610ed7806101576000396000f3fe608060405234801561001057600080fd5b50600436106100d45760003560e01c8063618af128116100815780637a7c27491161005b5780637a7c2749146102b55780638da5cb5b146102c8578063f2fde38b146102f057600080fd5b8063618af1281461020a578063794860871461024357806379ba5097146102ad57600080fd5b8063397796f7116100b2578063397796f7146101ba5780633f42ab73146101c25780634d616771146101d957600080fd5b8063119a3527146100d9578063257174dc1461012b5780632cbc26bb14610192575b600080fd5b6101296100e73660046107fe565b50600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1674010000000000000000000000000000000000000000179055565b005b6101296101393660046109db565b7fffffffffffffffffffffffffffffffff0000000000000000000000000000000016600090815260066020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905550565b6101a56101a0366004610a29565b610303565b60405190151581526020015b60405180910390f35b6101a56103b7565b6101ca610424565b6040516101b193929190610a4b565b6101a56101e7366004610b1e565b5060015474010000000000000000000000000000000000000000900460ff161590565b610129610218366004610b36565b50600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff169055565b610129610251366004610b73565b7fffffffffffffffffffffffffffffffff0000000000000000000000000000000016600090815260066020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905550565b610129610565565b6101296102c3366004610b96565b610662565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101b1565b6101296102fe366004610c49565b610672565b60006002805461031290610c64565b1590506103575760026040517f5a4ff67100000000000000000000000000000000000000000000000000000000815260040161034e9190610cb1565b60405180910390fd5b60015474010000000000000000000000000000000000000000900460ff16806103b157507fffffffffffffffffffffffffffffffff00000000000000000000000000000000821660009081526006602052604090205460ff165b92915050565b6000600280546103c690610c64565b1590506104025760026040517f5a4ff67100000000000000000000000000000000000000000000000000000000815260040161034e9190610cb1565b5060015474010000000000000000000000000000000000000000900460ff1690565b6040805160608082018352815260006020820181905291810182905281906005546040805160038054608060208202840181019094526060830181815263ffffffff8087169664010000000090041694929392849284929184919060009085015b828210156105315760008481526020908190206040805160a08101825260038602909201805473ffffffffffffffffffffffffffffffffffffffff90811684526001808301548216858701526002909201549081169284019290925260ff74010000000000000000000000000000000000000000830481166060850152750100000000000000000000000000000000000000000090920490911660808301529083529092019101610485565b505050908252506001919091015461ffff808216602084015262010000909104166040909101529296919550919350915050565b60015473ffffffffffffffffffffffffffffffffffffffff1633146105e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e657200000000000000000000604482015260640161034e565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b600261066e8282610db0565b5050565b61067a610686565b61068381610709565b50565b60005473ffffffffffffffffffffffffffffffffffffffff163314610707576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640161034e565b565b3373ffffffffffffffffffffffffffffffffffffffff821603610788576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161034e565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60006020828403121561081057600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516060810167ffffffffffffffff8111828210171561086957610869610817565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156108b6576108b6610817565b604052919050565b803573ffffffffffffffffffffffffffffffffffffffff811681146108e257600080fd5b919050565b600082601f8301126108f857600080fd5b8135602067ffffffffffffffff82111561091457610914610817565b610922818360051b0161086f565b8281526060928302850182019282820191908785111561094157600080fd5b8387015b8581101561099e5781818a03121561095d5760008081fd5b610965610846565b61096e826108be565b81528582013586820152604080830135801515811461098d5760008081fd5b908201528452928401928101610945565b5090979650505050505050565b80357fffffffffffffffffffffffffffffffff00000000000000000000000000000000811681146108e257600080fd5b600080604083850312156109ee57600080fd5b823567ffffffffffffffff811115610a0557600080fd5b610a11858286016108e7565b925050610a20602084016109ab565b90509250929050565b600060208284031215610a3b57600080fd5b610a44826109ab565b9392505050565b63ffffffff84811682528316602080830191909152606060408084018290528451848301839052805160c0860181905260009491820190859060e08801905b80831015610af1578351805173ffffffffffffffffffffffffffffffffffffffff9081168452868201518116878501528782015116878401528781015160ff908116898501526080918201511690830152928401926001929092019160a090910190610a8a565b509288015161ffff908116608089015260409098015190971660a090960195909552979650505050505050565b600060408284031215610b3057600080fd5b50919050565b600060208284031215610b4857600080fd5b813567ffffffffffffffff811115610b5f57600080fd5b610b6b848285016108e7565b949350505050565b60008060408385031215610b8657600080fd5b82359150610a20602084016109ab565b60006020808385031215610ba957600080fd5b823567ffffffffffffffff80821115610bc157600080fd5b818501915085601f830112610bd557600080fd5b813581811115610be757610be7610817565b610c17847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161086f565b91508082528684828501011115610c2d57600080fd5b8084840185840137600090820190930192909252509392505050565b600060208284031215610c5b57600080fd5b610a44826108be565b600181811c90821680610c7857607f821691505b602082108103610b30577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000602080835260008454610cc581610c64565b8060208701526040600180841660008114610ce75760018114610d2157610d51565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00851660408a0152604084151560051b8a01019550610d51565b89600052602060002060005b85811015610d485781548b8201860152908301908801610d2d565b8a016040019650505b509398975050505050505050565b601f821115610dab576000816000526020600020601f850160051c81016020861015610d885750805b601f850160051c820191505b81811015610da757828155600101610d94565b5050505b505050565b815167ffffffffffffffff811115610dca57610dca610817565b610dde81610dd88454610c64565b84610d5f565b602080601f831160018114610e315760008415610dfb5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555610da7565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015610e7e57888601518255948401946001909101908401610e5f565b5085821015610eba57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b0190555056fea164736f6c6343000818000a", +} + +var MockARMContractABI = MockARMContractMetaData.ABI + +var MockARMContractBin = MockARMContractMetaData.Bin + +func DeployMockARMContract(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *MockARMContract, error) { + parsed, err := MockARMContractMetaData.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(MockARMContractBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &MockARMContract{address: address, abi: *parsed, MockARMContractCaller: MockARMContractCaller{contract: contract}, MockARMContractTransactor: MockARMContractTransactor{contract: contract}, MockARMContractFilterer: MockARMContractFilterer{contract: contract}}, nil +} + +type MockARMContract struct { + address common.Address + abi abi.ABI + MockARMContractCaller + MockARMContractTransactor + MockARMContractFilterer +} + +type MockARMContractCaller struct { + contract *bind.BoundContract +} + +type MockARMContractTransactor struct { + contract *bind.BoundContract +} + +type MockARMContractFilterer struct { + contract *bind.BoundContract +} + +type MockARMContractSession struct { + Contract *MockARMContract + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type MockARMContractCallerSession struct { + Contract *MockARMContractCaller + CallOpts bind.CallOpts +} + +type MockARMContractTransactorSession struct { + Contract *MockARMContractTransactor + TransactOpts bind.TransactOpts +} + +type MockARMContractRaw struct { + Contract *MockARMContract +} + +type MockARMContractCallerRaw struct { + Contract *MockARMContractCaller +} + +type MockARMContractTransactorRaw struct { + Contract *MockARMContractTransactor +} + +func NewMockARMContract(address common.Address, backend bind.ContractBackend) (*MockARMContract, error) { + abi, err := abi.JSON(strings.NewReader(MockARMContractABI)) + if err != nil { + return nil, err + } + contract, err := bindMockARMContract(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &MockARMContract{address: address, abi: abi, MockARMContractCaller: MockARMContractCaller{contract: contract}, MockARMContractTransactor: MockARMContractTransactor{contract: contract}, MockARMContractFilterer: MockARMContractFilterer{contract: contract}}, nil +} + +func NewMockARMContractCaller(address common.Address, caller bind.ContractCaller) (*MockARMContractCaller, error) { + contract, err := bindMockARMContract(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &MockARMContractCaller{contract: contract}, nil +} + +func NewMockARMContractTransactor(address common.Address, transactor bind.ContractTransactor) (*MockARMContractTransactor, error) { + contract, err := bindMockARMContract(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &MockARMContractTransactor{contract: contract}, nil +} + +func NewMockARMContractFilterer(address common.Address, filterer bind.ContractFilterer) (*MockARMContractFilterer, error) { + contract, err := bindMockARMContract(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &MockARMContractFilterer{contract: contract}, nil +} + +func bindMockARMContract(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := MockARMContractMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_MockARMContract *MockARMContractRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _MockARMContract.Contract.MockARMContractCaller.contract.Call(opts, result, method, params...) +} + +func (_MockARMContract *MockARMContractRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _MockARMContract.Contract.MockARMContractTransactor.contract.Transfer(opts) +} + +func (_MockARMContract *MockARMContractRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _MockARMContract.Contract.MockARMContractTransactor.contract.Transact(opts, method, params...) +} + +func (_MockARMContract *MockARMContractCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _MockARMContract.Contract.contract.Call(opts, result, method, params...) +} + +func (_MockARMContract *MockARMContractTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _MockARMContract.Contract.contract.Transfer(opts) +} + +func (_MockARMContract *MockARMContractTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _MockARMContract.Contract.contract.Transact(opts, method, params...) +} + +func (_MockARMContract *MockARMContractCaller) GetConfigDetails(opts *bind.CallOpts) (GetConfigDetails, + + error) { + var out []interface{} + err := _MockARMContract.contract.Call(opts, &out, "getConfigDetails") + + outstruct := new(GetConfigDetails) + if err != nil { + return *outstruct, err + } + + outstruct.Version = *abi.ConvertType(out[0], new(uint32)).(*uint32) + outstruct.BlockNumber = *abi.ConvertType(out[1], new(uint32)).(*uint32) + outstruct.Config = *abi.ConvertType(out[2], new(RMNConfig)).(*RMNConfig) + + return *outstruct, err + +} + +func (_MockARMContract *MockARMContractSession) GetConfigDetails() (GetConfigDetails, + + error) { + return _MockARMContract.Contract.GetConfigDetails(&_MockARMContract.CallOpts) +} + +func (_MockARMContract *MockARMContractCallerSession) GetConfigDetails() (GetConfigDetails, + + error) { + return _MockARMContract.Contract.GetConfigDetails(&_MockARMContract.CallOpts) +} + +func (_MockARMContract *MockARMContractCaller) IsBlessed(opts *bind.CallOpts, arg0 IRMNTaggedRoot) (bool, error) { + var out []interface{} + err := _MockARMContract.contract.Call(opts, &out, "isBlessed", arg0) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +func (_MockARMContract *MockARMContractSession) IsBlessed(arg0 IRMNTaggedRoot) (bool, error) { + return _MockARMContract.Contract.IsBlessed(&_MockARMContract.CallOpts, arg0) +} + +func (_MockARMContract *MockARMContractCallerSession) IsBlessed(arg0 IRMNTaggedRoot) (bool, error) { + return _MockARMContract.Contract.IsBlessed(&_MockARMContract.CallOpts, arg0) +} + +func (_MockARMContract *MockARMContractCaller) IsCursed(opts *bind.CallOpts, subject [16]byte) (bool, error) { + var out []interface{} + err := _MockARMContract.contract.Call(opts, &out, "isCursed", subject) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +func (_MockARMContract *MockARMContractSession) IsCursed(subject [16]byte) (bool, error) { + return _MockARMContract.Contract.IsCursed(&_MockARMContract.CallOpts, subject) +} + +func (_MockARMContract *MockARMContractCallerSession) IsCursed(subject [16]byte) (bool, error) { + return _MockARMContract.Contract.IsCursed(&_MockARMContract.CallOpts, subject) +} + +func (_MockARMContract *MockARMContractCaller) IsCursed0(opts *bind.CallOpts) (bool, error) { + var out []interface{} + err := _MockARMContract.contract.Call(opts, &out, "isCursed0") + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +func (_MockARMContract *MockARMContractSession) IsCursed0() (bool, error) { + return _MockARMContract.Contract.IsCursed0(&_MockARMContract.CallOpts) +} + +func (_MockARMContract *MockARMContractCallerSession) IsCursed0() (bool, error) { + return _MockARMContract.Contract.IsCursed0(&_MockARMContract.CallOpts) +} + +func (_MockARMContract *MockARMContractCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _MockARMContract.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_MockARMContract *MockARMContractSession) Owner() (common.Address, error) { + return _MockARMContract.Contract.Owner(&_MockARMContract.CallOpts) +} + +func (_MockARMContract *MockARMContractCallerSession) Owner() (common.Address, error) { + return _MockARMContract.Contract.Owner(&_MockARMContract.CallOpts) +} + +func (_MockARMContract *MockARMContractTransactor) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _MockARMContract.contract.Transact(opts, "acceptOwnership") +} + +func (_MockARMContract *MockARMContractSession) AcceptOwnership() (*types.Transaction, error) { + return _MockARMContract.Contract.AcceptOwnership(&_MockARMContract.TransactOpts) +} + +func (_MockARMContract *MockARMContractTransactorSession) AcceptOwnership() (*types.Transaction, error) { + return _MockARMContract.Contract.AcceptOwnership(&_MockARMContract.TransactOpts) +} + +func (_MockARMContract *MockARMContractTransactor) OwnerUnvoteToCurse(opts *bind.TransactOpts, arg0 []RMNUnvoteToCurseRecord, subject [16]byte) (*types.Transaction, error) { + return _MockARMContract.contract.Transact(opts, "ownerUnvoteToCurse", arg0, subject) +} + +func (_MockARMContract *MockARMContractSession) OwnerUnvoteToCurse(arg0 []RMNUnvoteToCurseRecord, subject [16]byte) (*types.Transaction, error) { + return _MockARMContract.Contract.OwnerUnvoteToCurse(&_MockARMContract.TransactOpts, arg0, subject) +} + +func (_MockARMContract *MockARMContractTransactorSession) OwnerUnvoteToCurse(arg0 []RMNUnvoteToCurseRecord, subject [16]byte) (*types.Transaction, error) { + return _MockARMContract.Contract.OwnerUnvoteToCurse(&_MockARMContract.TransactOpts, arg0, subject) +} + +func (_MockARMContract *MockARMContractTransactor) OwnerUnvoteToCurse0(opts *bind.TransactOpts, arg0 []RMNUnvoteToCurseRecord) (*types.Transaction, error) { + return _MockARMContract.contract.Transact(opts, "ownerUnvoteToCurse0", arg0) +} + +func (_MockARMContract *MockARMContractSession) OwnerUnvoteToCurse0(arg0 []RMNUnvoteToCurseRecord) (*types.Transaction, error) { + return _MockARMContract.Contract.OwnerUnvoteToCurse0(&_MockARMContract.TransactOpts, arg0) +} + +func (_MockARMContract *MockARMContractTransactorSession) OwnerUnvoteToCurse0(arg0 []RMNUnvoteToCurseRecord) (*types.Transaction, error) { + return _MockARMContract.Contract.OwnerUnvoteToCurse0(&_MockARMContract.TransactOpts, arg0) +} + +func (_MockARMContract *MockARMContractTransactor) SetRevert(opts *bind.TransactOpts, err []byte) (*types.Transaction, error) { + return _MockARMContract.contract.Transact(opts, "setRevert", err) +} + +func (_MockARMContract *MockARMContractSession) SetRevert(err []byte) (*types.Transaction, error) { + return _MockARMContract.Contract.SetRevert(&_MockARMContract.TransactOpts, err) +} + +func (_MockARMContract *MockARMContractTransactorSession) SetRevert(err []byte) (*types.Transaction, error) { + return _MockARMContract.Contract.SetRevert(&_MockARMContract.TransactOpts, err) +} + +func (_MockARMContract *MockARMContractTransactor) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { + return _MockARMContract.contract.Transact(opts, "transferOwnership", to) +} + +func (_MockARMContract *MockARMContractSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _MockARMContract.Contract.TransferOwnership(&_MockARMContract.TransactOpts, to) +} + +func (_MockARMContract *MockARMContractTransactorSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _MockARMContract.Contract.TransferOwnership(&_MockARMContract.TransactOpts, to) +} + +func (_MockARMContract *MockARMContractTransactor) VoteToCurse(opts *bind.TransactOpts, arg0 [32]byte) (*types.Transaction, error) { + return _MockARMContract.contract.Transact(opts, "voteToCurse", arg0) +} + +func (_MockARMContract *MockARMContractSession) VoteToCurse(arg0 [32]byte) (*types.Transaction, error) { + return _MockARMContract.Contract.VoteToCurse(&_MockARMContract.TransactOpts, arg0) +} + +func (_MockARMContract *MockARMContractTransactorSession) VoteToCurse(arg0 [32]byte) (*types.Transaction, error) { + return _MockARMContract.Contract.VoteToCurse(&_MockARMContract.TransactOpts, arg0) +} + +func (_MockARMContract *MockARMContractTransactor) VoteToCurse0(opts *bind.TransactOpts, arg0 [32]byte, subject [16]byte) (*types.Transaction, error) { + return _MockARMContract.contract.Transact(opts, "voteToCurse0", arg0, subject) +} + +func (_MockARMContract *MockARMContractSession) VoteToCurse0(arg0 [32]byte, subject [16]byte) (*types.Transaction, error) { + return _MockARMContract.Contract.VoteToCurse0(&_MockARMContract.TransactOpts, arg0, subject) +} + +func (_MockARMContract *MockARMContractTransactorSession) VoteToCurse0(arg0 [32]byte, subject [16]byte) (*types.Transaction, error) { + return _MockARMContract.Contract.VoteToCurse0(&_MockARMContract.TransactOpts, arg0, subject) +} + +type MockARMContractOwnershipTransferRequestedIterator struct { + Event *MockARMContractOwnershipTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *MockARMContractOwnershipTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(MockARMContractOwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(MockARMContractOwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *MockARMContractOwnershipTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *MockARMContractOwnershipTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type MockARMContractOwnershipTransferRequested struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_MockARMContract *MockARMContractFilterer) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*MockARMContractOwnershipTransferRequestedIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _MockARMContract.contract.FilterLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return &MockARMContractOwnershipTransferRequestedIterator{contract: _MockARMContract.contract, event: "OwnershipTransferRequested", logs: logs, sub: sub}, nil +} + +func (_MockARMContract *MockARMContractFilterer) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *MockARMContractOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _MockARMContract.contract.WatchLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(MockARMContractOwnershipTransferRequested) + if err := _MockARMContract.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_MockARMContract *MockARMContractFilterer) ParseOwnershipTransferRequested(log types.Log) (*MockARMContractOwnershipTransferRequested, error) { + event := new(MockARMContractOwnershipTransferRequested) + if err := _MockARMContract.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type MockARMContractOwnershipTransferredIterator struct { + Event *MockARMContractOwnershipTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *MockARMContractOwnershipTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(MockARMContractOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(MockARMContractOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *MockARMContractOwnershipTransferredIterator) Error() error { + return it.fail +} + +func (it *MockARMContractOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type MockARMContractOwnershipTransferred struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_MockARMContract *MockARMContractFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*MockARMContractOwnershipTransferredIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _MockARMContract.contract.FilterLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return &MockARMContractOwnershipTransferredIterator{contract: _MockARMContract.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +func (_MockARMContract *MockARMContractFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *MockARMContractOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _MockARMContract.contract.WatchLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(MockARMContractOwnershipTransferred) + if err := _MockARMContract.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_MockARMContract *MockARMContractFilterer) ParseOwnershipTransferred(log types.Log) (*MockARMContractOwnershipTransferred, error) { + event := new(MockARMContractOwnershipTransferred) + if err := _MockARMContract.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type GetConfigDetails struct { + Version uint32 + BlockNumber uint32 + Config RMNConfig +} + +func (_MockARMContract *MockARMContract) ParseLog(log types.Log) (generated.AbigenLog, error) { + switch log.Topics[0] { + case _MockARMContract.abi.Events["OwnershipTransferRequested"].ID: + return _MockARMContract.ParseOwnershipTransferRequested(log) + case _MockARMContract.abi.Events["OwnershipTransferred"].ID: + return _MockARMContract.ParseOwnershipTransferred(log) + + default: + return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) + } +} + +func (MockARMContractOwnershipTransferRequested) Topic() common.Hash { + return common.HexToHash("0xed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278") +} + +func (MockARMContractOwnershipTransferred) Topic() common.Hash { + return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") +} + +func (_MockARMContract *MockARMContract) Address() common.Address { + return _MockARMContract.address +} + +type MockARMContractInterface interface { + GetConfigDetails(opts *bind.CallOpts) (GetConfigDetails, + + error) + + IsBlessed(opts *bind.CallOpts, arg0 IRMNTaggedRoot) (bool, error) + + IsCursed(opts *bind.CallOpts, subject [16]byte) (bool, error) + + IsCursed0(opts *bind.CallOpts) (bool, error) + + Owner(opts *bind.CallOpts) (common.Address, error) + + AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) + + OwnerUnvoteToCurse(opts *bind.TransactOpts, arg0 []RMNUnvoteToCurseRecord, subject [16]byte) (*types.Transaction, error) + + OwnerUnvoteToCurse0(opts *bind.TransactOpts, arg0 []RMNUnvoteToCurseRecord) (*types.Transaction, error) + + SetRevert(opts *bind.TransactOpts, err []byte) (*types.Transaction, error) + + TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + + VoteToCurse(opts *bind.TransactOpts, arg0 [32]byte) (*types.Transaction, error) + + VoteToCurse0(opts *bind.TransactOpts, arg0 [32]byte, subject [16]byte) (*types.Transaction, error) + + FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*MockARMContractOwnershipTransferRequestedIterator, error) + + WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *MockARMContractOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferRequested(log types.Log) (*MockARMContractOwnershipTransferRequested, error) + + FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*MockARMContractOwnershipTransferredIterator, error) + + WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *MockARMContractOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferred(log types.Log) (*MockARMContractOwnershipTransferred, error) + + ParseLog(log types.Log) (generated.AbigenLog, error) + + Address() common.Address +} diff --git a/core/gethwrappers/ccip/generated/price_registry/price_registry.go b/core/gethwrappers/ccip/generated/price_registry/price_registry.go new file mode 100644 index 0000000000..f7b44ab66b --- /dev/null +++ b/core/gethwrappers/ccip/generated/price_registry/price_registry.go @@ -0,0 +1,2863 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package price_registry + +import ( + "errors" + "fmt" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" +) + +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +type AuthorizedCallersAuthorizedCallerArgs struct { + AddedCallers []common.Address + RemovedCallers []common.Address +} + +type ClientEVM2AnyMessage struct { + Receiver []byte + Data []byte + TokenAmounts []ClientEVMTokenAmount + FeeToken common.Address + ExtraArgs []byte +} + +type ClientEVMTokenAmount struct { + Token common.Address + Amount *big.Int +} + +type IPriceRegistryTokenPriceFeedConfig struct { + DataFeedAddress common.Address + TokenDecimals uint8 +} + +type InternalGasPriceUpdate struct { + DestChainSelector uint64 + UsdPerUnitGas *big.Int +} + +type InternalPriceUpdates struct { + TokenPriceUpdates []InternalTokenPriceUpdate + GasPriceUpdates []InternalGasPriceUpdate +} + +type InternalRampTokenAmount struct { + SourcePoolAddress []byte + DestTokenAddress []byte + ExtraData []byte + Amount *big.Int +} + +type InternalTimestampedPackedUint224 struct { + Value *big.Int + Timestamp uint32 +} + +type InternalTokenPriceUpdate struct { + SourceToken common.Address + UsdPerToken *big.Int +} + +type PriceRegistryDestChainConfig struct { + IsEnabled bool + MaxNumberOfTokensPerMsg uint16 + MaxDataBytes uint32 + MaxPerMsgGasLimit uint32 + DestGasOverhead uint32 + DestGasPerPayloadByte uint16 + DestDataAvailabilityOverheadGas uint32 + DestGasPerDataAvailabilityByte uint16 + DestDataAvailabilityMultiplierBps uint16 + DefaultTokenFeeUSDCents uint16 + DefaultTokenDestGasOverhead uint32 + DefaultTokenDestBytesOverhead uint32 + DefaultTxGasLimit uint32 + GasMultiplierWeiPerEth uint64 + NetworkFeeUSDCents uint32 + EnforceOutOfOrder bool + ChainFamilySelector [4]byte +} + +type PriceRegistryDestChainConfigArgs struct { + DestChainSelector uint64 + DestChainConfig PriceRegistryDestChainConfig +} + +type PriceRegistryPremiumMultiplierWeiPerEthArgs struct { + Token common.Address + PremiumMultiplierWeiPerEth uint64 +} + +type PriceRegistryStaticConfig struct { + MaxFeeJuelsPerMsg *big.Int + LinkToken common.Address + StalenessThreshold uint32 +} + +type PriceRegistryTokenPriceFeedUpdate struct { + SourceToken common.Address + FeedConfig IPriceRegistryTokenPriceFeedConfig +} + +type PriceRegistryTokenTransferFeeConfig struct { + MinFeeUSDCents uint32 + MaxFeeUSDCents uint32 + DeciBps uint16 + DestGasOverhead uint32 + DestBytesOverhead uint32 + IsEnabled bool +} + +type PriceRegistryTokenTransferFeeConfigArgs struct { + DestChainSelector uint64 + TokenTransferFeeConfigs []PriceRegistryTokenTransferFeeConfigSingleTokenArgs +} + +type PriceRegistryTokenTransferFeeConfigRemoveArgs struct { + DestChainSelector uint64 + Token common.Address +} + +type PriceRegistryTokenTransferFeeConfigSingleTokenArgs struct { + Token common.Address + TokenTransferFeeConfig PriceRegistryTokenTransferFeeConfig +} + +var PriceRegistryMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"components\":[{\"internalType\":\"uint96\",\"name\":\"maxFeeJuelsPerMsg\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"linkToken\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"stalenessThreshold\",\"type\":\"uint32\"}],\"internalType\":\"structPriceRegistry.StaticConfig\",\"name\":\"staticConfig\",\"type\":\"tuple\"},{\"internalType\":\"address[]\",\"name\":\"priceUpdaters\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"feeTokens\",\"type\":\"address[]\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"sourceToken\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"dataFeedAddress\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"tokenDecimals\",\"type\":\"uint8\"}],\"internalType\":\"structIPriceRegistry.TokenPriceFeedConfig\",\"name\":\"feedConfig\",\"type\":\"tuple\"}],\"internalType\":\"structPriceRegistry.TokenPriceFeedUpdate[]\",\"name\":\"tokenPriceFeeds\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"minFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"deciBps\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"}],\"internalType\":\"structPriceRegistry.TokenTransferFeeConfig\",\"name\":\"tokenTransferFeeConfig\",\"type\":\"tuple\"}],\"internalType\":\"structPriceRegistry.TokenTransferFeeConfigSingleTokenArgs[]\",\"name\":\"tokenTransferFeeConfigs\",\"type\":\"tuple[]\"}],\"internalType\":\"structPriceRegistry.TokenTransferFeeConfigArgs[]\",\"name\":\"tokenTransferFeeConfigArgs\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"premiumMultiplierWeiPerEth\",\"type\":\"uint64\"}],\"internalType\":\"structPriceRegistry.PremiumMultiplierWeiPerEthArgs[]\",\"name\":\"premiumMultiplierWeiPerEthArgs\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint16\",\"name\":\"maxNumberOfTokensPerMsg\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxDataBytes\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerMsgGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerPayloadByte\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destDataAvailabilityOverheadGas\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerDataAvailabilityByte\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"destDataAvailabilityMultiplierBps\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"defaultTokenFeeUSDCents\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTxGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"gasMultiplierWeiPerEth\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"networkFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"enforceOutOfOrder\",\"type\":\"bool\"},{\"internalType\":\"bytes4\",\"name\":\"chainFamilySelector\",\"type\":\"bytes4\"}],\"internalType\":\"structPriceRegistry.DestChainConfig\",\"name\":\"destChainConfig\",\"type\":\"tuple\"}],\"internalType\":\"structPriceRegistry.DestChainConfigArgs[]\",\"name\":\"destChainConfigArgs\",\"type\":\"tuple[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"chain\",\"type\":\"uint64\"}],\"name\":\"ChainNotSupported\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DataFeedValueOutOfUint224Range\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"}],\"name\":\"DestinationChainNotEnabled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ExtraArgOutOfOrderExecutionMustBeTrue\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"destBytesOverhead\",\"type\":\"uint32\"}],\"name\":\"InvalidDestBytesOverhead\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"}],\"name\":\"InvalidDestChainConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"encodedAddress\",\"type\":\"bytes\"}],\"name\":\"InvalidEVMAddress\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidExtraArgsTag\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidStaticConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"msgFeeJuels\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxFeeJuelsPerMsg\",\"type\":\"uint256\"}],\"name\":\"MessageFeeTooHigh\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MessageGasLimitTooHigh\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"maxSize\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actualSize\",\"type\":\"uint256\"}],\"name\":\"MessageTooLarge\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"SourceTokenDataTooLarge\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"threshold\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"timePassed\",\"type\":\"uint256\"}],\"name\":\"StaleGasPrice\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"TokenNotSupported\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"UnauthorizedCaller\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnsupportedNumberOfTokens\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"AuthorizedCallerAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"AuthorizedCallerRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint16\",\"name\":\"maxNumberOfTokensPerMsg\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxDataBytes\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerMsgGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerPayloadByte\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destDataAvailabilityOverheadGas\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerDataAvailabilityByte\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"destDataAvailabilityMultiplierBps\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"defaultTokenFeeUSDCents\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTxGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"gasMultiplierWeiPerEth\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"networkFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"enforceOutOfOrder\",\"type\":\"bool\"},{\"internalType\":\"bytes4\",\"name\":\"chainFamilySelector\",\"type\":\"bytes4\"}],\"indexed\":false,\"internalType\":\"structPriceRegistry.DestChainConfig\",\"name\":\"destChainConfig\",\"type\":\"tuple\"}],\"name\":\"DestChainAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint16\",\"name\":\"maxNumberOfTokensPerMsg\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxDataBytes\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerMsgGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerPayloadByte\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destDataAvailabilityOverheadGas\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerDataAvailabilityByte\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"destDataAvailabilityMultiplierBps\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"defaultTokenFeeUSDCents\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTxGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"gasMultiplierWeiPerEth\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"networkFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"enforceOutOfOrder\",\"type\":\"bool\"},{\"internalType\":\"bytes4\",\"name\":\"chainFamilySelector\",\"type\":\"bytes4\"}],\"indexed\":false,\"internalType\":\"structPriceRegistry.DestChainConfig\",\"name\":\"destChainConfig\",\"type\":\"tuple\"}],\"name\":\"DestChainConfigUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"}],\"name\":\"FeeTokenAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"}],\"name\":\"FeeTokenRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"premiumMultiplierWeiPerEth\",\"type\":\"uint64\"}],\"name\":\"PremiumMultiplierWeiPerEthUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"dataFeedAddress\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"tokenDecimals\",\"type\":\"uint8\"}],\"indexed\":false,\"internalType\":\"structIPriceRegistry.TokenPriceFeedConfig\",\"name\":\"priceFeedConfig\",\"type\":\"tuple\"}],\"name\":\"PriceFeedPerTokenUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"TokenTransferFeeConfigDeleted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"minFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"deciBps\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"}],\"indexed\":false,\"internalType\":\"structPriceRegistry.TokenTransferFeeConfig\",\"name\":\"tokenTransferFeeConfig\",\"type\":\"tuple\"}],\"name\":\"TokenTransferFeeConfigUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"}],\"name\":\"UsdPerTokenUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"destChain\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"}],\"name\":\"UsdPerUnitGasUpdated\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address[]\",\"name\":\"addedCallers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"removedCallers\",\"type\":\"address[]\"}],\"internalType\":\"structAuthorizedCallers.AuthorizedCallerArgs\",\"name\":\"authorizedCallerArgs\",\"type\":\"tuple\"}],\"name\":\"applyAuthorizedCallerUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint16\",\"name\":\"maxNumberOfTokensPerMsg\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxDataBytes\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerMsgGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerPayloadByte\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destDataAvailabilityOverheadGas\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerDataAvailabilityByte\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"destDataAvailabilityMultiplierBps\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"defaultTokenFeeUSDCents\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTxGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"gasMultiplierWeiPerEth\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"networkFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"enforceOutOfOrder\",\"type\":\"bool\"},{\"internalType\":\"bytes4\",\"name\":\"chainFamilySelector\",\"type\":\"bytes4\"}],\"internalType\":\"structPriceRegistry.DestChainConfig\",\"name\":\"destChainConfig\",\"type\":\"tuple\"}],\"internalType\":\"structPriceRegistry.DestChainConfigArgs[]\",\"name\":\"destChainConfigArgs\",\"type\":\"tuple[]\"}],\"name\":\"applyDestChainConfigUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"feeTokensToAdd\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"feeTokensToRemove\",\"type\":\"address[]\"}],\"name\":\"applyFeeTokensUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"premiumMultiplierWeiPerEth\",\"type\":\"uint64\"}],\"internalType\":\"structPriceRegistry.PremiumMultiplierWeiPerEthArgs[]\",\"name\":\"premiumMultiplierWeiPerEthArgs\",\"type\":\"tuple[]\"}],\"name\":\"applyPremiumMultiplierWeiPerEthUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"minFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"deciBps\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"}],\"internalType\":\"structPriceRegistry.TokenTransferFeeConfig\",\"name\":\"tokenTransferFeeConfig\",\"type\":\"tuple\"}],\"internalType\":\"structPriceRegistry.TokenTransferFeeConfigSingleTokenArgs[]\",\"name\":\"tokenTransferFeeConfigs\",\"type\":\"tuple[]\"}],\"internalType\":\"structPriceRegistry.TokenTransferFeeConfigArgs[]\",\"name\":\"tokenTransferFeeConfigArgs\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"internalType\":\"structPriceRegistry.TokenTransferFeeConfigRemoveArgs[]\",\"name\":\"tokensToUseDefaultFeeConfigs\",\"type\":\"tuple[]\"}],\"name\":\"applyTokenTransferFeeConfigUpdates\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"fromToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"fromTokenAmount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"toToken\",\"type\":\"address\"}],\"name\":\"convertTokenAmount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllAuthorizedCallers\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"}],\"name\":\"getDestChainConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint16\",\"name\":\"maxNumberOfTokensPerMsg\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxDataBytes\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerMsgGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerPayloadByte\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destDataAvailabilityOverheadGas\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"destGasPerDataAvailabilityByte\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"destDataAvailabilityMultiplierBps\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"defaultTokenFeeUSDCents\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTokenDestBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"defaultTxGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"gasMultiplierWeiPerEth\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"networkFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"enforceOutOfOrder\",\"type\":\"bool\"},{\"internalType\":\"bytes4\",\"name\":\"chainFamilySelector\",\"type\":\"bytes4\"}],\"internalType\":\"structPriceRegistry.DestChainConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"}],\"name\":\"getDestinationChainGasPrice\",\"outputs\":[{\"components\":[{\"internalType\":\"uint224\",\"name\":\"value\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"timestamp\",\"type\":\"uint32\"}],\"internalType\":\"structInternal.TimestampedPackedUint224\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getFeeTokens\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"getPremiumMultiplierWeiPerEth\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"premiumMultiplierWeiPerEth\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStaticConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint96\",\"name\":\"maxFeeJuelsPerMsg\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"linkToken\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"stalenessThreshold\",\"type\":\"uint32\"}],\"internalType\":\"structPriceRegistry.StaticConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"}],\"name\":\"getTokenAndGasPrices\",\"outputs\":[{\"internalType\":\"uint224\",\"name\":\"tokenPrice\",\"type\":\"uint224\"},{\"internalType\":\"uint224\",\"name\":\"gasPriceValue\",\"type\":\"uint224\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"getTokenPrice\",\"outputs\":[{\"components\":[{\"internalType\":\"uint224\",\"name\":\"value\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"timestamp\",\"type\":\"uint32\"}],\"internalType\":\"structInternal.TimestampedPackedUint224\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"getTokenPriceFeedConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"dataFeedAddress\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"tokenDecimals\",\"type\":\"uint8\"}],\"internalType\":\"structIPriceRegistry.TokenPriceFeedConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"tokens\",\"type\":\"address[]\"}],\"name\":\"getTokenPrices\",\"outputs\":[{\"components\":[{\"internalType\":\"uint224\",\"name\":\"value\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"timestamp\",\"type\":\"uint32\"}],\"internalType\":\"structInternal.TimestampedPackedUint224[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"getTokenTransferFeeConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"minFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxFeeUSDCents\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"deciBps\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destGasOverhead\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"destBytesOverhead\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isEnabled\",\"type\":\"bool\"}],\"internalType\":\"structPriceRegistry.TokenTransferFeeConfig\",\"name\":\"tokenTransferFeeConfig\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"receiver\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"tokenAmounts\",\"type\":\"tuple[]\"},{\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"}],\"internalType\":\"structClient.EVM2AnyMessage\",\"name\":\"message\",\"type\":\"tuple\"}],\"name\":\"getValidatedFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"feeTokenAmount\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"getValidatedTokenPrice\",\"outputs\":[{\"internalType\":\"uint224\",\"name\":\"\",\"type\":\"uint224\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"feeToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"feeTokenAmount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"}],\"name\":\"processMessageArgs\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"msgFeeJuels\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isOutOfOrderExecution\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"convertedExtraArgs\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sourceToken\",\"type\":\"address\"},{\"internalType\":\"uint224\",\"name\":\"usdPerToken\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.TokenPriceUpdate[]\",\"name\":\"tokenPriceUpdates\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint224\",\"name\":\"usdPerUnitGas\",\"type\":\"uint224\"}],\"internalType\":\"structInternal.GasPriceUpdate[]\",\"name\":\"gasPriceUpdates\",\"type\":\"tuple[]\"}],\"internalType\":\"structInternal.PriceUpdates\",\"name\":\"priceUpdates\",\"type\":\"tuple\"}],\"name\":\"updatePrices\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sourceToken\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"dataFeedAddress\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"tokenDecimals\",\"type\":\"uint8\"}],\"internalType\":\"structIPriceRegistry.TokenPriceFeedConfig\",\"name\":\"feedConfig\",\"type\":\"tuple\"}],\"internalType\":\"structPriceRegistry.TokenPriceFeedUpdate[]\",\"name\":\"tokenPriceFeedUpdates\",\"type\":\"tuple[]\"}],\"name\":\"updateTokenPriceFeeds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"destChainSelector\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes\",\"name\":\"sourcePoolAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"destTokenAddress\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structInternal.RampTokenAmount[]\",\"name\":\"rampTokenAmounts\",\"type\":\"tuple[]\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structClient.EVMTokenAmount[]\",\"name\":\"sourceTokenAmounts\",\"type\":\"tuple[]\"}],\"name\":\"validatePoolReturnData\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x60e06040523480156200001157600080fd5b5060405162006a4238038062006a4283398101604081905262000034916200188e565b8533806000816200008c5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000bf57620000bf8162000207565b5050604080518082018252838152815160008152602080820190935291810191909152620000ee9150620002b2565b5060208701516001600160a01b0316158062000112575086516001600160601b0316155b80620001265750604087015163ffffffff16155b15620001455760405163d794ef9560e01b815260040160405180910390fd5b6020878101516001600160a01b031660a05287516001600160601b031660805260408089015163ffffffff1660c05280516000815291820190526200018c90869062000401565b620001978462000549565b620001a2816200061a565b620001ad8262000a9f565b60408051600080825260208201909252620001fa91859190620001f3565b6040805180820190915260008082526020820152815260200190600190039081620001cb5790505b5062000b6b565b5050505050505062001b4c565b336001600160a01b03821603620002615760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000083565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b602081015160005b815181101562000342576000828281518110620002db57620002db620019ad565b60209081029190910101519050620002f560028262000ea4565b1562000338576040516001600160a01b03821681527fc3803387881faad271c47728894e3e36fac830ffc8602ca6fc07733cbda775809060200160405180910390a15b50600101620002ba565b50815160005b8151811015620003fb576000828281518110620003695762000369620019ad565b6020026020010151905060006001600160a01b0316816001600160a01b031603620003a7576040516342bcdf7f60e11b815260040160405180910390fd5b620003b460028262000ec4565b506040516001600160a01b03821681527feb1b9b92e50b7f88f9ff25d56765095ac6e91540eee214906f4036a908ffbdef9060200160405180910390a15060010162000348565b50505050565b60005b8251811015620004a25762000440838281518110620004275762000427620019ad565b6020026020010151600a62000ec460201b90919060201c565b1562000499578281815181106200045b576200045b620019ad565b60200260200101516001600160a01b03167fdf1b1bd32a69711488d71554706bb130b1fc63a5fa1a2cd85e8440f84065ba2360405160405180910390a25b60010162000404565b5060005b81518110156200054457620004e2828281518110620004c957620004c9620019ad565b6020026020010151600a62000edb60201b90919060201c565b156200053b57818181518110620004fd57620004fd620019ad565b60200260200101516001600160a01b03167f1795838dc8ab2ffc5f431a1729a6afa0b587f982f7b2be0b9d7187a1ef547f9160405160405180910390a25b600101620004a6565b505050565b60005b8151811015620006165760008282815181106200056d576200056d620019ad565b6020908102919091018101518051818301516001600160a01b0380831660008181526006875260409081902084518154868a018051929096166001600160a81b03199091168117600160a01b60ff9384160217909255825191825293519093169683019690965293955091939092917f08a5f7f5bb38a81d8e43aca13ecd76431dbf8816ae4699affff7b00b2fc1c464910160405180910390a25050508060010190506200054c565b5050565b60005b8151811015620006165760008282815181106200063e576200063e620019ad565b6020026020010151905060008383815181106200065f576200065f620019ad565b6020026020010151600001519050600082602001519050816001600160401b03166000148062000698575061018081015163ffffffff16155b80620006ba57506102008101516001600160e01b031916630a04b54b60e21b14155b80620006d75750602063ffffffff1681610160015163ffffffff16105b80620006f75750806060015163ffffffff1681610180015163ffffffff16115b15620007225760405163c35aa79d60e01b81526001600160401b038316600482015260240162000083565b6001600160401b038216600090815260086020526040812060010154600160a81b900460e01b6001600160e01b0319169003620007a257816001600160401b03167fa937382a486d993de71c220bc8b559242deb4e286a353fa732330b4aa7d1357782604051620007949190620019c3565b60405180910390a2620007e6565b816001600160401b03167fa7b607fc10d28a1caf39ab7d27f4c94945db708a576d572781a455c5894fad9382604051620007dd9190620019c3565b60405180910390a25b8060086000846001600160401b03166001600160401b0316815260200190815260200160002060008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a81548161ffff021916908361ffff16021790555060408201518160000160036101000a81548163ffffffff021916908363ffffffff16021790555060608201518160000160076101000a81548163ffffffff021916908363ffffffff160217905550608082015181600001600b6101000a81548163ffffffff021916908363ffffffff16021790555060a082015181600001600f6101000a81548161ffff021916908361ffff16021790555060c08201518160000160116101000a81548163ffffffff021916908363ffffffff16021790555060e08201518160000160156101000a81548161ffff021916908361ffff1602179055506101008201518160000160176101000a81548161ffff021916908361ffff1602179055506101208201518160000160196101000a81548161ffff021916908361ffff16021790555061014082015181600001601b6101000a81548163ffffffff021916908363ffffffff1602179055506101608201518160010160006101000a81548163ffffffff021916908363ffffffff1602179055506101808201518160010160046101000a81548163ffffffff021916908363ffffffff1602179055506101a08201518160010160086101000a8154816001600160401b0302191690836001600160401b031602179055506101c08201518160010160106101000a81548163ffffffff021916908363ffffffff1602179055506101e08201518160010160146101000a81548160ff0219169083151502179055506102008201518160010160156101000a81548163ffffffff021916908360e01c02179055509050505050508060010190506200061d565b60005b81518110156200061657600082828151811062000ac35762000ac3620019ad565b6020026020010151600001519050600083838151811062000ae85762000ae8620019ad565b6020908102919091018101518101516001600160a01b03841660008181526007845260409081902080546001600160401b0319166001600160401b0385169081179091559051908152919350917fbb77da6f7210cdd16904228a9360133d1d7dfff99b1bc75f128da5b53e28f97d910160405180910390a2505060010162000aa2565b60005b825181101562000dde57600083828151811062000b8f5762000b8f620019ad565b6020026020010151905060008160000151905060005b82602001515181101562000dcf5760008360200151828151811062000bce5762000bce620019ad565b602002602001015160200151905060008460200151838151811062000bf75762000bf7620019ad565b6020026020010151600001519050602063ffffffff16826080015163ffffffff16101562000c565760808201516040516312766e0160e11b81526001600160a01b038316600482015263ffffffff909116602482015260440162000083565b6001600160401b03841660008181526009602090815260408083206001600160a01b0386168085529083529281902086518154938801518389015160608a015160808b015160a08c01511515600160901b0260ff60901b1963ffffffff928316600160701b021664ffffffffff60701b199383166a01000000000000000000000263ffffffff60501b1961ffff90961668010000000000000000029590951665ffffffffffff60401b19968416640100000000026001600160401b0319909b16939097169290921798909817939093169390931717919091161792909217909155519091907f94967ae9ea7729ad4f54021c1981765d2b1d954f7c92fbec340aa0a54f46b8b59062000dbc908690600060c08201905063ffffffff80845116835280602085015116602084015261ffff60408501511660408401528060608501511660608401528060808501511660808401525060a0830151151560a083015292915050565b60405180910390a3505060010162000ba5565b50505080600101905062000b6e565b5060005b81518110156200054457600082828151811062000e035762000e03620019ad565b6020026020010151600001519050600083838151811062000e285762000e28620019ad565b6020908102919091018101518101516001600160401b03841660008181526009845260408082206001600160a01b038516808452955280822080546001600160981b03191690555192945090917f4de5b1bcbca6018c11303a2c3f4a4b4f22a1c741d8c4ba430d246ac06c5ddf8b9190a3505060010162000de2565b600062000ebb836001600160a01b03841662000ef2565b90505b92915050565b600062000ebb836001600160a01b03841662000ff6565b600062000ebb836001600160a01b03841662001048565b6000818152600183016020526040812054801562000feb57600062000f1960018362001b14565b855490915060009062000f2f9060019062001b14565b905081811462000f9b57600086600001828154811062000f535762000f53620019ad565b906000526020600020015490508087600001848154811062000f795762000f79620019ad565b6000918252602080832090910192909255918252600188019052604090208390555b855486908062000faf5762000faf62001b36565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505062000ebe565b600091505062000ebe565b60008181526001830160205260408120546200103f5750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915562000ebe565b50600062000ebe565b6000818152600183016020526040812054801562000feb5760006200106f60018362001b14565b8554909150600090620010859060019062001b14565b905080821462000f9b57600086600001828154811062000f535762000f53620019ad565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b0381118282101715620010e457620010e4620010a9565b60405290565b60405160c081016001600160401b0381118282101715620010e457620010e4620010a9565b60405161022081016001600160401b0381118282101715620010e457620010e4620010a9565b604051601f8201601f191681016001600160401b0381118282101715620011605762001160620010a9565b604052919050565b80516001600160a01b03811681146200118057600080fd5b919050565b805163ffffffff811681146200118057600080fd5b600060608284031215620011ad57600080fd5b604051606081016001600160401b0381118282101715620011d257620011d2620010a9565b604052825190915081906001600160601b0381168114620011f257600080fd5b8152620012026020840162001168565b6020820152620012156040840162001185565b60408201525092915050565b60006001600160401b038211156200123d576200123d620010a9565b5060051b60200190565b600082601f8301126200125957600080fd5b81516020620012726200126c8362001221565b62001135565b8083825260208201915060208460051b8701019350868411156200129557600080fd5b602086015b84811015620012bc57620012ae8162001168565b83529183019183016200129a565b509695505050505050565b600082601f830112620012d957600080fd5b81516020620012ec6200126c8362001221565b828152606092830285018201928282019190878511156200130c57600080fd5b8387015b858110156200139f57808903828112156200132b5760008081fd5b62001335620010bf565b620013408362001168565b8152604080601f1984011215620013575760008081fd5b62001361620010bf565b92506200137088850162001168565b835283015160ff81168114620013865760008081fd5b8288015280870191909152845292840192810162001310565b5090979650505050505050565b80516001600160401b03811681146200118057600080fd5b805161ffff811681146200118057600080fd5b805180151581146200118057600080fd5b600082601f830112620013fa57600080fd5b815160206200140d6200126c8362001221565b82815260059290921b840181019181810190868411156200142d57600080fd5b8286015b84811015620012bc5780516001600160401b03808211156200145257600080fd5b908801906040601f19838c0381018213156200146d57600080fd5b62001477620010bf565b62001484898601620013ac565b815282850151848111156200149857600080fd5b8086019550508c603f860112620014ae57600080fd5b888501519350620014c36200126c8562001221565b84815260e09094028501830193898101908e861115620014e257600080fd5b958401955b85871015620015bb57868f0360e08112156200150257600080fd5b6200150c620010bf565b620015178962001168565b815260c086830112156200152a57600080fd5b62001534620010ea565b9150620015438d8a0162001185565b825262001552878a0162001185565b8d8301526200156460608a01620013c4565b878301526200157660808a0162001185565b60608301526200158960a08a0162001185565b60808301526200159c60c08a01620013d7565b60a0830152808d0191909152825260e09690960195908a0190620014e7565b828b01525087525050509284019250830162001431565b600082601f830112620015e457600080fd5b81516020620015f76200126c8362001221565b82815260069290921b840181019181810190868411156200161757600080fd5b8286015b84811015620012bc5760408189031215620016365760008081fd5b62001640620010bf565b6200164b8262001168565b81526200165a858301620013ac565b818601528352918301916040016200161b565b80516001600160e01b0319811681146200118057600080fd5b600082601f8301126200169857600080fd5b81516020620016ab6200126c8362001221565b8281526102409283028501820192828201919087851115620016cc57600080fd5b8387015b858110156200139f5780890382811215620016eb5760008081fd5b620016f5620010bf565b6200170083620013ac565b815261022080601f1984011215620017185760008081fd5b620017226200110f565b925062001731888501620013d7565b8352604062001742818601620013c4565b8985015260606200175581870162001185565b82860152608091506200176a82870162001185565b9085015260a06200177d86820162001185565b8286015260c0915062001792828701620013c4565b9085015260e0620017a586820162001185565b828601526101009150620017bb828701620013c4565b90850152610120620017cf868201620013c4565b828601526101409150620017e5828701620013c4565b90850152610160620017f986820162001185565b8286015261018091506200180f82870162001185565b908501526101a06200182386820162001185565b828601526101c0915062001839828701620013ac565b908501526101e06200184d86820162001185565b82860152610200915062001863828701620013d7565b90850152620018748583016200166d565b9084015250808701919091528452928401928101620016d0565b6000806000806000806000610120888a031215620018ab57600080fd5b620018b789896200119a565b60608901519097506001600160401b0380821115620018d557600080fd5b620018e38b838c0162001247565b975060808a0151915080821115620018fa57600080fd5b620019088b838c0162001247565b965060a08a01519150808211156200191f57600080fd5b6200192d8b838c01620012c7565b955060c08a01519150808211156200194457600080fd5b620019528b838c01620013e8565b945060e08a01519150808211156200196957600080fd5b620019778b838c01620015d2565b93506101008a01519150808211156200198f57600080fd5b506200199e8a828b0162001686565b91505092959891949750929550565b634e487b7160e01b600052603260045260246000fd5b81511515815261022081016020830151620019e4602084018261ffff169052565b506040830151620019fd604084018263ffffffff169052565b50606083015162001a16606084018263ffffffff169052565b50608083015162001a2f608084018263ffffffff169052565b5060a083015162001a4660a084018261ffff169052565b5060c083015162001a5f60c084018263ffffffff169052565b5060e083015162001a7660e084018261ffff169052565b506101008381015161ffff9081169184019190915261012080850151909116908301526101408084015163ffffffff9081169184019190915261016080850151821690840152610180808501518216908401526101a0808501516001600160401b0316908401526101c080850151909116908301526101e080840151151590830152610200928301516001600160e01b031916929091019190915290565b8181038181111562000ebe57634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603160045260246000fd5b60805160a05160c051614e9c62001ba6600039600081816102d901528181611ad30152611b3c01526000818161029d0152818161104e01526110ae015260008181610269015281816110d701526111470152614e9c6000f3fe608060405234801561001057600080fd5b50600436106101b85760003560e01c80637afac322116100f9578063cc88924c11610097578063d8694ccd11610071578063d8694ccd14610a5c578063f2fde38b14610a6f578063f700042a14610a82578063ffdb4b3714610a9557600080fd5b8063cc88924c14610a2e578063cdc73d5114610a41578063d02641a014610a4957600080fd5b806391a2749a116100d357806391a2749a14610939578063a69c64c01461094c578063bf78e03f1461095f578063c4276bfc14610a0c57600080fd5b80637afac3221461078e57806382b49eb0146107a15780638da5cb5b1461091157600080fd5b8063407e108611610166578063514e8cff11610140578063514e8cff146104385780636def4ce7146104db578063770e2dc41461077357806379ba50971461078657600080fd5b8063407e1086146103c557806345ac924d146103d85780634ab35b0b146103f857600080fd5b8063181f5a7711610197578063181f5a77146103525780632451a6271461039b5780633937306f146103b057600080fd5b806241e5be146101bd578063061877e3146101e357806306285c691461023c575b600080fd5b6101d06101cb366004613889565b610add565b6040519081526020015b60405180910390f35b6102236101f13660046138c5565b73ffffffffffffffffffffffffffffffffffffffff1660009081526007602052604090205467ffffffffffffffff1690565b60405167ffffffffffffffff90911681526020016101da565b610306604080516060810182526000808252602082018190529181019190915260405180606001604052807f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff1681526020017f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1681526020017f000000000000000000000000000000000000000000000000000000000000000063ffffffff16815250905090565b6040805182516bffffffffffffffffffffffff16815260208084015173ffffffffffffffffffffffffffffffffffffffff16908201529181015163ffffffff16908201526060016101da565b61038e6040518060400160405280601781526020017f5072696365526567697374727920312e362e302d64657600000000000000000081525081565b6040516101da9190613944565b6103a3610b4b565b6040516101da9190613957565b6103c36103be3660046139b1565b610b5c565b005b6103c36103d3366004613b0d565b610e11565b6103eb6103e6366004613c6b565b610e25565b6040516101da9190613cad565b61040b6104063660046138c5565b610ef0565b6040517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff90911681526020016101da565b6104ce610446366004613d40565b60408051808201909152600080825260208201525067ffffffffffffffff166000908152600460209081526040918290208251808401909352547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811683527c0100000000000000000000000000000000000000000000000000000000900463ffffffff169082015290565b6040516101da9190613d5b565b6107666104e9366004613d40565b6040805161022081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810182905261018081018290526101a081018290526101c081018290526101e081018290526102008101919091525067ffffffffffffffff908116600090815260086020908152604091829020825161022081018452815460ff8082161515835261ffff61010080840482169685019690965263ffffffff630100000084048116978501979097526701000000000000008304871660608501526b0100000000000000000000008304871660808501526f010000000000000000000000000000008304811660a0850152710100000000000000000000000000000000008304871660c08501527501000000000000000000000000000000000000000000808404821660e08087019190915277010000000000000000000000000000000000000000000000850483169786019790975279010000000000000000000000000000000000000000000000000084049091166101208501527b010000000000000000000000000000000000000000000000000000009092048616610140840152600190930154808616610160840152640100000000810486166101808401526801000000000000000081049096166101a083015270010000000000000000000000000000000086049094166101c082015274010000000000000000000000000000000000000000850490911615156101e08201527fffffffff0000000000000000000000000000000000000000000000000000000092909304901b1661020082015290565b6040516101da9190613d96565b6103c3610781366004613fd3565b610efb565b6103c3610f11565b6103c361079c3660046142ed565b611013565b6108b16107af366004614351565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a08101919091525067ffffffffffffffff91909116600090815260096020908152604080832073ffffffffffffffffffffffffffffffffffffffff94909416835292815290829020825160c081018452905463ffffffff8082168352640100000000820481169383019390935268010000000000000000810461ffff16938201939093526a01000000000000000000008304821660608201526e01000000000000000000000000000083049091166080820152720100000000000000000000000000000000000090910460ff16151560a082015290565b6040516101da9190600060c08201905063ffffffff80845116835280602085015116602084015261ffff60408501511660408401528060608501511660608401528060808501511660808401525060a0830151151560a083015292915050565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101da565b6103c361094736600461437b565b611025565b6103c361095a36600461440c565b611036565b6109d861096d3660046138c5565b6040805180820182526000808252602091820181905273ffffffffffffffffffffffffffffffffffffffff93841681526006825282902082518084019093525492831682527401000000000000000000000000000000000000000090920460ff169181019190915290565b60408051825173ffffffffffffffffffffffffffffffffffffffff16815260209283015160ff1692810192909252016101da565b610a1f610a1a3660046144d1565b611047565b6040516101da9392919061456c565b6103c3610a3c366004614596565b611245565b6103a361141b565b6104ce610a573660046138c5565b611427565b6101d0610a6a366004614631565b611523565b6103c3610a7d3660046138c5565b6119dd565b6103c3610a903660046146b6565b6119ee565b610aa8610aa33660046148d6565b6119ff565b604080517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff9384168152929091166020830152016101da565b6000610ae882611b8a565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16610b0f85611b8a565b610b37907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff168561492f565b610b419190614946565b90505b9392505050565b6060610b576002611c24565b905090565b610b64611c31565b6000610b708280614981565b9050905060005b81811015610cba576000610b8b8480614981565b83818110610b9b57610b9b6149e9565b905060400201803603810190610bb19190614a44565b604080518082018252602080840180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff908116845263ffffffff42818116858701908152885173ffffffffffffffffffffffffffffffffffffffff9081166000908152600590975295889020965190519092167c010000000000000000000000000000000000000000000000000000000002919092161790935584519051935194955016927f52f50aa6d1a95a4595361ecf953d095f125d442e4673716dede699e049de148a92610ca99290917bffffffffffffffffffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b60405180910390a250600101610b77565b506000610cca6020840184614981565b9050905060005b81811015610e0b576000610ce86020860186614981565b83818110610cf857610cf86149e9565b905060400201803603810190610d0e9190614a81565b604080518082018252602080840180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff908116845263ffffffff42818116858701908152885167ffffffffffffffff9081166000908152600490975295889020965190519092167c010000000000000000000000000000000000000000000000000000000002919092161790935584519051935194955016927fdd84a3fa9ef9409f550d54d6affec7e9c480c878c6ab27b78912a03e1b371c6e92610dfa9290917bffffffffffffffffffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b60405180910390a250600101610cd1565b50505050565b610e19611c76565b610e2281611cf7565b50565b60608160008167ffffffffffffffff811115610e4357610e436139ec565b604051908082528060200260200182016040528015610e8857816020015b6040805180820190915260008082526020820152815260200190600190039081610e615790505b50905060005b82811015610ee557610ec0868683818110610eab57610eab6149e9565b9050602002016020810190610a5791906138c5565b828281518110610ed257610ed26149e9565b6020908102919091010152600101610e8e565b509150505b92915050565b6000610eea82611b8a565b610f03611c76565b610f0d8282611df5565b5050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610f97576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b61101b611c76565b610f0d8282612207565b61102d611c76565b610e228161234e565b61103e611c76565b610e22816124da565b60008060607f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff16036110a7578592506110d5565b6110d287877f0000000000000000000000000000000000000000000000000000000000000000610add565b92505b7f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff16831115611174576040517f6a92a483000000000000000000000000000000000000000000000000000000008152600481018490526bffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166024820152604401610f8e565b67ffffffffffffffff8816600090815260086020526040812060010154640100000000900463ffffffff16906111ab8787846125c4565b9050806020015193508484611232836040805182516024820152602092830151151560448083019190915282518083039091018152606490910190915290810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f181dcf100000000000000000000000000000000000000000000000000000000017905290565b9450945094505050955095509592505050565b67ffffffffffffffff85166000908152600860205260408120600101547501000000000000000000000000000000000000000000900460e01b905b8481101561141257600084848381811061129c5761129c6149e9565b6112b292602060409092020190810191506138c5565b905060008787848181106112c8576112c86149e9565b90506020028101906112da9190614aa4565b6112e8906040810190614ae2565b91505060208111156113985767ffffffffffffffff8916600090815260096020908152604080832073ffffffffffffffffffffffffffffffffffffffff861684529091529020546e010000000000000000000000000000900463ffffffff16811115611398576040517f36f536ca00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83166004820152602401610f8e565b611408848989868181106113ae576113ae6149e9565b90506020028101906113c09190614aa4565b6113ce906020810190614ae2565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061276d92505050565b5050600101611280565b50505050505050565b6060610b57600a611c24565b604080518082019091526000808252602082015273ffffffffffffffffffffffffffffffffffffffff8281166000908152600660209081526040918290208251808401909352549283168083527401000000000000000000000000000000000000000090930460ff16908201529061151a57505073ffffffffffffffffffffffffffffffffffffffff166000908152600560209081526040918290208251808401909352547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811683527c0100000000000000000000000000000000000000000000000000000000900463ffffffff169082015290565b610b44816127bf565b67ffffffffffffffff8083166000908152600860209081526040808320815161022081018352815460ff808216151580845261ffff61010080850482169886019890985263ffffffff630100000085048116978601979097526701000000000000008404871660608601526b0100000000000000000000008404871660808601526f010000000000000000000000000000008404811660a0860152710100000000000000000000000000000000008404871660c08601527501000000000000000000000000000000000000000000808504821660e08088019190915277010000000000000000000000000000000000000000000000860483169987019990995279010000000000000000000000000000000000000000000000000085049091166101208601527b010000000000000000000000000000000000000000000000000000009093048616610140850152600190940154808616610160850152640100000000810486166101808501526801000000000000000081049098166101a084015270010000000000000000000000000000000088049094166101c083015274010000000000000000000000000000000000000000870490931615156101e08201527fffffffff000000000000000000000000000000000000000000000000000000009290950490921b16610200840152909190611759576040517f99ac52f200000000000000000000000000000000000000000000000000000000815267ffffffffffffffff85166004820152602401610f8e565b60006117686040850185614981565b91506117c490508261177d6020870187614ae2565b90508361178a8880614ae2565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612a0292505050565b60006007816117d960808801606089016138c5565b73ffffffffffffffffffffffffffffffffffffffff168152602081019190915260400160009081205467ffffffffffffffff169150806118286118226080890160608a016138c5565b896119ff565b909250905060008080861561186e57611862888c61184c60808e0160608f016138c5565b888e806040019061185d9190614981565b612aac565b9194509250905061188e565b6101c088015161188b9063ffffffff16662386f26fc1000061492f565b92505b61010088015160009061ffff16156118d2576118cf896dffffffffffffffffffffffffffff607088901c166118c660208f018f614ae2565b90508b86612d8a565b90505b6101a089015160009067ffffffffffffffff166118fb6118f560808f018f614ae2565b8d612e3a565b600001518563ffffffff168c60a0015161ffff168f806020019061191f9190614ae2565b61192a92915061492f565b8d6080015163ffffffff1661193f9190614b47565b6119499190614b47565b6119539190614b47565b61196d906dffffffffffffffffffffffffffff891661492f565b611977919061492f565b90507bffffffffffffffffffffffffffffffffffffffffffffffffffffffff871682826119ae67ffffffffffffffff8c168961492f565b6119b89190614b47565b6119c29190614b47565b6119cc9190614946565b9d9c50505050505050505050505050565b6119e5611c76565b610e2281612efb565b6119f6611c76565b610e2281612ff0565b67ffffffffffffffff811660009081526004602090815260408083208151808301909252547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811682527c0100000000000000000000000000000000000000000000000000000000900463ffffffff1691810182905282918203611ab7576040517f2e59db3a00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff85166004820152602401610f8e565b6000816020015163ffffffff1642611acf9190614b5a565b90507f000000000000000000000000000000000000000000000000000000000000000063ffffffff16811115611b70576040517ff08bcb3e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8616600482015263ffffffff7f000000000000000000000000000000000000000000000000000000000000000016602482015260448101829052606401610f8e565b611b7986611b8a565b9151919350909150505b9250929050565b600080611b9683611427565b9050806020015163ffffffff1660001480611bce575080517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16155b15611c1d576040517f06439c6b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152602401610f8e565b5192915050565b60606000610b44836134de565b611c3c60023361353a565b611c74576040517fd86ad9cf000000000000000000000000000000000000000000000000000000008152336004820152602401610f8e565b565b60005473ffffffffffffffffffffffffffffffffffffffff163314611c74576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610f8e565b60005b8151811015610f0d576000828281518110611d1757611d176149e9565b60209081029190910181015180518183015173ffffffffffffffffffffffffffffffffffffffff80831660008181526006875260409081902084518154868a018051929096167fffffffffffffffffffffff00000000000000000000000000000000000000000090911681177401000000000000000000000000000000000000000060ff9384160217909255825191825293519093169683019690965293955091939092917f08a5f7f5bb38a81d8e43aca13ecd76431dbf8816ae4699affff7b00b2fc1c464910160405180910390a2505050806001019050611cfa565b60005b825181101561211e576000838281518110611e1557611e156149e9565b6020026020010151905060008160000151905060005b82602001515181101561211057600083602001518281518110611e5057611e506149e9565b6020026020010151602001519050600084602001518381518110611e7657611e766149e9565b6020026020010151600001519050602063ffffffff16826080015163ffffffff161015611ef95760808201516040517f24ecdc0200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316600482015263ffffffff9091166024820152604401610f8e565b67ffffffffffffffff8416600081815260096020908152604080832073ffffffffffffffffffffffffffffffffffffffff86168085529083529281902086518154938801518389015160608a015160808b015160a08c015115157201000000000000000000000000000000000000027fffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffff63ffffffff9283166e01000000000000000000000000000002167fffffffffffffffffffffffffff0000000000ffffffffffffffffffffffffffff9383166a0100000000000000000000027fffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffff61ffff9096166801000000000000000002959095167fffffffffffffffffffffffffffffffffffff000000000000ffffffffffffffff968416640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000909b16939097169290921798909817939093169390931717919091161792909217909155519091907f94967ae9ea7729ad4f54021c1981765d2b1d954f7c92fbec340aa0a54f46b8b5906120fe908690600060c08201905063ffffffff80845116835280602085015116602084015261ffff60408501511660408401528060608501511660608401528060808501511660808401525060a0830151151560a083015292915050565b60405180910390a35050600101611e2b565b505050806001019050611df8565b5060005b815181101561220257600082828151811061213f5761213f6149e9565b60200260200101516000015190506000838381518110612161576121616149e9565b60209081029190910181015181015167ffffffffffffffff8416600081815260098452604080822073ffffffffffffffffffffffffffffffffffffffff8516808452955280822080547fffffffffffffffffffffffffff000000000000000000000000000000000000001690555192945090917f4de5b1bcbca6018c11303a2c3f4a4b4f22a1c741d8c4ba430d246ac06c5ddf8b9190a35050600101612122565b505050565b60005b82518110156122aa57612240838281518110612228576122286149e9565b6020026020010151600a61356990919063ffffffff16565b156122a257828181518110612257576122576149e9565b602002602001015173ffffffffffffffffffffffffffffffffffffffff167fdf1b1bd32a69711488d71554706bb130b1fc63a5fa1a2cd85e8440f84065ba2360405160405180910390a25b60010161220a565b5060005b8151811015612202576122e48282815181106122cc576122cc6149e9565b6020026020010151600a61358b90919063ffffffff16565b15612346578181815181106122fb576122fb6149e9565b602002602001015173ffffffffffffffffffffffffffffffffffffffff167f1795838dc8ab2ffc5f431a1729a6afa0b587f982f7b2be0b9d7187a1ef547f9160405160405180910390a25b6001016122ae565b602081015160005b81518110156123e9576000828281518110612373576123736149e9565b602002602001015190506123918160026135ad90919063ffffffff16565b156123e05760405173ffffffffffffffffffffffffffffffffffffffff821681527fc3803387881faad271c47728894e3e36fac830ffc8602ca6fc07733cbda775809060200160405180910390a15b50600101612356565b50815160005b8151811015610e0b57600082828151811061240c5761240c6149e9565b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361247c576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612487600282613569565b5060405173ffffffffffffffffffffffffffffffffffffffff821681527feb1b9b92e50b7f88f9ff25d56765095ac6e91540eee214906f4036a908ffbdef9060200160405180910390a1506001016123ef565b60005b8151811015610f0d5760008282815181106124fa576124fa6149e9565b6020026020010151600001519050600083838151811061251c5761251c6149e9565b60209081029190910181015181015173ffffffffffffffffffffffffffffffffffffffff841660008181526007845260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff85169081179091559051908152919350917fbb77da6f7210cdd16904228a9360133d1d7dfff99b1bc75f128da5b53e28f97d910160405180910390a250506001016124dd565b6040805180820190915260008082526020820152600083900361260557506040805180820190915267ffffffffffffffff8216815260006020820152610b44565b60006126118486614b6d565b905060006126228560048189614bb3565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293505050507fffffffff0000000000000000000000000000000000000000000000000000000082167fe7e230f000000000000000000000000000000000000000000000000000000000016126bf57808060200190518101906126b69190614bdd565b92505050610b44565b7f6859a837000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083160161273b576040518060400160405280828060200190518101906127279190614c09565b815260006020909101529250610b44915050565b6040517f5247fdce00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fd7ed2ad4000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831601610f0d57612202816135cf565b604080518082019091526000808252602082015260008260000151905060008173ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015612829573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061284d9190614c3c565b505050915050600081121561288e576040517f10cb51d100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000819050600085602001518473ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156128e5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129099190614c8c565b6129139190614ca9565b905060248160ff1611156129485761292c602482614cc2565b61293790600a614dfb565b6129419083614946565b915061296b565b612953816024614cc2565b61295e90600a614dfb565b612968908361492f565b91505b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8211156129c1576040517f10cb51d100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50604080518082019091527bffffffffffffffffffffffffffffffffffffffffffffffffffffffff909116815263ffffffff42166020820152949350505050565b836040015163ffffffff16831115612a5b5760408085015190517f8693378900000000000000000000000000000000000000000000000000000000815263ffffffff909116600482015260248101849052604401610f8e565b836020015161ffff16821115612a9d576040517f4c056b6a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e0b8461020001518261276d565b6000808083815b81811015612d7c576000878783818110612acf57612acf6149e9565b905060400201803603810190612ae59190614e0a565b67ffffffffffffffff8c166000908152600960209081526040808320845173ffffffffffffffffffffffffffffffffffffffff168452825291829020825160c081018452905463ffffffff8082168352640100000000820481169383019390935268010000000000000000810461ffff16938201939093526a01000000000000000000008304821660608201526e01000000000000000000000000000083049091166080820152720100000000000000000000000000000000000090910460ff16151560a0820181905291925090612c0b576101208d0151612bd29061ffff16662386f26fc1000061492f565b612bdc9088614b47565b96508c610140015186612bef9190614e43565b95508c610160015185612c029190614e43565b94505050612d74565b604081015160009061ffff1615612cc45760008c73ffffffffffffffffffffffffffffffffffffffff16846000015173ffffffffffffffffffffffffffffffffffffffff1614612c67578351612c6090611b8a565b9050612c6a565b508a5b620186a0836040015161ffff16612cac8660200151847bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1661368290919063ffffffff16565b612cb6919061492f565b612cc09190614946565b9150505b6060820151612cd39088614e43565b9650816080015186612ce59190614e43565b8251909650600090612d049063ffffffff16662386f26fc1000061492f565b905080821015612d2357612d18818a614b47565b985050505050612d74565b6000836020015163ffffffff16662386f26fc10000612d42919061492f565b905080831115612d6257612d56818b614b47565b99505050505050612d74565b612d6c838b614b47565b995050505050505b600101612ab3565b505096509650969350505050565b60008063ffffffff8316612da06101408661492f565b612dac876101c0614b47565b612db69190614b47565b612dc09190614b47565b905060008760c0015163ffffffff168860e0015161ffff1683612de3919061492f565b612ded9190614b47565b61010089015190915061ffff16612e146dffffffffffffffffffffffffffff89168361492f565b612e1e919061492f565b612e2e90655af3107a400061492f565b98975050505050505050565b60408051808201909152600080825260208201526000612e66858585610180015163ffffffff166125c4565b9050826060015163ffffffff1681600001511115612eb0576040517f4c4fc93a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b826101e001518015612ec457508060200151155b15610b41576040517fee433e9900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff821603612f7a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610f8e565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60005b8151811015610f0d576000828281518110613010576130106149e9565b60200260200101519050600083838151811061302e5761302e6149e9565b60200260200101516000015190506000826020015190508167ffffffffffffffff1660001480613067575061018081015163ffffffff16155b806130b957506102008101517fffffffff00000000000000000000000000000000000000000000000000000000167f2812d52c0000000000000000000000000000000000000000000000000000000014155b806130d55750602063ffffffff1681610160015163ffffffff16105b806130f45750806060015163ffffffff1681610180015163ffffffff16115b15613137576040517fc35aa79d00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff83166004820152602401610f8e565b67ffffffffffffffff82166000908152600860205260408120600101547501000000000000000000000000000000000000000000900460e01b7fffffffff000000000000000000000000000000000000000000000000000000001690036131df578167ffffffffffffffff167fa937382a486d993de71c220bc8b559242deb4e286a353fa732330b4aa7d13577826040516131d29190613d96565b60405180910390a2613222565b8167ffffffffffffffff167fa7b607fc10d28a1caf39ab7d27f4c94945db708a576d572781a455c5894fad93826040516132199190613d96565b60405180910390a25b80600860008467ffffffffffffffff1667ffffffffffffffff16815260200190815260200160002060008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a81548161ffff021916908361ffff16021790555060408201518160000160036101000a81548163ffffffff021916908363ffffffff16021790555060608201518160000160076101000a81548163ffffffff021916908363ffffffff160217905550608082015181600001600b6101000a81548163ffffffff021916908363ffffffff16021790555060a082015181600001600f6101000a81548161ffff021916908361ffff16021790555060c08201518160000160116101000a81548163ffffffff021916908363ffffffff16021790555060e08201518160000160156101000a81548161ffff021916908361ffff1602179055506101008201518160000160176101000a81548161ffff021916908361ffff1602179055506101208201518160000160196101000a81548161ffff021916908361ffff16021790555061014082015181600001601b6101000a81548163ffffffff021916908363ffffffff1602179055506101608201518160010160006101000a81548163ffffffff021916908363ffffffff1602179055506101808201518160010160046101000a81548163ffffffff021916908363ffffffff1602179055506101a08201518160010160086101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506101c08201518160010160106101000a81548163ffffffff021916908363ffffffff1602179055506101e08201518160010160146101000a81548160ff0219169083151502179055506102008201518160010160156101000a81548163ffffffff021916908360e01c0217905550905050505050806001019050612ff3565b60608160000180548060200260200160405190810160405280929190818152602001828054801561352e57602002820191906000526020600020905b81548152602001906001019080831161351a575b50505050509050919050565b73ffffffffffffffffffffffffffffffffffffffff811660009081526001830160205260408120541515610b44565b6000610b448373ffffffffffffffffffffffffffffffffffffffff84166136bf565b6000610b448373ffffffffffffffffffffffffffffffffffffffff841661370e565b6000610b448373ffffffffffffffffffffffffffffffffffffffff8416613808565b6000815160201461360e57816040517f8d666f60000000000000000000000000000000000000000000000000000000008152600401610f8e9190613944565b6000828060200190518101906136249190614c09565b905073ffffffffffffffffffffffffffffffffffffffff811180613649575061040081105b15610eea57826040517f8d666f60000000000000000000000000000000000000000000000000000000008152600401610f8e9190613944565b6000670de0b6b3a76400006136b5837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff861661492f565b610b449190614946565b600081815260018301602052604081205461370657508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610eea565b506000610eea565b600081815260018301602052604081205480156137f7576000613732600183614b5a565b855490915060009061374690600190614b5a565b90508082146137ab576000866000018281548110613766576137666149e9565b9060005260206000200154905080876000018481548110613789576137896149e9565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806137bc576137bc614e60565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610eea565b6000915050610eea565b5092915050565b600081815260018301602052604081205480156137f757600061382c600183614b5a565b855490915060009061384090600190614b5a565b90508181146137ab576000866000018281548110613766576137666149e9565b803573ffffffffffffffffffffffffffffffffffffffff8116811461388457600080fd5b919050565b60008060006060848603121561389e57600080fd5b6138a784613860565b9250602084013591506138bc60408501613860565b90509250925092565b6000602082840312156138d757600080fd5b610b4482613860565b6000815180845260005b81811015613906576020818501810151868301820152016138ea565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081526000610b4460208301846138e0565b6020808252825182820181905260009190848201906040850190845b818110156139a557835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101613973565b50909695505050505050565b6000602082840312156139c357600080fd5b813567ffffffffffffffff8111156139da57600080fd5b820160408185031215610b4457600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715613a3e57613a3e6139ec565b60405290565b60405160c0810167ffffffffffffffff81118282101715613a3e57613a3e6139ec565b604051610220810167ffffffffffffffff81118282101715613a3e57613a3e6139ec565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613ad257613ad26139ec565b604052919050565b600067ffffffffffffffff821115613af457613af46139ec565b5060051b60200190565b60ff81168114610e2257600080fd5b60006020808385031215613b2057600080fd5b823567ffffffffffffffff811115613b3757600080fd5b8301601f81018513613b4857600080fd5b8035613b5b613b5682613ada565b613a8b565b81815260609182028301840191848201919088841115613b7a57600080fd5b938501935b83851015613c1a5784890381811215613b985760008081fd5b613ba0613a1b565b613ba987613860565b81526040807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe084011215613bdd5760008081fd5b613be5613a1b565b9250613bf2898901613860565b8352870135613c0081613afe565b828901528088019190915283529384019391850191613b7f565b50979650505050505050565b60008083601f840112613c3857600080fd5b50813567ffffffffffffffff811115613c5057600080fd5b6020830191508360208260051b8501011115611b8357600080fd5b60008060208385031215613c7e57600080fd5b823567ffffffffffffffff811115613c9557600080fd5b613ca185828601613c26565b90969095509350505050565b602080825282518282018190526000919060409081850190868401855b82811015613d1b57613d0b84835180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16825260209081015163ffffffff16910152565b9284019290850190600101613cca565b5091979650505050505050565b803567ffffffffffffffff8116811461388457600080fd5b600060208284031215613d5257600080fd5b610b4482613d28565b81517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16815260208083015163ffffffff169082015260408101610eea565b81511515815261022081016020830151613db6602084018261ffff169052565b506040830151613dce604084018263ffffffff169052565b506060830151613de6606084018263ffffffff169052565b506080830151613dfe608084018263ffffffff169052565b5060a0830151613e1460a084018261ffff169052565b5060c0830151613e2c60c084018263ffffffff169052565b5060e0830151613e4260e084018261ffff169052565b506101008381015161ffff9081169184019190915261012080850151909116908301526101408084015163ffffffff9081169184019190915261016080850151821690840152610180808501518216908401526101a08085015167ffffffffffffffff16908401526101c080850151909116908301526101e080840151151590830152610200808401517fffffffff000000000000000000000000000000000000000000000000000000008116828501525b505092915050565b803563ffffffff8116811461388457600080fd5b803561ffff8116811461388457600080fd5b8015158114610e2257600080fd5b803561388481613f22565b600082601f830112613f4c57600080fd5b81356020613f5c613b5683613ada565b82815260069290921b84018101918181019086841115613f7b57600080fd5b8286015b84811015613fc85760408189031215613f985760008081fd5b613fa0613a1b565b613fa982613d28565b8152613fb6858301613860565b81860152835291830191604001613f7f565b509695505050505050565b60008060408385031215613fe657600080fd5b67ffffffffffffffff83351115613ffc57600080fd5b83601f84358501011261400e57600080fd5b61401e613b568435850135613ada565b8335840180358083526020808401939260059290921b9091010186101561404457600080fd5b602085358601015b85358601803560051b016020018110156142515767ffffffffffffffff8135111561407657600080fd5b8035863587010160407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0828a030112156140af57600080fd5b6140b7613a1b565b6140c360208301613d28565b815267ffffffffffffffff604083013511156140de57600080fd5b88603f6040840135840101126140f357600080fd5b614109613b566020604085013585010135613ada565b6020604084810135850182810135808552928401939260e00201018b101561413057600080fd5b6040808501358501015b6040858101358601602081013560e00201018110156142325760e0818d03121561416357600080fd5b61416b613a1b565b61417482613860565b815260c07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0838f030112156141a857600080fd5b6141b0613a44565b6141bc60208401613efc565b81526141ca60408401613efc565b60208201526141db60608401613f10565b60408201526141ec60808401613efc565b60608201526141fd60a08401613efc565b608082015261420f60c0840135613f22565b60c083013560a0820152602082810191909152908452929092019160e00161413a565b508060208401525050808552505060208301925060208101905061404c565b5092505067ffffffffffffffff6020840135111561426e57600080fd5b61427e8460208501358501613f3b565b90509250929050565b600082601f83011261429857600080fd5b813560206142a8613b5683613ada565b8083825260208201915060208460051b8701019350868411156142ca57600080fd5b602086015b84811015613fc8576142e081613860565b83529183019183016142cf565b6000806040838503121561430057600080fd5b823567ffffffffffffffff8082111561431857600080fd5b61432486838701614287565b9350602085013591508082111561433a57600080fd5b5061434785828601614287565b9150509250929050565b6000806040838503121561436457600080fd5b61436d83613d28565b915061427e60208401613860565b60006020828403121561438d57600080fd5b813567ffffffffffffffff808211156143a557600080fd5b90830190604082860312156143b957600080fd5b6143c1613a1b565b8235828111156143d057600080fd5b6143dc87828601614287565b8252506020830135828111156143f157600080fd5b6143fd87828601614287565b60208301525095945050505050565b6000602080838503121561441f57600080fd5b823567ffffffffffffffff81111561443657600080fd5b8301601f8101851361444757600080fd5b8035614455613b5682613ada565b81815260069190911b8201830190838101908783111561447457600080fd5b928401925b828410156144c657604084890312156144925760008081fd5b61449a613a1b565b6144a385613860565b81526144b0868601613d28565b8187015282526040939093019290840190614479565b979650505050505050565b6000806000806000608086880312156144e957600080fd5b6144f286613d28565b945061450060208701613860565b935060408601359250606086013567ffffffffffffffff8082111561452457600080fd5b818801915088601f83011261453857600080fd5b81358181111561454757600080fd5b89602082850101111561455957600080fd5b9699959850939650602001949392505050565b838152821515602082015260606040820152600061458d60608301846138e0565b95945050505050565b6000806000806000606086880312156145ae57600080fd5b6145b786613d28565b9450602086013567ffffffffffffffff808211156145d457600080fd5b6145e089838a01613c26565b909650945060408801359150808211156145f957600080fd5b818801915088601f83011261460d57600080fd5b81358181111561461c57600080fd5b8960208260061b850101111561455957600080fd5b6000806040838503121561464457600080fd5b61464d83613d28565b9150602083013567ffffffffffffffff81111561466957600080fd5b830160a0818603121561467b57600080fd5b809150509250929050565b80357fffffffff000000000000000000000000000000000000000000000000000000008116811461388457600080fd5b600060208083850312156146c957600080fd5b823567ffffffffffffffff8111156146e057600080fd5b8301601f810185136146f157600080fd5b80356146ff613b5682613ada565b818152610240918202830184019184820191908884111561471f57600080fd5b938501935b83851015613c1a578489038181121561473d5760008081fd5b614745613a1b565b61474e87613d28565b8152610220807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0840112156147835760008081fd5b61478b613a67565b9250614798898901613f30565b835260406147a7818a01613f10565b8a85015260606147b8818b01613efc565b82860152608091506147cb828b01613efc565b9085015260a06147dc8a8201613efc565b8286015260c091506147ef828b01613f10565b9085015260e06148008a8201613efc565b828601526101009150614814828b01613f10565b908501526101206148268a8201613f10565b82860152610140915061483a828b01613f10565b9085015261016061484c8a8201613efc565b828601526101809150614860828b01613efc565b908501526101a06148728a8201613efc565b828601526101c09150614886828b01613d28565b908501526101e06148988a8201613efc565b8286015261020091506148ac828b01613f30565b908501526148bb898301614686565b90840152508088019190915283529384019391850191614724565b600080604083850312156148e957600080fd5b6148f283613860565b915061427e60208401613d28565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082028115828204841417610eea57610eea614900565b60008261497c577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126149b657600080fd5b83018035915067ffffffffffffffff8211156149d157600080fd5b6020019150600681901b3603821315611b8357600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b80357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116811461388457600080fd5b600060408284031215614a5657600080fd5b614a5e613a1b565b614a6783613860565b8152614a7560208401614a18565b60208201529392505050565b600060408284031215614a9357600080fd5b614a9b613a1b565b614a6783613d28565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112614ad857600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112614b1757600080fd5b83018035915067ffffffffffffffff821115614b3257600080fd5b602001915036819003821315611b8357600080fd5b80820180821115610eea57610eea614900565b81810381811115610eea57610eea614900565b7fffffffff000000000000000000000000000000000000000000000000000000008135818116916004851015613ef45760049490940360031b84901b1690921692915050565b60008085851115614bc357600080fd5b83861115614bd057600080fd5b5050820193919092039150565b600060408284031215614bef57600080fd5b614bf7613a1b565b825181526020830151614a7581613f22565b600060208284031215614c1b57600080fd5b5051919050565b805169ffffffffffffffffffff8116811461388457600080fd5b600080600080600060a08688031215614c5457600080fd5b614c5d86614c22565b9450602086015193506040860151925060608601519150614c8060808701614c22565b90509295509295909350565b600060208284031215614c9e57600080fd5b8151610b4481613afe565b60ff8181168382160190811115610eea57610eea614900565b60ff8281168282160390811115610eea57610eea614900565b600181815b80851115614d3457817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115614d1a57614d1a614900565b80851615614d2757918102915b93841c9390800290614ce0565b509250929050565b600082614d4b57506001610eea565b81614d5857506000610eea565b8160018114614d6e5760028114614d7857614d94565b6001915050610eea565b60ff841115614d8957614d89614900565b50506001821b610eea565b5060208310610133831016604e8410600b8410161715614db7575081810a610eea565b614dc18383614cdb565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115614df357614df3614900565b029392505050565b6000610b4460ff841683614d3c565b600060408284031215614e1c57600080fd5b614e24613a1b565b614e2d83613860565b8152602083013560208201528091505092915050565b63ffffffff81811683821601908082111561380157613801614900565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfea164736f6c6343000818000a", +} + +var PriceRegistryABI = PriceRegistryMetaData.ABI + +var PriceRegistryBin = PriceRegistryMetaData.Bin + +func DeployPriceRegistry(auth *bind.TransactOpts, backend bind.ContractBackend, staticConfig PriceRegistryStaticConfig, priceUpdaters []common.Address, feeTokens []common.Address, tokenPriceFeeds []PriceRegistryTokenPriceFeedUpdate, tokenTransferFeeConfigArgs []PriceRegistryTokenTransferFeeConfigArgs, premiumMultiplierWeiPerEthArgs []PriceRegistryPremiumMultiplierWeiPerEthArgs, destChainConfigArgs []PriceRegistryDestChainConfigArgs) (common.Address, *types.Transaction, *PriceRegistry, error) { + parsed, err := PriceRegistryMetaData.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(PriceRegistryBin), backend, staticConfig, priceUpdaters, feeTokens, tokenPriceFeeds, tokenTransferFeeConfigArgs, premiumMultiplierWeiPerEthArgs, destChainConfigArgs) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &PriceRegistry{address: address, abi: *parsed, PriceRegistryCaller: PriceRegistryCaller{contract: contract}, PriceRegistryTransactor: PriceRegistryTransactor{contract: contract}, PriceRegistryFilterer: PriceRegistryFilterer{contract: contract}}, nil +} + +type PriceRegistry struct { + address common.Address + abi abi.ABI + PriceRegistryCaller + PriceRegistryTransactor + PriceRegistryFilterer +} + +type PriceRegistryCaller struct { + contract *bind.BoundContract +} + +type PriceRegistryTransactor struct { + contract *bind.BoundContract +} + +type PriceRegistryFilterer struct { + contract *bind.BoundContract +} + +type PriceRegistrySession struct { + Contract *PriceRegistry + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type PriceRegistryCallerSession struct { + Contract *PriceRegistryCaller + CallOpts bind.CallOpts +} + +type PriceRegistryTransactorSession struct { + Contract *PriceRegistryTransactor + TransactOpts bind.TransactOpts +} + +type PriceRegistryRaw struct { + Contract *PriceRegistry +} + +type PriceRegistryCallerRaw struct { + Contract *PriceRegistryCaller +} + +type PriceRegistryTransactorRaw struct { + Contract *PriceRegistryTransactor +} + +func NewPriceRegistry(address common.Address, backend bind.ContractBackend) (*PriceRegistry, error) { + abi, err := abi.JSON(strings.NewReader(PriceRegistryABI)) + if err != nil { + return nil, err + } + contract, err := bindPriceRegistry(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &PriceRegistry{address: address, abi: abi, PriceRegistryCaller: PriceRegistryCaller{contract: contract}, PriceRegistryTransactor: PriceRegistryTransactor{contract: contract}, PriceRegistryFilterer: PriceRegistryFilterer{contract: contract}}, nil +} + +func NewPriceRegistryCaller(address common.Address, caller bind.ContractCaller) (*PriceRegistryCaller, error) { + contract, err := bindPriceRegistry(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &PriceRegistryCaller{contract: contract}, nil +} + +func NewPriceRegistryTransactor(address common.Address, transactor bind.ContractTransactor) (*PriceRegistryTransactor, error) { + contract, err := bindPriceRegistry(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &PriceRegistryTransactor{contract: contract}, nil +} + +func NewPriceRegistryFilterer(address common.Address, filterer bind.ContractFilterer) (*PriceRegistryFilterer, error) { + contract, err := bindPriceRegistry(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &PriceRegistryFilterer{contract: contract}, nil +} + +func bindPriceRegistry(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := PriceRegistryMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_PriceRegistry *PriceRegistryRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _PriceRegistry.Contract.PriceRegistryCaller.contract.Call(opts, result, method, params...) +} + +func (_PriceRegistry *PriceRegistryRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _PriceRegistry.Contract.PriceRegistryTransactor.contract.Transfer(opts) +} + +func (_PriceRegistry *PriceRegistryRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _PriceRegistry.Contract.PriceRegistryTransactor.contract.Transact(opts, method, params...) +} + +func (_PriceRegistry *PriceRegistryCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _PriceRegistry.Contract.contract.Call(opts, result, method, params...) +} + +func (_PriceRegistry *PriceRegistryTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _PriceRegistry.Contract.contract.Transfer(opts) +} + +func (_PriceRegistry *PriceRegistryTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _PriceRegistry.Contract.contract.Transact(opts, method, params...) +} + +func (_PriceRegistry *PriceRegistryCaller) ConvertTokenAmount(opts *bind.CallOpts, fromToken common.Address, fromTokenAmount *big.Int, toToken common.Address) (*big.Int, error) { + var out []interface{} + err := _PriceRegistry.contract.Call(opts, &out, "convertTokenAmount", fromToken, fromTokenAmount, toToken) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_PriceRegistry *PriceRegistrySession) ConvertTokenAmount(fromToken common.Address, fromTokenAmount *big.Int, toToken common.Address) (*big.Int, error) { + return _PriceRegistry.Contract.ConvertTokenAmount(&_PriceRegistry.CallOpts, fromToken, fromTokenAmount, toToken) +} + +func (_PriceRegistry *PriceRegistryCallerSession) ConvertTokenAmount(fromToken common.Address, fromTokenAmount *big.Int, toToken common.Address) (*big.Int, error) { + return _PriceRegistry.Contract.ConvertTokenAmount(&_PriceRegistry.CallOpts, fromToken, fromTokenAmount, toToken) +} + +func (_PriceRegistry *PriceRegistryCaller) GetAllAuthorizedCallers(opts *bind.CallOpts) ([]common.Address, error) { + var out []interface{} + err := _PriceRegistry.contract.Call(opts, &out, "getAllAuthorizedCallers") + + if err != nil { + return *new([]common.Address), err + } + + out0 := *abi.ConvertType(out[0], new([]common.Address)).(*[]common.Address) + + return out0, err + +} + +func (_PriceRegistry *PriceRegistrySession) GetAllAuthorizedCallers() ([]common.Address, error) { + return _PriceRegistry.Contract.GetAllAuthorizedCallers(&_PriceRegistry.CallOpts) +} + +func (_PriceRegistry *PriceRegistryCallerSession) GetAllAuthorizedCallers() ([]common.Address, error) { + return _PriceRegistry.Contract.GetAllAuthorizedCallers(&_PriceRegistry.CallOpts) +} + +func (_PriceRegistry *PriceRegistryCaller) GetDestChainConfig(opts *bind.CallOpts, destChainSelector uint64) (PriceRegistryDestChainConfig, error) { + var out []interface{} + err := _PriceRegistry.contract.Call(opts, &out, "getDestChainConfig", destChainSelector) + + if err != nil { + return *new(PriceRegistryDestChainConfig), err + } + + out0 := *abi.ConvertType(out[0], new(PriceRegistryDestChainConfig)).(*PriceRegistryDestChainConfig) + + return out0, err + +} + +func (_PriceRegistry *PriceRegistrySession) GetDestChainConfig(destChainSelector uint64) (PriceRegistryDestChainConfig, error) { + return _PriceRegistry.Contract.GetDestChainConfig(&_PriceRegistry.CallOpts, destChainSelector) +} + +func (_PriceRegistry *PriceRegistryCallerSession) GetDestChainConfig(destChainSelector uint64) (PriceRegistryDestChainConfig, error) { + return _PriceRegistry.Contract.GetDestChainConfig(&_PriceRegistry.CallOpts, destChainSelector) +} + +func (_PriceRegistry *PriceRegistryCaller) GetDestinationChainGasPrice(opts *bind.CallOpts, destChainSelector uint64) (InternalTimestampedPackedUint224, error) { + var out []interface{} + err := _PriceRegistry.contract.Call(opts, &out, "getDestinationChainGasPrice", destChainSelector) + + if err != nil { + return *new(InternalTimestampedPackedUint224), err + } + + out0 := *abi.ConvertType(out[0], new(InternalTimestampedPackedUint224)).(*InternalTimestampedPackedUint224) + + return out0, err + +} + +func (_PriceRegistry *PriceRegistrySession) GetDestinationChainGasPrice(destChainSelector uint64) (InternalTimestampedPackedUint224, error) { + return _PriceRegistry.Contract.GetDestinationChainGasPrice(&_PriceRegistry.CallOpts, destChainSelector) +} + +func (_PriceRegistry *PriceRegistryCallerSession) GetDestinationChainGasPrice(destChainSelector uint64) (InternalTimestampedPackedUint224, error) { + return _PriceRegistry.Contract.GetDestinationChainGasPrice(&_PriceRegistry.CallOpts, destChainSelector) +} + +func (_PriceRegistry *PriceRegistryCaller) GetFeeTokens(opts *bind.CallOpts) ([]common.Address, error) { + var out []interface{} + err := _PriceRegistry.contract.Call(opts, &out, "getFeeTokens") + + if err != nil { + return *new([]common.Address), err + } + + out0 := *abi.ConvertType(out[0], new([]common.Address)).(*[]common.Address) + + return out0, err + +} + +func (_PriceRegistry *PriceRegistrySession) GetFeeTokens() ([]common.Address, error) { + return _PriceRegistry.Contract.GetFeeTokens(&_PriceRegistry.CallOpts) +} + +func (_PriceRegistry *PriceRegistryCallerSession) GetFeeTokens() ([]common.Address, error) { + return _PriceRegistry.Contract.GetFeeTokens(&_PriceRegistry.CallOpts) +} + +func (_PriceRegistry *PriceRegistryCaller) GetPremiumMultiplierWeiPerEth(opts *bind.CallOpts, token common.Address) (uint64, error) { + var out []interface{} + err := _PriceRegistry.contract.Call(opts, &out, "getPremiumMultiplierWeiPerEth", token) + + if err != nil { + return *new(uint64), err + } + + out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) + + return out0, err + +} + +func (_PriceRegistry *PriceRegistrySession) GetPremiumMultiplierWeiPerEth(token common.Address) (uint64, error) { + return _PriceRegistry.Contract.GetPremiumMultiplierWeiPerEth(&_PriceRegistry.CallOpts, token) +} + +func (_PriceRegistry *PriceRegistryCallerSession) GetPremiumMultiplierWeiPerEth(token common.Address) (uint64, error) { + return _PriceRegistry.Contract.GetPremiumMultiplierWeiPerEth(&_PriceRegistry.CallOpts, token) +} + +func (_PriceRegistry *PriceRegistryCaller) GetStaticConfig(opts *bind.CallOpts) (PriceRegistryStaticConfig, error) { + var out []interface{} + err := _PriceRegistry.contract.Call(opts, &out, "getStaticConfig") + + if err != nil { + return *new(PriceRegistryStaticConfig), err + } + + out0 := *abi.ConvertType(out[0], new(PriceRegistryStaticConfig)).(*PriceRegistryStaticConfig) + + return out0, err + +} + +func (_PriceRegistry *PriceRegistrySession) GetStaticConfig() (PriceRegistryStaticConfig, error) { + return _PriceRegistry.Contract.GetStaticConfig(&_PriceRegistry.CallOpts) +} + +func (_PriceRegistry *PriceRegistryCallerSession) GetStaticConfig() (PriceRegistryStaticConfig, error) { + return _PriceRegistry.Contract.GetStaticConfig(&_PriceRegistry.CallOpts) +} + +func (_PriceRegistry *PriceRegistryCaller) GetTokenAndGasPrices(opts *bind.CallOpts, token common.Address, destChainSelector uint64) (GetTokenAndGasPrices, + + error) { + var out []interface{} + err := _PriceRegistry.contract.Call(opts, &out, "getTokenAndGasPrices", token, destChainSelector) + + outstruct := new(GetTokenAndGasPrices) + if err != nil { + return *outstruct, err + } + + outstruct.TokenPrice = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + outstruct.GasPriceValue = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int) + + return *outstruct, err + +} + +func (_PriceRegistry *PriceRegistrySession) GetTokenAndGasPrices(token common.Address, destChainSelector uint64) (GetTokenAndGasPrices, + + error) { + return _PriceRegistry.Contract.GetTokenAndGasPrices(&_PriceRegistry.CallOpts, token, destChainSelector) +} + +func (_PriceRegistry *PriceRegistryCallerSession) GetTokenAndGasPrices(token common.Address, destChainSelector uint64) (GetTokenAndGasPrices, + + error) { + return _PriceRegistry.Contract.GetTokenAndGasPrices(&_PriceRegistry.CallOpts, token, destChainSelector) +} + +func (_PriceRegistry *PriceRegistryCaller) GetTokenPrice(opts *bind.CallOpts, token common.Address) (InternalTimestampedPackedUint224, error) { + var out []interface{} + err := _PriceRegistry.contract.Call(opts, &out, "getTokenPrice", token) + + if err != nil { + return *new(InternalTimestampedPackedUint224), err + } + + out0 := *abi.ConvertType(out[0], new(InternalTimestampedPackedUint224)).(*InternalTimestampedPackedUint224) + + return out0, err + +} + +func (_PriceRegistry *PriceRegistrySession) GetTokenPrice(token common.Address) (InternalTimestampedPackedUint224, error) { + return _PriceRegistry.Contract.GetTokenPrice(&_PriceRegistry.CallOpts, token) +} + +func (_PriceRegistry *PriceRegistryCallerSession) GetTokenPrice(token common.Address) (InternalTimestampedPackedUint224, error) { + return _PriceRegistry.Contract.GetTokenPrice(&_PriceRegistry.CallOpts, token) +} + +func (_PriceRegistry *PriceRegistryCaller) GetTokenPriceFeedConfig(opts *bind.CallOpts, token common.Address) (IPriceRegistryTokenPriceFeedConfig, error) { + var out []interface{} + err := _PriceRegistry.contract.Call(opts, &out, "getTokenPriceFeedConfig", token) + + if err != nil { + return *new(IPriceRegistryTokenPriceFeedConfig), err + } + + out0 := *abi.ConvertType(out[0], new(IPriceRegistryTokenPriceFeedConfig)).(*IPriceRegistryTokenPriceFeedConfig) + + return out0, err + +} + +func (_PriceRegistry *PriceRegistrySession) GetTokenPriceFeedConfig(token common.Address) (IPriceRegistryTokenPriceFeedConfig, error) { + return _PriceRegistry.Contract.GetTokenPriceFeedConfig(&_PriceRegistry.CallOpts, token) +} + +func (_PriceRegistry *PriceRegistryCallerSession) GetTokenPriceFeedConfig(token common.Address) (IPriceRegistryTokenPriceFeedConfig, error) { + return _PriceRegistry.Contract.GetTokenPriceFeedConfig(&_PriceRegistry.CallOpts, token) +} + +func (_PriceRegistry *PriceRegistryCaller) GetTokenPrices(opts *bind.CallOpts, tokens []common.Address) ([]InternalTimestampedPackedUint224, error) { + var out []interface{} + err := _PriceRegistry.contract.Call(opts, &out, "getTokenPrices", tokens) + + if err != nil { + return *new([]InternalTimestampedPackedUint224), err + } + + out0 := *abi.ConvertType(out[0], new([]InternalTimestampedPackedUint224)).(*[]InternalTimestampedPackedUint224) + + return out0, err + +} + +func (_PriceRegistry *PriceRegistrySession) GetTokenPrices(tokens []common.Address) ([]InternalTimestampedPackedUint224, error) { + return _PriceRegistry.Contract.GetTokenPrices(&_PriceRegistry.CallOpts, tokens) +} + +func (_PriceRegistry *PriceRegistryCallerSession) GetTokenPrices(tokens []common.Address) ([]InternalTimestampedPackedUint224, error) { + return _PriceRegistry.Contract.GetTokenPrices(&_PriceRegistry.CallOpts, tokens) +} + +func (_PriceRegistry *PriceRegistryCaller) GetTokenTransferFeeConfig(opts *bind.CallOpts, destChainSelector uint64, token common.Address) (PriceRegistryTokenTransferFeeConfig, error) { + var out []interface{} + err := _PriceRegistry.contract.Call(opts, &out, "getTokenTransferFeeConfig", destChainSelector, token) + + if err != nil { + return *new(PriceRegistryTokenTransferFeeConfig), err + } + + out0 := *abi.ConvertType(out[0], new(PriceRegistryTokenTransferFeeConfig)).(*PriceRegistryTokenTransferFeeConfig) + + return out0, err + +} + +func (_PriceRegistry *PriceRegistrySession) GetTokenTransferFeeConfig(destChainSelector uint64, token common.Address) (PriceRegistryTokenTransferFeeConfig, error) { + return _PriceRegistry.Contract.GetTokenTransferFeeConfig(&_PriceRegistry.CallOpts, destChainSelector, token) +} + +func (_PriceRegistry *PriceRegistryCallerSession) GetTokenTransferFeeConfig(destChainSelector uint64, token common.Address) (PriceRegistryTokenTransferFeeConfig, error) { + return _PriceRegistry.Contract.GetTokenTransferFeeConfig(&_PriceRegistry.CallOpts, destChainSelector, token) +} + +func (_PriceRegistry *PriceRegistryCaller) GetValidatedFee(opts *bind.CallOpts, destChainSelector uint64, message ClientEVM2AnyMessage) (*big.Int, error) { + var out []interface{} + err := _PriceRegistry.contract.Call(opts, &out, "getValidatedFee", destChainSelector, message) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_PriceRegistry *PriceRegistrySession) GetValidatedFee(destChainSelector uint64, message ClientEVM2AnyMessage) (*big.Int, error) { + return _PriceRegistry.Contract.GetValidatedFee(&_PriceRegistry.CallOpts, destChainSelector, message) +} + +func (_PriceRegistry *PriceRegistryCallerSession) GetValidatedFee(destChainSelector uint64, message ClientEVM2AnyMessage) (*big.Int, error) { + return _PriceRegistry.Contract.GetValidatedFee(&_PriceRegistry.CallOpts, destChainSelector, message) +} + +func (_PriceRegistry *PriceRegistryCaller) GetValidatedTokenPrice(opts *bind.CallOpts, token common.Address) (*big.Int, error) { + var out []interface{} + err := _PriceRegistry.contract.Call(opts, &out, "getValidatedTokenPrice", token) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_PriceRegistry *PriceRegistrySession) GetValidatedTokenPrice(token common.Address) (*big.Int, error) { + return _PriceRegistry.Contract.GetValidatedTokenPrice(&_PriceRegistry.CallOpts, token) +} + +func (_PriceRegistry *PriceRegistryCallerSession) GetValidatedTokenPrice(token common.Address) (*big.Int, error) { + return _PriceRegistry.Contract.GetValidatedTokenPrice(&_PriceRegistry.CallOpts, token) +} + +func (_PriceRegistry *PriceRegistryCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _PriceRegistry.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_PriceRegistry *PriceRegistrySession) Owner() (common.Address, error) { + return _PriceRegistry.Contract.Owner(&_PriceRegistry.CallOpts) +} + +func (_PriceRegistry *PriceRegistryCallerSession) Owner() (common.Address, error) { + return _PriceRegistry.Contract.Owner(&_PriceRegistry.CallOpts) +} + +func (_PriceRegistry *PriceRegistryCaller) ProcessMessageArgs(opts *bind.CallOpts, destChainSelector uint64, feeToken common.Address, feeTokenAmount *big.Int, extraArgs []byte) (ProcessMessageArgs, + + error) { + var out []interface{} + err := _PriceRegistry.contract.Call(opts, &out, "processMessageArgs", destChainSelector, feeToken, feeTokenAmount, extraArgs) + + outstruct := new(ProcessMessageArgs) + if err != nil { + return *outstruct, err + } + + outstruct.MsgFeeJuels = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + outstruct.IsOutOfOrderExecution = *abi.ConvertType(out[1], new(bool)).(*bool) + outstruct.ConvertedExtraArgs = *abi.ConvertType(out[2], new([]byte)).(*[]byte) + + return *outstruct, err + +} + +func (_PriceRegistry *PriceRegistrySession) ProcessMessageArgs(destChainSelector uint64, feeToken common.Address, feeTokenAmount *big.Int, extraArgs []byte) (ProcessMessageArgs, + + error) { + return _PriceRegistry.Contract.ProcessMessageArgs(&_PriceRegistry.CallOpts, destChainSelector, feeToken, feeTokenAmount, extraArgs) +} + +func (_PriceRegistry *PriceRegistryCallerSession) ProcessMessageArgs(destChainSelector uint64, feeToken common.Address, feeTokenAmount *big.Int, extraArgs []byte) (ProcessMessageArgs, + + error) { + return _PriceRegistry.Contract.ProcessMessageArgs(&_PriceRegistry.CallOpts, destChainSelector, feeToken, feeTokenAmount, extraArgs) +} + +func (_PriceRegistry *PriceRegistryCaller) TypeAndVersion(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _PriceRegistry.contract.Call(opts, &out, "typeAndVersion") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +func (_PriceRegistry *PriceRegistrySession) TypeAndVersion() (string, error) { + return _PriceRegistry.Contract.TypeAndVersion(&_PriceRegistry.CallOpts) +} + +func (_PriceRegistry *PriceRegistryCallerSession) TypeAndVersion() (string, error) { + return _PriceRegistry.Contract.TypeAndVersion(&_PriceRegistry.CallOpts) +} + +func (_PriceRegistry *PriceRegistryCaller) ValidatePoolReturnData(opts *bind.CallOpts, destChainSelector uint64, rampTokenAmounts []InternalRampTokenAmount, sourceTokenAmounts []ClientEVMTokenAmount) error { + var out []interface{} + err := _PriceRegistry.contract.Call(opts, &out, "validatePoolReturnData", destChainSelector, rampTokenAmounts, sourceTokenAmounts) + + if err != nil { + return err + } + + return err + +} + +func (_PriceRegistry *PriceRegistrySession) ValidatePoolReturnData(destChainSelector uint64, rampTokenAmounts []InternalRampTokenAmount, sourceTokenAmounts []ClientEVMTokenAmount) error { + return _PriceRegistry.Contract.ValidatePoolReturnData(&_PriceRegistry.CallOpts, destChainSelector, rampTokenAmounts, sourceTokenAmounts) +} + +func (_PriceRegistry *PriceRegistryCallerSession) ValidatePoolReturnData(destChainSelector uint64, rampTokenAmounts []InternalRampTokenAmount, sourceTokenAmounts []ClientEVMTokenAmount) error { + return _PriceRegistry.Contract.ValidatePoolReturnData(&_PriceRegistry.CallOpts, destChainSelector, rampTokenAmounts, sourceTokenAmounts) +} + +func (_PriceRegistry *PriceRegistryTransactor) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _PriceRegistry.contract.Transact(opts, "acceptOwnership") +} + +func (_PriceRegistry *PriceRegistrySession) AcceptOwnership() (*types.Transaction, error) { + return _PriceRegistry.Contract.AcceptOwnership(&_PriceRegistry.TransactOpts) +} + +func (_PriceRegistry *PriceRegistryTransactorSession) AcceptOwnership() (*types.Transaction, error) { + return _PriceRegistry.Contract.AcceptOwnership(&_PriceRegistry.TransactOpts) +} + +func (_PriceRegistry *PriceRegistryTransactor) ApplyAuthorizedCallerUpdates(opts *bind.TransactOpts, authorizedCallerArgs AuthorizedCallersAuthorizedCallerArgs) (*types.Transaction, error) { + return _PriceRegistry.contract.Transact(opts, "applyAuthorizedCallerUpdates", authorizedCallerArgs) +} + +func (_PriceRegistry *PriceRegistrySession) ApplyAuthorizedCallerUpdates(authorizedCallerArgs AuthorizedCallersAuthorizedCallerArgs) (*types.Transaction, error) { + return _PriceRegistry.Contract.ApplyAuthorizedCallerUpdates(&_PriceRegistry.TransactOpts, authorizedCallerArgs) +} + +func (_PriceRegistry *PriceRegistryTransactorSession) ApplyAuthorizedCallerUpdates(authorizedCallerArgs AuthorizedCallersAuthorizedCallerArgs) (*types.Transaction, error) { + return _PriceRegistry.Contract.ApplyAuthorizedCallerUpdates(&_PriceRegistry.TransactOpts, authorizedCallerArgs) +} + +func (_PriceRegistry *PriceRegistryTransactor) ApplyDestChainConfigUpdates(opts *bind.TransactOpts, destChainConfigArgs []PriceRegistryDestChainConfigArgs) (*types.Transaction, error) { + return _PriceRegistry.contract.Transact(opts, "applyDestChainConfigUpdates", destChainConfigArgs) +} + +func (_PriceRegistry *PriceRegistrySession) ApplyDestChainConfigUpdates(destChainConfigArgs []PriceRegistryDestChainConfigArgs) (*types.Transaction, error) { + return _PriceRegistry.Contract.ApplyDestChainConfigUpdates(&_PriceRegistry.TransactOpts, destChainConfigArgs) +} + +func (_PriceRegistry *PriceRegistryTransactorSession) ApplyDestChainConfigUpdates(destChainConfigArgs []PriceRegistryDestChainConfigArgs) (*types.Transaction, error) { + return _PriceRegistry.Contract.ApplyDestChainConfigUpdates(&_PriceRegistry.TransactOpts, destChainConfigArgs) +} + +func (_PriceRegistry *PriceRegistryTransactor) ApplyFeeTokensUpdates(opts *bind.TransactOpts, feeTokensToAdd []common.Address, feeTokensToRemove []common.Address) (*types.Transaction, error) { + return _PriceRegistry.contract.Transact(opts, "applyFeeTokensUpdates", feeTokensToAdd, feeTokensToRemove) +} + +func (_PriceRegistry *PriceRegistrySession) ApplyFeeTokensUpdates(feeTokensToAdd []common.Address, feeTokensToRemove []common.Address) (*types.Transaction, error) { + return _PriceRegistry.Contract.ApplyFeeTokensUpdates(&_PriceRegistry.TransactOpts, feeTokensToAdd, feeTokensToRemove) +} + +func (_PriceRegistry *PriceRegistryTransactorSession) ApplyFeeTokensUpdates(feeTokensToAdd []common.Address, feeTokensToRemove []common.Address) (*types.Transaction, error) { + return _PriceRegistry.Contract.ApplyFeeTokensUpdates(&_PriceRegistry.TransactOpts, feeTokensToAdd, feeTokensToRemove) +} + +func (_PriceRegistry *PriceRegistryTransactor) ApplyPremiumMultiplierWeiPerEthUpdates(opts *bind.TransactOpts, premiumMultiplierWeiPerEthArgs []PriceRegistryPremiumMultiplierWeiPerEthArgs) (*types.Transaction, error) { + return _PriceRegistry.contract.Transact(opts, "applyPremiumMultiplierWeiPerEthUpdates", premiumMultiplierWeiPerEthArgs) +} + +func (_PriceRegistry *PriceRegistrySession) ApplyPremiumMultiplierWeiPerEthUpdates(premiumMultiplierWeiPerEthArgs []PriceRegistryPremiumMultiplierWeiPerEthArgs) (*types.Transaction, error) { + return _PriceRegistry.Contract.ApplyPremiumMultiplierWeiPerEthUpdates(&_PriceRegistry.TransactOpts, premiumMultiplierWeiPerEthArgs) +} + +func (_PriceRegistry *PriceRegistryTransactorSession) ApplyPremiumMultiplierWeiPerEthUpdates(premiumMultiplierWeiPerEthArgs []PriceRegistryPremiumMultiplierWeiPerEthArgs) (*types.Transaction, error) { + return _PriceRegistry.Contract.ApplyPremiumMultiplierWeiPerEthUpdates(&_PriceRegistry.TransactOpts, premiumMultiplierWeiPerEthArgs) +} + +func (_PriceRegistry *PriceRegistryTransactor) ApplyTokenTransferFeeConfigUpdates(opts *bind.TransactOpts, tokenTransferFeeConfigArgs []PriceRegistryTokenTransferFeeConfigArgs, tokensToUseDefaultFeeConfigs []PriceRegistryTokenTransferFeeConfigRemoveArgs) (*types.Transaction, error) { + return _PriceRegistry.contract.Transact(opts, "applyTokenTransferFeeConfigUpdates", tokenTransferFeeConfigArgs, tokensToUseDefaultFeeConfigs) +} + +func (_PriceRegistry *PriceRegistrySession) ApplyTokenTransferFeeConfigUpdates(tokenTransferFeeConfigArgs []PriceRegistryTokenTransferFeeConfigArgs, tokensToUseDefaultFeeConfigs []PriceRegistryTokenTransferFeeConfigRemoveArgs) (*types.Transaction, error) { + return _PriceRegistry.Contract.ApplyTokenTransferFeeConfigUpdates(&_PriceRegistry.TransactOpts, tokenTransferFeeConfigArgs, tokensToUseDefaultFeeConfigs) +} + +func (_PriceRegistry *PriceRegistryTransactorSession) ApplyTokenTransferFeeConfigUpdates(tokenTransferFeeConfigArgs []PriceRegistryTokenTransferFeeConfigArgs, tokensToUseDefaultFeeConfigs []PriceRegistryTokenTransferFeeConfigRemoveArgs) (*types.Transaction, error) { + return _PriceRegistry.Contract.ApplyTokenTransferFeeConfigUpdates(&_PriceRegistry.TransactOpts, tokenTransferFeeConfigArgs, tokensToUseDefaultFeeConfigs) +} + +func (_PriceRegistry *PriceRegistryTransactor) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { + return _PriceRegistry.contract.Transact(opts, "transferOwnership", to) +} + +func (_PriceRegistry *PriceRegistrySession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _PriceRegistry.Contract.TransferOwnership(&_PriceRegistry.TransactOpts, to) +} + +func (_PriceRegistry *PriceRegistryTransactorSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _PriceRegistry.Contract.TransferOwnership(&_PriceRegistry.TransactOpts, to) +} + +func (_PriceRegistry *PriceRegistryTransactor) UpdatePrices(opts *bind.TransactOpts, priceUpdates InternalPriceUpdates) (*types.Transaction, error) { + return _PriceRegistry.contract.Transact(opts, "updatePrices", priceUpdates) +} + +func (_PriceRegistry *PriceRegistrySession) UpdatePrices(priceUpdates InternalPriceUpdates) (*types.Transaction, error) { + return _PriceRegistry.Contract.UpdatePrices(&_PriceRegistry.TransactOpts, priceUpdates) +} + +func (_PriceRegistry *PriceRegistryTransactorSession) UpdatePrices(priceUpdates InternalPriceUpdates) (*types.Transaction, error) { + return _PriceRegistry.Contract.UpdatePrices(&_PriceRegistry.TransactOpts, priceUpdates) +} + +func (_PriceRegistry *PriceRegistryTransactor) UpdateTokenPriceFeeds(opts *bind.TransactOpts, tokenPriceFeedUpdates []PriceRegistryTokenPriceFeedUpdate) (*types.Transaction, error) { + return _PriceRegistry.contract.Transact(opts, "updateTokenPriceFeeds", tokenPriceFeedUpdates) +} + +func (_PriceRegistry *PriceRegistrySession) UpdateTokenPriceFeeds(tokenPriceFeedUpdates []PriceRegistryTokenPriceFeedUpdate) (*types.Transaction, error) { + return _PriceRegistry.Contract.UpdateTokenPriceFeeds(&_PriceRegistry.TransactOpts, tokenPriceFeedUpdates) +} + +func (_PriceRegistry *PriceRegistryTransactorSession) UpdateTokenPriceFeeds(tokenPriceFeedUpdates []PriceRegistryTokenPriceFeedUpdate) (*types.Transaction, error) { + return _PriceRegistry.Contract.UpdateTokenPriceFeeds(&_PriceRegistry.TransactOpts, tokenPriceFeedUpdates) +} + +type PriceRegistryAuthorizedCallerAddedIterator struct { + Event *PriceRegistryAuthorizedCallerAdded + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *PriceRegistryAuthorizedCallerAddedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(PriceRegistryAuthorizedCallerAdded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(PriceRegistryAuthorizedCallerAdded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *PriceRegistryAuthorizedCallerAddedIterator) Error() error { + return it.fail +} + +func (it *PriceRegistryAuthorizedCallerAddedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type PriceRegistryAuthorizedCallerAdded struct { + Caller common.Address + Raw types.Log +} + +func (_PriceRegistry *PriceRegistryFilterer) FilterAuthorizedCallerAdded(opts *bind.FilterOpts) (*PriceRegistryAuthorizedCallerAddedIterator, error) { + + logs, sub, err := _PriceRegistry.contract.FilterLogs(opts, "AuthorizedCallerAdded") + if err != nil { + return nil, err + } + return &PriceRegistryAuthorizedCallerAddedIterator{contract: _PriceRegistry.contract, event: "AuthorizedCallerAdded", logs: logs, sub: sub}, nil +} + +func (_PriceRegistry *PriceRegistryFilterer) WatchAuthorizedCallerAdded(opts *bind.WatchOpts, sink chan<- *PriceRegistryAuthorizedCallerAdded) (event.Subscription, error) { + + logs, sub, err := _PriceRegistry.contract.WatchLogs(opts, "AuthorizedCallerAdded") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(PriceRegistryAuthorizedCallerAdded) + if err := _PriceRegistry.contract.UnpackLog(event, "AuthorizedCallerAdded", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_PriceRegistry *PriceRegistryFilterer) ParseAuthorizedCallerAdded(log types.Log) (*PriceRegistryAuthorizedCallerAdded, error) { + event := new(PriceRegistryAuthorizedCallerAdded) + if err := _PriceRegistry.contract.UnpackLog(event, "AuthorizedCallerAdded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type PriceRegistryAuthorizedCallerRemovedIterator struct { + Event *PriceRegistryAuthorizedCallerRemoved + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *PriceRegistryAuthorizedCallerRemovedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(PriceRegistryAuthorizedCallerRemoved) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(PriceRegistryAuthorizedCallerRemoved) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *PriceRegistryAuthorizedCallerRemovedIterator) Error() error { + return it.fail +} + +func (it *PriceRegistryAuthorizedCallerRemovedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type PriceRegistryAuthorizedCallerRemoved struct { + Caller common.Address + Raw types.Log +} + +func (_PriceRegistry *PriceRegistryFilterer) FilterAuthorizedCallerRemoved(opts *bind.FilterOpts) (*PriceRegistryAuthorizedCallerRemovedIterator, error) { + + logs, sub, err := _PriceRegistry.contract.FilterLogs(opts, "AuthorizedCallerRemoved") + if err != nil { + return nil, err + } + return &PriceRegistryAuthorizedCallerRemovedIterator{contract: _PriceRegistry.contract, event: "AuthorizedCallerRemoved", logs: logs, sub: sub}, nil +} + +func (_PriceRegistry *PriceRegistryFilterer) WatchAuthorizedCallerRemoved(opts *bind.WatchOpts, sink chan<- *PriceRegistryAuthorizedCallerRemoved) (event.Subscription, error) { + + logs, sub, err := _PriceRegistry.contract.WatchLogs(opts, "AuthorizedCallerRemoved") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(PriceRegistryAuthorizedCallerRemoved) + if err := _PriceRegistry.contract.UnpackLog(event, "AuthorizedCallerRemoved", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_PriceRegistry *PriceRegistryFilterer) ParseAuthorizedCallerRemoved(log types.Log) (*PriceRegistryAuthorizedCallerRemoved, error) { + event := new(PriceRegistryAuthorizedCallerRemoved) + if err := _PriceRegistry.contract.UnpackLog(event, "AuthorizedCallerRemoved", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type PriceRegistryDestChainAddedIterator struct { + Event *PriceRegistryDestChainAdded + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *PriceRegistryDestChainAddedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(PriceRegistryDestChainAdded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(PriceRegistryDestChainAdded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *PriceRegistryDestChainAddedIterator) Error() error { + return it.fail +} + +func (it *PriceRegistryDestChainAddedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type PriceRegistryDestChainAdded struct { + DestChainSelector uint64 + DestChainConfig PriceRegistryDestChainConfig + Raw types.Log +} + +func (_PriceRegistry *PriceRegistryFilterer) FilterDestChainAdded(opts *bind.FilterOpts, destChainSelector []uint64) (*PriceRegistryDestChainAddedIterator, error) { + + var destChainSelectorRule []interface{} + for _, destChainSelectorItem := range destChainSelector { + destChainSelectorRule = append(destChainSelectorRule, destChainSelectorItem) + } + + logs, sub, err := _PriceRegistry.contract.FilterLogs(opts, "DestChainAdded", destChainSelectorRule) + if err != nil { + return nil, err + } + return &PriceRegistryDestChainAddedIterator{contract: _PriceRegistry.contract, event: "DestChainAdded", logs: logs, sub: sub}, nil +} + +func (_PriceRegistry *PriceRegistryFilterer) WatchDestChainAdded(opts *bind.WatchOpts, sink chan<- *PriceRegistryDestChainAdded, destChainSelector []uint64) (event.Subscription, error) { + + var destChainSelectorRule []interface{} + for _, destChainSelectorItem := range destChainSelector { + destChainSelectorRule = append(destChainSelectorRule, destChainSelectorItem) + } + + logs, sub, err := _PriceRegistry.contract.WatchLogs(opts, "DestChainAdded", destChainSelectorRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(PriceRegistryDestChainAdded) + if err := _PriceRegistry.contract.UnpackLog(event, "DestChainAdded", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_PriceRegistry *PriceRegistryFilterer) ParseDestChainAdded(log types.Log) (*PriceRegistryDestChainAdded, error) { + event := new(PriceRegistryDestChainAdded) + if err := _PriceRegistry.contract.UnpackLog(event, "DestChainAdded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type PriceRegistryDestChainConfigUpdatedIterator struct { + Event *PriceRegistryDestChainConfigUpdated + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *PriceRegistryDestChainConfigUpdatedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(PriceRegistryDestChainConfigUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(PriceRegistryDestChainConfigUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *PriceRegistryDestChainConfigUpdatedIterator) Error() error { + return it.fail +} + +func (it *PriceRegistryDestChainConfigUpdatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type PriceRegistryDestChainConfigUpdated struct { + DestChainSelector uint64 + DestChainConfig PriceRegistryDestChainConfig + Raw types.Log +} + +func (_PriceRegistry *PriceRegistryFilterer) FilterDestChainConfigUpdated(opts *bind.FilterOpts, destChainSelector []uint64) (*PriceRegistryDestChainConfigUpdatedIterator, error) { + + var destChainSelectorRule []interface{} + for _, destChainSelectorItem := range destChainSelector { + destChainSelectorRule = append(destChainSelectorRule, destChainSelectorItem) + } + + logs, sub, err := _PriceRegistry.contract.FilterLogs(opts, "DestChainConfigUpdated", destChainSelectorRule) + if err != nil { + return nil, err + } + return &PriceRegistryDestChainConfigUpdatedIterator{contract: _PriceRegistry.contract, event: "DestChainConfigUpdated", logs: logs, sub: sub}, nil +} + +func (_PriceRegistry *PriceRegistryFilterer) WatchDestChainConfigUpdated(opts *bind.WatchOpts, sink chan<- *PriceRegistryDestChainConfigUpdated, destChainSelector []uint64) (event.Subscription, error) { + + var destChainSelectorRule []interface{} + for _, destChainSelectorItem := range destChainSelector { + destChainSelectorRule = append(destChainSelectorRule, destChainSelectorItem) + } + + logs, sub, err := _PriceRegistry.contract.WatchLogs(opts, "DestChainConfigUpdated", destChainSelectorRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(PriceRegistryDestChainConfigUpdated) + if err := _PriceRegistry.contract.UnpackLog(event, "DestChainConfigUpdated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_PriceRegistry *PriceRegistryFilterer) ParseDestChainConfigUpdated(log types.Log) (*PriceRegistryDestChainConfigUpdated, error) { + event := new(PriceRegistryDestChainConfigUpdated) + if err := _PriceRegistry.contract.UnpackLog(event, "DestChainConfigUpdated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type PriceRegistryFeeTokenAddedIterator struct { + Event *PriceRegistryFeeTokenAdded + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *PriceRegistryFeeTokenAddedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(PriceRegistryFeeTokenAdded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(PriceRegistryFeeTokenAdded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *PriceRegistryFeeTokenAddedIterator) Error() error { + return it.fail +} + +func (it *PriceRegistryFeeTokenAddedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type PriceRegistryFeeTokenAdded struct { + FeeToken common.Address + Raw types.Log +} + +func (_PriceRegistry *PriceRegistryFilterer) FilterFeeTokenAdded(opts *bind.FilterOpts, feeToken []common.Address) (*PriceRegistryFeeTokenAddedIterator, error) { + + var feeTokenRule []interface{} + for _, feeTokenItem := range feeToken { + feeTokenRule = append(feeTokenRule, feeTokenItem) + } + + logs, sub, err := _PriceRegistry.contract.FilterLogs(opts, "FeeTokenAdded", feeTokenRule) + if err != nil { + return nil, err + } + return &PriceRegistryFeeTokenAddedIterator{contract: _PriceRegistry.contract, event: "FeeTokenAdded", logs: logs, sub: sub}, nil +} + +func (_PriceRegistry *PriceRegistryFilterer) WatchFeeTokenAdded(opts *bind.WatchOpts, sink chan<- *PriceRegistryFeeTokenAdded, feeToken []common.Address) (event.Subscription, error) { + + var feeTokenRule []interface{} + for _, feeTokenItem := range feeToken { + feeTokenRule = append(feeTokenRule, feeTokenItem) + } + + logs, sub, err := _PriceRegistry.contract.WatchLogs(opts, "FeeTokenAdded", feeTokenRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(PriceRegistryFeeTokenAdded) + if err := _PriceRegistry.contract.UnpackLog(event, "FeeTokenAdded", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_PriceRegistry *PriceRegistryFilterer) ParseFeeTokenAdded(log types.Log) (*PriceRegistryFeeTokenAdded, error) { + event := new(PriceRegistryFeeTokenAdded) + if err := _PriceRegistry.contract.UnpackLog(event, "FeeTokenAdded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type PriceRegistryFeeTokenRemovedIterator struct { + Event *PriceRegistryFeeTokenRemoved + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *PriceRegistryFeeTokenRemovedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(PriceRegistryFeeTokenRemoved) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(PriceRegistryFeeTokenRemoved) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *PriceRegistryFeeTokenRemovedIterator) Error() error { + return it.fail +} + +func (it *PriceRegistryFeeTokenRemovedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type PriceRegistryFeeTokenRemoved struct { + FeeToken common.Address + Raw types.Log +} + +func (_PriceRegistry *PriceRegistryFilterer) FilterFeeTokenRemoved(opts *bind.FilterOpts, feeToken []common.Address) (*PriceRegistryFeeTokenRemovedIterator, error) { + + var feeTokenRule []interface{} + for _, feeTokenItem := range feeToken { + feeTokenRule = append(feeTokenRule, feeTokenItem) + } + + logs, sub, err := _PriceRegistry.contract.FilterLogs(opts, "FeeTokenRemoved", feeTokenRule) + if err != nil { + return nil, err + } + return &PriceRegistryFeeTokenRemovedIterator{contract: _PriceRegistry.contract, event: "FeeTokenRemoved", logs: logs, sub: sub}, nil +} + +func (_PriceRegistry *PriceRegistryFilterer) WatchFeeTokenRemoved(opts *bind.WatchOpts, sink chan<- *PriceRegistryFeeTokenRemoved, feeToken []common.Address) (event.Subscription, error) { + + var feeTokenRule []interface{} + for _, feeTokenItem := range feeToken { + feeTokenRule = append(feeTokenRule, feeTokenItem) + } + + logs, sub, err := _PriceRegistry.contract.WatchLogs(opts, "FeeTokenRemoved", feeTokenRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(PriceRegistryFeeTokenRemoved) + if err := _PriceRegistry.contract.UnpackLog(event, "FeeTokenRemoved", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_PriceRegistry *PriceRegistryFilterer) ParseFeeTokenRemoved(log types.Log) (*PriceRegistryFeeTokenRemoved, error) { + event := new(PriceRegistryFeeTokenRemoved) + if err := _PriceRegistry.contract.UnpackLog(event, "FeeTokenRemoved", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type PriceRegistryOwnershipTransferRequestedIterator struct { + Event *PriceRegistryOwnershipTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *PriceRegistryOwnershipTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(PriceRegistryOwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(PriceRegistryOwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *PriceRegistryOwnershipTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *PriceRegistryOwnershipTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type PriceRegistryOwnershipTransferRequested struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_PriceRegistry *PriceRegistryFilterer) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*PriceRegistryOwnershipTransferRequestedIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _PriceRegistry.contract.FilterLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return &PriceRegistryOwnershipTransferRequestedIterator{contract: _PriceRegistry.contract, event: "OwnershipTransferRequested", logs: logs, sub: sub}, nil +} + +func (_PriceRegistry *PriceRegistryFilterer) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *PriceRegistryOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _PriceRegistry.contract.WatchLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(PriceRegistryOwnershipTransferRequested) + if err := _PriceRegistry.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_PriceRegistry *PriceRegistryFilterer) ParseOwnershipTransferRequested(log types.Log) (*PriceRegistryOwnershipTransferRequested, error) { + event := new(PriceRegistryOwnershipTransferRequested) + if err := _PriceRegistry.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type PriceRegistryOwnershipTransferredIterator struct { + Event *PriceRegistryOwnershipTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *PriceRegistryOwnershipTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(PriceRegistryOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(PriceRegistryOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *PriceRegistryOwnershipTransferredIterator) Error() error { + return it.fail +} + +func (it *PriceRegistryOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type PriceRegistryOwnershipTransferred struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_PriceRegistry *PriceRegistryFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*PriceRegistryOwnershipTransferredIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _PriceRegistry.contract.FilterLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return &PriceRegistryOwnershipTransferredIterator{contract: _PriceRegistry.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +func (_PriceRegistry *PriceRegistryFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *PriceRegistryOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _PriceRegistry.contract.WatchLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(PriceRegistryOwnershipTransferred) + if err := _PriceRegistry.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_PriceRegistry *PriceRegistryFilterer) ParseOwnershipTransferred(log types.Log) (*PriceRegistryOwnershipTransferred, error) { + event := new(PriceRegistryOwnershipTransferred) + if err := _PriceRegistry.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type PriceRegistryPremiumMultiplierWeiPerEthUpdatedIterator struct { + Event *PriceRegistryPremiumMultiplierWeiPerEthUpdated + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *PriceRegistryPremiumMultiplierWeiPerEthUpdatedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(PriceRegistryPremiumMultiplierWeiPerEthUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(PriceRegistryPremiumMultiplierWeiPerEthUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *PriceRegistryPremiumMultiplierWeiPerEthUpdatedIterator) Error() error { + return it.fail +} + +func (it *PriceRegistryPremiumMultiplierWeiPerEthUpdatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type PriceRegistryPremiumMultiplierWeiPerEthUpdated struct { + Token common.Address + PremiumMultiplierWeiPerEth uint64 + Raw types.Log +} + +func (_PriceRegistry *PriceRegistryFilterer) FilterPremiumMultiplierWeiPerEthUpdated(opts *bind.FilterOpts, token []common.Address) (*PriceRegistryPremiumMultiplierWeiPerEthUpdatedIterator, error) { + + var tokenRule []interface{} + for _, tokenItem := range token { + tokenRule = append(tokenRule, tokenItem) + } + + logs, sub, err := _PriceRegistry.contract.FilterLogs(opts, "PremiumMultiplierWeiPerEthUpdated", tokenRule) + if err != nil { + return nil, err + } + return &PriceRegistryPremiumMultiplierWeiPerEthUpdatedIterator{contract: _PriceRegistry.contract, event: "PremiumMultiplierWeiPerEthUpdated", logs: logs, sub: sub}, nil +} + +func (_PriceRegistry *PriceRegistryFilterer) WatchPremiumMultiplierWeiPerEthUpdated(opts *bind.WatchOpts, sink chan<- *PriceRegistryPremiumMultiplierWeiPerEthUpdated, token []common.Address) (event.Subscription, error) { + + var tokenRule []interface{} + for _, tokenItem := range token { + tokenRule = append(tokenRule, tokenItem) + } + + logs, sub, err := _PriceRegistry.contract.WatchLogs(opts, "PremiumMultiplierWeiPerEthUpdated", tokenRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(PriceRegistryPremiumMultiplierWeiPerEthUpdated) + if err := _PriceRegistry.contract.UnpackLog(event, "PremiumMultiplierWeiPerEthUpdated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_PriceRegistry *PriceRegistryFilterer) ParsePremiumMultiplierWeiPerEthUpdated(log types.Log) (*PriceRegistryPremiumMultiplierWeiPerEthUpdated, error) { + event := new(PriceRegistryPremiumMultiplierWeiPerEthUpdated) + if err := _PriceRegistry.contract.UnpackLog(event, "PremiumMultiplierWeiPerEthUpdated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type PriceRegistryPriceFeedPerTokenUpdatedIterator struct { + Event *PriceRegistryPriceFeedPerTokenUpdated + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *PriceRegistryPriceFeedPerTokenUpdatedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(PriceRegistryPriceFeedPerTokenUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(PriceRegistryPriceFeedPerTokenUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *PriceRegistryPriceFeedPerTokenUpdatedIterator) Error() error { + return it.fail +} + +func (it *PriceRegistryPriceFeedPerTokenUpdatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type PriceRegistryPriceFeedPerTokenUpdated struct { + Token common.Address + PriceFeedConfig IPriceRegistryTokenPriceFeedConfig + Raw types.Log +} + +func (_PriceRegistry *PriceRegistryFilterer) FilterPriceFeedPerTokenUpdated(opts *bind.FilterOpts, token []common.Address) (*PriceRegistryPriceFeedPerTokenUpdatedIterator, error) { + + var tokenRule []interface{} + for _, tokenItem := range token { + tokenRule = append(tokenRule, tokenItem) + } + + logs, sub, err := _PriceRegistry.contract.FilterLogs(opts, "PriceFeedPerTokenUpdated", tokenRule) + if err != nil { + return nil, err + } + return &PriceRegistryPriceFeedPerTokenUpdatedIterator{contract: _PriceRegistry.contract, event: "PriceFeedPerTokenUpdated", logs: logs, sub: sub}, nil +} + +func (_PriceRegistry *PriceRegistryFilterer) WatchPriceFeedPerTokenUpdated(opts *bind.WatchOpts, sink chan<- *PriceRegistryPriceFeedPerTokenUpdated, token []common.Address) (event.Subscription, error) { + + var tokenRule []interface{} + for _, tokenItem := range token { + tokenRule = append(tokenRule, tokenItem) + } + + logs, sub, err := _PriceRegistry.contract.WatchLogs(opts, "PriceFeedPerTokenUpdated", tokenRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(PriceRegistryPriceFeedPerTokenUpdated) + if err := _PriceRegistry.contract.UnpackLog(event, "PriceFeedPerTokenUpdated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_PriceRegistry *PriceRegistryFilterer) ParsePriceFeedPerTokenUpdated(log types.Log) (*PriceRegistryPriceFeedPerTokenUpdated, error) { + event := new(PriceRegistryPriceFeedPerTokenUpdated) + if err := _PriceRegistry.contract.UnpackLog(event, "PriceFeedPerTokenUpdated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type PriceRegistryTokenTransferFeeConfigDeletedIterator struct { + Event *PriceRegistryTokenTransferFeeConfigDeleted + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *PriceRegistryTokenTransferFeeConfigDeletedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(PriceRegistryTokenTransferFeeConfigDeleted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(PriceRegistryTokenTransferFeeConfigDeleted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *PriceRegistryTokenTransferFeeConfigDeletedIterator) Error() error { + return it.fail +} + +func (it *PriceRegistryTokenTransferFeeConfigDeletedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type PriceRegistryTokenTransferFeeConfigDeleted struct { + DestChainSelector uint64 + Token common.Address + Raw types.Log +} + +func (_PriceRegistry *PriceRegistryFilterer) FilterTokenTransferFeeConfigDeleted(opts *bind.FilterOpts, destChainSelector []uint64, token []common.Address) (*PriceRegistryTokenTransferFeeConfigDeletedIterator, error) { + + var destChainSelectorRule []interface{} + for _, destChainSelectorItem := range destChainSelector { + destChainSelectorRule = append(destChainSelectorRule, destChainSelectorItem) + } + var tokenRule []interface{} + for _, tokenItem := range token { + tokenRule = append(tokenRule, tokenItem) + } + + logs, sub, err := _PriceRegistry.contract.FilterLogs(opts, "TokenTransferFeeConfigDeleted", destChainSelectorRule, tokenRule) + if err != nil { + return nil, err + } + return &PriceRegistryTokenTransferFeeConfigDeletedIterator{contract: _PriceRegistry.contract, event: "TokenTransferFeeConfigDeleted", logs: logs, sub: sub}, nil +} + +func (_PriceRegistry *PriceRegistryFilterer) WatchTokenTransferFeeConfigDeleted(opts *bind.WatchOpts, sink chan<- *PriceRegistryTokenTransferFeeConfigDeleted, destChainSelector []uint64, token []common.Address) (event.Subscription, error) { + + var destChainSelectorRule []interface{} + for _, destChainSelectorItem := range destChainSelector { + destChainSelectorRule = append(destChainSelectorRule, destChainSelectorItem) + } + var tokenRule []interface{} + for _, tokenItem := range token { + tokenRule = append(tokenRule, tokenItem) + } + + logs, sub, err := _PriceRegistry.contract.WatchLogs(opts, "TokenTransferFeeConfigDeleted", destChainSelectorRule, tokenRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(PriceRegistryTokenTransferFeeConfigDeleted) + if err := _PriceRegistry.contract.UnpackLog(event, "TokenTransferFeeConfigDeleted", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_PriceRegistry *PriceRegistryFilterer) ParseTokenTransferFeeConfigDeleted(log types.Log) (*PriceRegistryTokenTransferFeeConfigDeleted, error) { + event := new(PriceRegistryTokenTransferFeeConfigDeleted) + if err := _PriceRegistry.contract.UnpackLog(event, "TokenTransferFeeConfigDeleted", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type PriceRegistryTokenTransferFeeConfigUpdatedIterator struct { + Event *PriceRegistryTokenTransferFeeConfigUpdated + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *PriceRegistryTokenTransferFeeConfigUpdatedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(PriceRegistryTokenTransferFeeConfigUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(PriceRegistryTokenTransferFeeConfigUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *PriceRegistryTokenTransferFeeConfigUpdatedIterator) Error() error { + return it.fail +} + +func (it *PriceRegistryTokenTransferFeeConfigUpdatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type PriceRegistryTokenTransferFeeConfigUpdated struct { + DestChainSelector uint64 + Token common.Address + TokenTransferFeeConfig PriceRegistryTokenTransferFeeConfig + Raw types.Log +} + +func (_PriceRegistry *PriceRegistryFilterer) FilterTokenTransferFeeConfigUpdated(opts *bind.FilterOpts, destChainSelector []uint64, token []common.Address) (*PriceRegistryTokenTransferFeeConfigUpdatedIterator, error) { + + var destChainSelectorRule []interface{} + for _, destChainSelectorItem := range destChainSelector { + destChainSelectorRule = append(destChainSelectorRule, destChainSelectorItem) + } + var tokenRule []interface{} + for _, tokenItem := range token { + tokenRule = append(tokenRule, tokenItem) + } + + logs, sub, err := _PriceRegistry.contract.FilterLogs(opts, "TokenTransferFeeConfigUpdated", destChainSelectorRule, tokenRule) + if err != nil { + return nil, err + } + return &PriceRegistryTokenTransferFeeConfigUpdatedIterator{contract: _PriceRegistry.contract, event: "TokenTransferFeeConfigUpdated", logs: logs, sub: sub}, nil +} + +func (_PriceRegistry *PriceRegistryFilterer) WatchTokenTransferFeeConfigUpdated(opts *bind.WatchOpts, sink chan<- *PriceRegistryTokenTransferFeeConfigUpdated, destChainSelector []uint64, token []common.Address) (event.Subscription, error) { + + var destChainSelectorRule []interface{} + for _, destChainSelectorItem := range destChainSelector { + destChainSelectorRule = append(destChainSelectorRule, destChainSelectorItem) + } + var tokenRule []interface{} + for _, tokenItem := range token { + tokenRule = append(tokenRule, tokenItem) + } + + logs, sub, err := _PriceRegistry.contract.WatchLogs(opts, "TokenTransferFeeConfigUpdated", destChainSelectorRule, tokenRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(PriceRegistryTokenTransferFeeConfigUpdated) + if err := _PriceRegistry.contract.UnpackLog(event, "TokenTransferFeeConfigUpdated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_PriceRegistry *PriceRegistryFilterer) ParseTokenTransferFeeConfigUpdated(log types.Log) (*PriceRegistryTokenTransferFeeConfigUpdated, error) { + event := new(PriceRegistryTokenTransferFeeConfigUpdated) + if err := _PriceRegistry.contract.UnpackLog(event, "TokenTransferFeeConfigUpdated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type PriceRegistryUsdPerTokenUpdatedIterator struct { + Event *PriceRegistryUsdPerTokenUpdated + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *PriceRegistryUsdPerTokenUpdatedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(PriceRegistryUsdPerTokenUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(PriceRegistryUsdPerTokenUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *PriceRegistryUsdPerTokenUpdatedIterator) Error() error { + return it.fail +} + +func (it *PriceRegistryUsdPerTokenUpdatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type PriceRegistryUsdPerTokenUpdated struct { + Token common.Address + Value *big.Int + Timestamp *big.Int + Raw types.Log +} + +func (_PriceRegistry *PriceRegistryFilterer) FilterUsdPerTokenUpdated(opts *bind.FilterOpts, token []common.Address) (*PriceRegistryUsdPerTokenUpdatedIterator, error) { + + var tokenRule []interface{} + for _, tokenItem := range token { + tokenRule = append(tokenRule, tokenItem) + } + + logs, sub, err := _PriceRegistry.contract.FilterLogs(opts, "UsdPerTokenUpdated", tokenRule) + if err != nil { + return nil, err + } + return &PriceRegistryUsdPerTokenUpdatedIterator{contract: _PriceRegistry.contract, event: "UsdPerTokenUpdated", logs: logs, sub: sub}, nil +} + +func (_PriceRegistry *PriceRegistryFilterer) WatchUsdPerTokenUpdated(opts *bind.WatchOpts, sink chan<- *PriceRegistryUsdPerTokenUpdated, token []common.Address) (event.Subscription, error) { + + var tokenRule []interface{} + for _, tokenItem := range token { + tokenRule = append(tokenRule, tokenItem) + } + + logs, sub, err := _PriceRegistry.contract.WatchLogs(opts, "UsdPerTokenUpdated", tokenRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(PriceRegistryUsdPerTokenUpdated) + if err := _PriceRegistry.contract.UnpackLog(event, "UsdPerTokenUpdated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_PriceRegistry *PriceRegistryFilterer) ParseUsdPerTokenUpdated(log types.Log) (*PriceRegistryUsdPerTokenUpdated, error) { + event := new(PriceRegistryUsdPerTokenUpdated) + if err := _PriceRegistry.contract.UnpackLog(event, "UsdPerTokenUpdated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type PriceRegistryUsdPerUnitGasUpdatedIterator struct { + Event *PriceRegistryUsdPerUnitGasUpdated + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *PriceRegistryUsdPerUnitGasUpdatedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(PriceRegistryUsdPerUnitGasUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(PriceRegistryUsdPerUnitGasUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *PriceRegistryUsdPerUnitGasUpdatedIterator) Error() error { + return it.fail +} + +func (it *PriceRegistryUsdPerUnitGasUpdatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type PriceRegistryUsdPerUnitGasUpdated struct { + DestChain uint64 + Value *big.Int + Timestamp *big.Int + Raw types.Log +} + +func (_PriceRegistry *PriceRegistryFilterer) FilterUsdPerUnitGasUpdated(opts *bind.FilterOpts, destChain []uint64) (*PriceRegistryUsdPerUnitGasUpdatedIterator, error) { + + var destChainRule []interface{} + for _, destChainItem := range destChain { + destChainRule = append(destChainRule, destChainItem) + } + + logs, sub, err := _PriceRegistry.contract.FilterLogs(opts, "UsdPerUnitGasUpdated", destChainRule) + if err != nil { + return nil, err + } + return &PriceRegistryUsdPerUnitGasUpdatedIterator{contract: _PriceRegistry.contract, event: "UsdPerUnitGasUpdated", logs: logs, sub: sub}, nil +} + +func (_PriceRegistry *PriceRegistryFilterer) WatchUsdPerUnitGasUpdated(opts *bind.WatchOpts, sink chan<- *PriceRegistryUsdPerUnitGasUpdated, destChain []uint64) (event.Subscription, error) { + + var destChainRule []interface{} + for _, destChainItem := range destChain { + destChainRule = append(destChainRule, destChainItem) + } + + logs, sub, err := _PriceRegistry.contract.WatchLogs(opts, "UsdPerUnitGasUpdated", destChainRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(PriceRegistryUsdPerUnitGasUpdated) + if err := _PriceRegistry.contract.UnpackLog(event, "UsdPerUnitGasUpdated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_PriceRegistry *PriceRegistryFilterer) ParseUsdPerUnitGasUpdated(log types.Log) (*PriceRegistryUsdPerUnitGasUpdated, error) { + event := new(PriceRegistryUsdPerUnitGasUpdated) + if err := _PriceRegistry.contract.UnpackLog(event, "UsdPerUnitGasUpdated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type GetTokenAndGasPrices struct { + TokenPrice *big.Int + GasPriceValue *big.Int +} +type ProcessMessageArgs struct { + MsgFeeJuels *big.Int + IsOutOfOrderExecution bool + ConvertedExtraArgs []byte +} + +func (_PriceRegistry *PriceRegistry) ParseLog(log types.Log) (generated.AbigenLog, error) { + switch log.Topics[0] { + case _PriceRegistry.abi.Events["AuthorizedCallerAdded"].ID: + return _PriceRegistry.ParseAuthorizedCallerAdded(log) + case _PriceRegistry.abi.Events["AuthorizedCallerRemoved"].ID: + return _PriceRegistry.ParseAuthorizedCallerRemoved(log) + case _PriceRegistry.abi.Events["DestChainAdded"].ID: + return _PriceRegistry.ParseDestChainAdded(log) + case _PriceRegistry.abi.Events["DestChainConfigUpdated"].ID: + return _PriceRegistry.ParseDestChainConfigUpdated(log) + case _PriceRegistry.abi.Events["FeeTokenAdded"].ID: + return _PriceRegistry.ParseFeeTokenAdded(log) + case _PriceRegistry.abi.Events["FeeTokenRemoved"].ID: + return _PriceRegistry.ParseFeeTokenRemoved(log) + case _PriceRegistry.abi.Events["OwnershipTransferRequested"].ID: + return _PriceRegistry.ParseOwnershipTransferRequested(log) + case _PriceRegistry.abi.Events["OwnershipTransferred"].ID: + return _PriceRegistry.ParseOwnershipTransferred(log) + case _PriceRegistry.abi.Events["PremiumMultiplierWeiPerEthUpdated"].ID: + return _PriceRegistry.ParsePremiumMultiplierWeiPerEthUpdated(log) + case _PriceRegistry.abi.Events["PriceFeedPerTokenUpdated"].ID: + return _PriceRegistry.ParsePriceFeedPerTokenUpdated(log) + case _PriceRegistry.abi.Events["TokenTransferFeeConfigDeleted"].ID: + return _PriceRegistry.ParseTokenTransferFeeConfigDeleted(log) + case _PriceRegistry.abi.Events["TokenTransferFeeConfigUpdated"].ID: + return _PriceRegistry.ParseTokenTransferFeeConfigUpdated(log) + case _PriceRegistry.abi.Events["UsdPerTokenUpdated"].ID: + return _PriceRegistry.ParseUsdPerTokenUpdated(log) + case _PriceRegistry.abi.Events["UsdPerUnitGasUpdated"].ID: + return _PriceRegistry.ParseUsdPerUnitGasUpdated(log) + + default: + return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) + } +} + +func (PriceRegistryAuthorizedCallerAdded) Topic() common.Hash { + return common.HexToHash("0xeb1b9b92e50b7f88f9ff25d56765095ac6e91540eee214906f4036a908ffbdef") +} + +func (PriceRegistryAuthorizedCallerRemoved) Topic() common.Hash { + return common.HexToHash("0xc3803387881faad271c47728894e3e36fac830ffc8602ca6fc07733cbda77580") +} + +func (PriceRegistryDestChainAdded) Topic() common.Hash { + return common.HexToHash("0xa937382a486d993de71c220bc8b559242deb4e286a353fa732330b4aa7d13577") +} + +func (PriceRegistryDestChainConfigUpdated) Topic() common.Hash { + return common.HexToHash("0xa7b607fc10d28a1caf39ab7d27f4c94945db708a576d572781a455c5894fad93") +} + +func (PriceRegistryFeeTokenAdded) Topic() common.Hash { + return common.HexToHash("0xdf1b1bd32a69711488d71554706bb130b1fc63a5fa1a2cd85e8440f84065ba23") +} + +func (PriceRegistryFeeTokenRemoved) Topic() common.Hash { + return common.HexToHash("0x1795838dc8ab2ffc5f431a1729a6afa0b587f982f7b2be0b9d7187a1ef547f91") +} + +func (PriceRegistryOwnershipTransferRequested) Topic() common.Hash { + return common.HexToHash("0xed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278") +} + +func (PriceRegistryOwnershipTransferred) Topic() common.Hash { + return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") +} + +func (PriceRegistryPremiumMultiplierWeiPerEthUpdated) Topic() common.Hash { + return common.HexToHash("0xbb77da6f7210cdd16904228a9360133d1d7dfff99b1bc75f128da5b53e28f97d") +} + +func (PriceRegistryPriceFeedPerTokenUpdated) Topic() common.Hash { + return common.HexToHash("0x08a5f7f5bb38a81d8e43aca13ecd76431dbf8816ae4699affff7b00b2fc1c464") +} + +func (PriceRegistryTokenTransferFeeConfigDeleted) Topic() common.Hash { + return common.HexToHash("0x4de5b1bcbca6018c11303a2c3f4a4b4f22a1c741d8c4ba430d246ac06c5ddf8b") +} + +func (PriceRegistryTokenTransferFeeConfigUpdated) Topic() common.Hash { + return common.HexToHash("0x94967ae9ea7729ad4f54021c1981765d2b1d954f7c92fbec340aa0a54f46b8b5") +} + +func (PriceRegistryUsdPerTokenUpdated) Topic() common.Hash { + return common.HexToHash("0x52f50aa6d1a95a4595361ecf953d095f125d442e4673716dede699e049de148a") +} + +func (PriceRegistryUsdPerUnitGasUpdated) Topic() common.Hash { + return common.HexToHash("0xdd84a3fa9ef9409f550d54d6affec7e9c480c878c6ab27b78912a03e1b371c6e") +} + +func (_PriceRegistry *PriceRegistry) Address() common.Address { + return _PriceRegistry.address +} + +type PriceRegistryInterface interface { + ConvertTokenAmount(opts *bind.CallOpts, fromToken common.Address, fromTokenAmount *big.Int, toToken common.Address) (*big.Int, error) + + GetAllAuthorizedCallers(opts *bind.CallOpts) ([]common.Address, error) + + GetDestChainConfig(opts *bind.CallOpts, destChainSelector uint64) (PriceRegistryDestChainConfig, error) + + GetDestinationChainGasPrice(opts *bind.CallOpts, destChainSelector uint64) (InternalTimestampedPackedUint224, error) + + GetFeeTokens(opts *bind.CallOpts) ([]common.Address, error) + + GetPremiumMultiplierWeiPerEth(opts *bind.CallOpts, token common.Address) (uint64, error) + + GetStaticConfig(opts *bind.CallOpts) (PriceRegistryStaticConfig, error) + + GetTokenAndGasPrices(opts *bind.CallOpts, token common.Address, destChainSelector uint64) (GetTokenAndGasPrices, + + error) + + GetTokenPrice(opts *bind.CallOpts, token common.Address) (InternalTimestampedPackedUint224, error) + + GetTokenPriceFeedConfig(opts *bind.CallOpts, token common.Address) (IPriceRegistryTokenPriceFeedConfig, error) + + GetTokenPrices(opts *bind.CallOpts, tokens []common.Address) ([]InternalTimestampedPackedUint224, error) + + GetTokenTransferFeeConfig(opts *bind.CallOpts, destChainSelector uint64, token common.Address) (PriceRegistryTokenTransferFeeConfig, error) + + GetValidatedFee(opts *bind.CallOpts, destChainSelector uint64, message ClientEVM2AnyMessage) (*big.Int, error) + + GetValidatedTokenPrice(opts *bind.CallOpts, token common.Address) (*big.Int, error) + + Owner(opts *bind.CallOpts) (common.Address, error) + + ProcessMessageArgs(opts *bind.CallOpts, destChainSelector uint64, feeToken common.Address, feeTokenAmount *big.Int, extraArgs []byte) (ProcessMessageArgs, + + error) + + TypeAndVersion(opts *bind.CallOpts) (string, error) + + ValidatePoolReturnData(opts *bind.CallOpts, destChainSelector uint64, rampTokenAmounts []InternalRampTokenAmount, sourceTokenAmounts []ClientEVMTokenAmount) error + + AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) + + ApplyAuthorizedCallerUpdates(opts *bind.TransactOpts, authorizedCallerArgs AuthorizedCallersAuthorizedCallerArgs) (*types.Transaction, error) + + ApplyDestChainConfigUpdates(opts *bind.TransactOpts, destChainConfigArgs []PriceRegistryDestChainConfigArgs) (*types.Transaction, error) + + ApplyFeeTokensUpdates(opts *bind.TransactOpts, feeTokensToAdd []common.Address, feeTokensToRemove []common.Address) (*types.Transaction, error) + + ApplyPremiumMultiplierWeiPerEthUpdates(opts *bind.TransactOpts, premiumMultiplierWeiPerEthArgs []PriceRegistryPremiumMultiplierWeiPerEthArgs) (*types.Transaction, error) + + ApplyTokenTransferFeeConfigUpdates(opts *bind.TransactOpts, tokenTransferFeeConfigArgs []PriceRegistryTokenTransferFeeConfigArgs, tokensToUseDefaultFeeConfigs []PriceRegistryTokenTransferFeeConfigRemoveArgs) (*types.Transaction, error) + + TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + + UpdatePrices(opts *bind.TransactOpts, priceUpdates InternalPriceUpdates) (*types.Transaction, error) + + UpdateTokenPriceFeeds(opts *bind.TransactOpts, tokenPriceFeedUpdates []PriceRegistryTokenPriceFeedUpdate) (*types.Transaction, error) + + FilterAuthorizedCallerAdded(opts *bind.FilterOpts) (*PriceRegistryAuthorizedCallerAddedIterator, error) + + WatchAuthorizedCallerAdded(opts *bind.WatchOpts, sink chan<- *PriceRegistryAuthorizedCallerAdded) (event.Subscription, error) + + ParseAuthorizedCallerAdded(log types.Log) (*PriceRegistryAuthorizedCallerAdded, error) + + FilterAuthorizedCallerRemoved(opts *bind.FilterOpts) (*PriceRegistryAuthorizedCallerRemovedIterator, error) + + WatchAuthorizedCallerRemoved(opts *bind.WatchOpts, sink chan<- *PriceRegistryAuthorizedCallerRemoved) (event.Subscription, error) + + ParseAuthorizedCallerRemoved(log types.Log) (*PriceRegistryAuthorizedCallerRemoved, error) + + FilterDestChainAdded(opts *bind.FilterOpts, destChainSelector []uint64) (*PriceRegistryDestChainAddedIterator, error) + + WatchDestChainAdded(opts *bind.WatchOpts, sink chan<- *PriceRegistryDestChainAdded, destChainSelector []uint64) (event.Subscription, error) + + ParseDestChainAdded(log types.Log) (*PriceRegistryDestChainAdded, error) + + FilterDestChainConfigUpdated(opts *bind.FilterOpts, destChainSelector []uint64) (*PriceRegistryDestChainConfigUpdatedIterator, error) + + WatchDestChainConfigUpdated(opts *bind.WatchOpts, sink chan<- *PriceRegistryDestChainConfigUpdated, destChainSelector []uint64) (event.Subscription, error) + + ParseDestChainConfigUpdated(log types.Log) (*PriceRegistryDestChainConfigUpdated, error) + + FilterFeeTokenAdded(opts *bind.FilterOpts, feeToken []common.Address) (*PriceRegistryFeeTokenAddedIterator, error) + + WatchFeeTokenAdded(opts *bind.WatchOpts, sink chan<- *PriceRegistryFeeTokenAdded, feeToken []common.Address) (event.Subscription, error) + + ParseFeeTokenAdded(log types.Log) (*PriceRegistryFeeTokenAdded, error) + + FilterFeeTokenRemoved(opts *bind.FilterOpts, feeToken []common.Address) (*PriceRegistryFeeTokenRemovedIterator, error) + + WatchFeeTokenRemoved(opts *bind.WatchOpts, sink chan<- *PriceRegistryFeeTokenRemoved, feeToken []common.Address) (event.Subscription, error) + + ParseFeeTokenRemoved(log types.Log) (*PriceRegistryFeeTokenRemoved, error) + + FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*PriceRegistryOwnershipTransferRequestedIterator, error) + + WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *PriceRegistryOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferRequested(log types.Log) (*PriceRegistryOwnershipTransferRequested, error) + + FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*PriceRegistryOwnershipTransferredIterator, error) + + WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *PriceRegistryOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferred(log types.Log) (*PriceRegistryOwnershipTransferred, error) + + FilterPremiumMultiplierWeiPerEthUpdated(opts *bind.FilterOpts, token []common.Address) (*PriceRegistryPremiumMultiplierWeiPerEthUpdatedIterator, error) + + WatchPremiumMultiplierWeiPerEthUpdated(opts *bind.WatchOpts, sink chan<- *PriceRegistryPremiumMultiplierWeiPerEthUpdated, token []common.Address) (event.Subscription, error) + + ParsePremiumMultiplierWeiPerEthUpdated(log types.Log) (*PriceRegistryPremiumMultiplierWeiPerEthUpdated, error) + + FilterPriceFeedPerTokenUpdated(opts *bind.FilterOpts, token []common.Address) (*PriceRegistryPriceFeedPerTokenUpdatedIterator, error) + + WatchPriceFeedPerTokenUpdated(opts *bind.WatchOpts, sink chan<- *PriceRegistryPriceFeedPerTokenUpdated, token []common.Address) (event.Subscription, error) + + ParsePriceFeedPerTokenUpdated(log types.Log) (*PriceRegistryPriceFeedPerTokenUpdated, error) + + FilterTokenTransferFeeConfigDeleted(opts *bind.FilterOpts, destChainSelector []uint64, token []common.Address) (*PriceRegistryTokenTransferFeeConfigDeletedIterator, error) + + WatchTokenTransferFeeConfigDeleted(opts *bind.WatchOpts, sink chan<- *PriceRegistryTokenTransferFeeConfigDeleted, destChainSelector []uint64, token []common.Address) (event.Subscription, error) + + ParseTokenTransferFeeConfigDeleted(log types.Log) (*PriceRegistryTokenTransferFeeConfigDeleted, error) + + FilterTokenTransferFeeConfigUpdated(opts *bind.FilterOpts, destChainSelector []uint64, token []common.Address) (*PriceRegistryTokenTransferFeeConfigUpdatedIterator, error) + + WatchTokenTransferFeeConfigUpdated(opts *bind.WatchOpts, sink chan<- *PriceRegistryTokenTransferFeeConfigUpdated, destChainSelector []uint64, token []common.Address) (event.Subscription, error) + + ParseTokenTransferFeeConfigUpdated(log types.Log) (*PriceRegistryTokenTransferFeeConfigUpdated, error) + + FilterUsdPerTokenUpdated(opts *bind.FilterOpts, token []common.Address) (*PriceRegistryUsdPerTokenUpdatedIterator, error) + + WatchUsdPerTokenUpdated(opts *bind.WatchOpts, sink chan<- *PriceRegistryUsdPerTokenUpdated, token []common.Address) (event.Subscription, error) + + ParseUsdPerTokenUpdated(log types.Log) (*PriceRegistryUsdPerTokenUpdated, error) + + FilterUsdPerUnitGasUpdated(opts *bind.FilterOpts, destChain []uint64) (*PriceRegistryUsdPerUnitGasUpdatedIterator, error) + + WatchUsdPerUnitGasUpdated(opts *bind.WatchOpts, sink chan<- *PriceRegistryUsdPerUnitGasUpdated, destChain []uint64) (event.Subscription, error) + + ParseUsdPerUnitGasUpdated(log types.Log) (*PriceRegistryUsdPerUnitGasUpdated, error) + + ParseLog(log types.Log) (generated.AbigenLog, error) + + Address() common.Address +} diff --git a/core/gethwrappers/ccip/generated/price_registry_1_2_0/price_registry.go b/core/gethwrappers/ccip/generated/price_registry_1_2_0/price_registry.go index 64e16bd1dc..30b1b26da9 100644 --- a/core/gethwrappers/ccip/generated/price_registry_1_2_0/price_registry.go +++ b/core/gethwrappers/ccip/generated/price_registry_1_2_0/price_registry.go @@ -1690,4 +1690,4 @@ type PriceRegistryInterface interface { ParseLog(log types.Log) (generated.AbigenLog, error) Address() common.Address -} \ No newline at end of file +} diff --git a/core/gethwrappers/ccip/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/ccip/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 8b9f869a71..e6b6907c28 100644 --- a/core/gethwrappers/ccip/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/ccip/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,4 +1,6 @@ GETH_VERSION: 1.13.8 +arm_contract: ../../../contracts/solc/v0.8.24/RMN/RMN.abi ../../../contracts/solc/v0.8.24/RMN/RMN.bin 8b45b0fb08631c6b582fd3c0b4052a79cc2b4e091e6286af1ab131bef63661f9 +arm_proxy_contract: ../../../contracts/solc/v0.8.24/ARMProxy/ARMProxy.abi ../../../contracts/solc/v0.8.24/ARMProxy/ARMProxy.bin b048d8e752e3c41113ebb305c1efa06737ad36b4907b93e627fb0a3113023454 burn_from_mint_token_pool: ../../../contracts/solc/v0.8.24/BurnFromMintTokenPool/BurnFromMintTokenPool.abi ../../../contracts/solc/v0.8.24/BurnFromMintTokenPool/BurnFromMintTokenPool.bin 1e60c28ad796a220a38043b369dec8d9bffe23e1c7d9895760e30672872afd06 burn_mint_token_pool: ../../../contracts/solc/v0.8.24/BurnMintTokenPool/BurnMintTokenPool.abi ../../../contracts/solc/v0.8.24/BurnMintTokenPool/BurnMintTokenPool.bin 3e8e3358f0bb520af069a7d37ea625940a88461a54418b1d5925eabced8c74df burn_mint_token_pool_and_proxy: ../../../contracts/solc/v0.8.24/BurnMintTokenPoolAndProxy/BurnMintTokenPoolAndProxy.abi ../../../contracts/solc/v0.8.24/BurnMintTokenPoolAndProxy/BurnMintTokenPoolAndProxy.bin 717c079d5d13300cf3c3ee871c6e5bf9af904411f204fb081a9f3b263cca1391 @@ -10,6 +12,8 @@ ccip_reader_tester: ../../../contracts/solc/v0.8.24/CCIPReaderTester/CCIPReaderT commit_store: ../../../contracts/solc/v0.8.24/CommitStore/CommitStore.abi ../../../contracts/solc/v0.8.24/CommitStore/CommitStore.bin 274d87db70b643e00ab0a7e7845bb4b791f3b613bfc87708d33fc5a8369e2a41 commit_store_helper: ../../../contracts/solc/v0.8.24/CommitStoreHelper/CommitStoreHelper.abi ../../../contracts/solc/v0.8.24/CommitStoreHelper/CommitStoreHelper.bin f7128dcc2ee6dbcbc976288abcc16970ffb19b59412c5202ef6b259d2007f801 ether_sender_receiver: ../../../contracts/solc/v0.8.24/EtherSenderReceiver/EtherSenderReceiver.abi ../../../contracts/solc/v0.8.24/EtherSenderReceiver/EtherSenderReceiver.bin 09510a3f773f108a3c231e8d202835c845ded862d071ec54c4f89c12d868b8de +evm_2_evm_multi_offramp: ../../../contracts/solc/v0.8.24/EVM2EVMMultiOffRamp/EVM2EVMMultiOffRamp.abi ../../../contracts/solc/v0.8.24/EVM2EVMMultiOffRamp/EVM2EVMMultiOffRamp.bin b913487363418c368ca412c4cc4a3b1e411395b3c6b8b982b1c48cdab5c7b10c +evm_2_evm_multi_onramp: ../../../contracts/solc/v0.8.24/EVM2EVMMultiOnRamp/EVM2EVMMultiOnRamp.abi ../../../contracts/solc/v0.8.24/EVM2EVMMultiOnRamp/EVM2EVMMultiOnRamp.bin 15cd5695049ab4be1f396ec1d7b609738b2bcefa3740a7a48316e1f72506a34a evm_2_evm_offramp: ../../../contracts/solc/v0.8.24/EVM2EVMOffRamp/EVM2EVMOffRamp.abi ../../../contracts/solc/v0.8.24/EVM2EVMOffRamp/EVM2EVMOffRamp.bin b0d77babbe635cd6ba04c2af049badc9e9d28a4b6ed6bb75f830ad902a618beb evm_2_evm_onramp: ../../../contracts/solc/v0.8.24/EVM2EVMOnRamp/EVM2EVMOnRamp.abi ../../../contracts/solc/v0.8.24/EVM2EVMOnRamp/EVM2EVMOnRamp.bin 5c02c2b167946b3467636ff2bb58594cb4652fc63d8bdfee2488ed562e2a3e50 fee_quoter: ../../../contracts/solc/v0.8.24/FeeQuoter/FeeQuoter.abi ../../../contracts/solc/v0.8.24/FeeQuoter/FeeQuoter.bin 6806f01f305df73a923361f128b8962e9a8d3e7338a9a5b5fbe0636e6c9fc35f @@ -17,6 +21,7 @@ lock_release_token_pool: ../../../contracts/solc/v0.8.24/LockReleaseTokenPool/Lo lock_release_token_pool_and_proxy: ../../../contracts/solc/v0.8.24/LockReleaseTokenPoolAndProxy/LockReleaseTokenPoolAndProxy.abi ../../../contracts/solc/v0.8.24/LockReleaseTokenPoolAndProxy/LockReleaseTokenPoolAndProxy.bin e632b08be0fbd1d013e8b3a9d75293d0d532b83071c531ff2be1deec1fa48ec1 maybe_revert_message_receiver: ../../../contracts/solc/v0.8.24/MaybeRevertMessageReceiver/MaybeRevertMessageReceiver.abi ../../../contracts/solc/v0.8.24/MaybeRevertMessageReceiver/MaybeRevertMessageReceiver.bin d73956c26232ebcc4a5444429fa99cbefed960e323be9b5a24925885c2e477d5 message_hasher: ../../../contracts/solc/v0.8.24/MessageHasher/MessageHasher.abi ../../../contracts/solc/v0.8.24/MessageHasher/MessageHasher.bin 0a2661da24147160383ad61d56a258515d1cc07f5e0f471ec5cbb4bccaf82389 +mock_arm_contract: ../../../contracts/solc/v0.8.24/MockRMN1_0/MockRMN.abi ../../../contracts/solc/v0.8.24/MockRMN1_0/MockRMN.bin e7a3a6c3eda5fb882e16bcc2b4340f78523acb67907bcdcaf3c8ffc51488688e mock_usdc_token_messenger: ../../../contracts/solc/v0.8.24/MockE2EUSDCTokenMessenger/MockE2EUSDCTokenMessenger.abi ../../../contracts/solc/v0.8.24/MockE2EUSDCTokenMessenger/MockE2EUSDCTokenMessenger.bin d976651d36b33ac2196b32b9d2f4fa6690c6a18d41b621365659fce1c1d1e737 mock_usdc_token_transmitter: ../../../contracts/solc/v0.8.24/MockE2EUSDCTransmitter/MockE2EUSDCTransmitter.abi ../../../contracts/solc/v0.8.24/MockE2EUSDCTransmitter/MockE2EUSDCTransmitter.bin be0dbc3e475741ea0b7a54ec2b935a321b428baa9f4ce18180a87fb38bb87de2 mock_v3_aggregator_contract: ../../../contracts/solc/v0.8.24/MockV3Aggregator/MockV3Aggregator.abi ../../../contracts/solc/v0.8.24/MockV3Aggregator/MockV3Aggregator.bin 518e19efa2ff52b0fefd8e597b05765317ee7638189bfe34ca43de2f6599faf4 @@ -26,7 +31,7 @@ nonce_manager: ../../../contracts/solc/v0.8.24/NonceManager/NonceManager.abi ../ offramp: ../../../contracts/solc/v0.8.24/OffRamp/OffRamp.abi ../../../contracts/solc/v0.8.24/OffRamp/OffRamp.bin 941b18b4e2aef977195fefba514e17c9475667e6f8513b18cabfb07ff90136ce onramp: ../../../contracts/solc/v0.8.24/OnRamp/OnRamp.abi ../../../contracts/solc/v0.8.24/OnRamp/OnRamp.bin 4f29f05261bdaf9b99e675c1169c0d5cba841b6dd7ca3a8997cc0d3cae7ba850 ping_pong_demo: ../../../contracts/solc/v0.8.24/PingPongDemo/PingPongDemo.abi ../../../contracts/solc/v0.8.24/PingPongDemo/PingPongDemo.bin c1c2f8a65c7ffd971899cae7fe62f2da57d09e936151e2b92163c4bebe699d6b -price_registry: ../../../contracts/solc/v0.8.24/PriceRegistry/PriceRegistry.abi ../../../contracts/solc/v0.8.24/PriceRegistry/PriceRegistry.bin e7781d600c1bb7aa4620106af7f6e146a109b97f4cb6a7d06c9e15773340ecb2 +price_registry: ../../../contracts/solc/v0.8.24/PriceRegistry/PriceRegistry.abi ../../../contracts/solc/v0.8.24/PriceRegistry/PriceRegistry.bin 8f4bdaa4d8239429ae4e047ab06d445bad42234a05bb7c99ba6141bd811e1722 registry_module_owner_custom: ../../../contracts/solc/v0.8.24/RegistryModuleOwnerCustom/RegistryModuleOwnerCustom.abi ../../../contracts/solc/v0.8.24/RegistryModuleOwnerCustom/RegistryModuleOwnerCustom.bin 75be86323c227917a9bbc3f799d7ed02f92db546653a36db30ed0ebe64461353 report_codec: ../../../contracts/solc/v0.8.24/ReportCodec/ReportCodec.abi ../../../contracts/solc/v0.8.24/ReportCodec/ReportCodec.bin 7413576693f76bac7c01fbaec4a161d37a1de51b13dcab78455d2c484ed938b1 rmn_contract: ../../../contracts/solc/v0.8.24/RMN/RMN.abi ../../../contracts/solc/v0.8.24/RMN/RMN.bin 8b45b0fb08631c6b582fd3c0b4052a79cc2b4e091e6286af1ab131bef63661f9 diff --git a/core/gethwrappers/ccip/mocks/price_registry_interface.go b/core/gethwrappers/ccip/mocks/price_registry_interface.go new file mode 100644 index 0000000000..b8b90b1293 --- /dev/null +++ b/core/gethwrappers/ccip/mocks/price_registry_interface.go @@ -0,0 +1,4201 @@ +// Code generated by mockery v2.43.2. DO NOT EDIT. + +package mock_contracts + +import ( + big "math/big" + + bind "github.com/ethereum/go-ethereum/accounts/abi/bind" + common "github.com/ethereum/go-ethereum/common" + + event "github.com/ethereum/go-ethereum/event" + + generated "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" + + mock "github.com/stretchr/testify/mock" + + price_registry "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/price_registry" + + types "github.com/ethereum/go-ethereum/core/types" +) + +// PriceRegistryInterface is an autogenerated mock type for the PriceRegistryInterface type +type PriceRegistryInterface struct { + mock.Mock +} + +type PriceRegistryInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *PriceRegistryInterface) EXPECT() *PriceRegistryInterface_Expecter { + return &PriceRegistryInterface_Expecter{mock: &_m.Mock} +} + +// AcceptOwnership provides a mock function with given fields: opts +func (_m *PriceRegistryInterface) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + ret := _m.Called(opts) + + if len(ret) == 0 { + panic("no return value specified for AcceptOwnership") + } + + var r0 *types.Transaction + var r1 error + if rf, ok := ret.Get(0).(func(*bind.TransactOpts) (*types.Transaction, error)); ok { + return rf(opts) + } + if rf, ok := ret.Get(0).(func(*bind.TransactOpts) *types.Transaction); ok { + r0 = rf(opts) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Transaction) + } + } + + if rf, ok := ret.Get(1).(func(*bind.TransactOpts) error); ok { + r1 = rf(opts) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_AcceptOwnership_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AcceptOwnership' +type PriceRegistryInterface_AcceptOwnership_Call struct { + *mock.Call +} + +// AcceptOwnership is a helper method to define mock.On call +// - opts *bind.TransactOpts +func (_e *PriceRegistryInterface_Expecter) AcceptOwnership(opts interface{}) *PriceRegistryInterface_AcceptOwnership_Call { + return &PriceRegistryInterface_AcceptOwnership_Call{Call: _e.mock.On("AcceptOwnership", opts)} +} + +func (_c *PriceRegistryInterface_AcceptOwnership_Call) Run(run func(opts *bind.TransactOpts)) *PriceRegistryInterface_AcceptOwnership_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.TransactOpts)) + }) + return _c +} + +func (_c *PriceRegistryInterface_AcceptOwnership_Call) Return(_a0 *types.Transaction, _a1 error) *PriceRegistryInterface_AcceptOwnership_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_AcceptOwnership_Call) RunAndReturn(run func(*bind.TransactOpts) (*types.Transaction, error)) *PriceRegistryInterface_AcceptOwnership_Call { + _c.Call.Return(run) + return _c +} + +// Address provides a mock function with given fields: +func (_m *PriceRegistryInterface) Address() common.Address { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Address") + } + + var r0 common.Address + if rf, ok := ret.Get(0).(func() common.Address); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(common.Address) + } + } + + return r0 +} + +// PriceRegistryInterface_Address_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Address' +type PriceRegistryInterface_Address_Call struct { + *mock.Call +} + +// Address is a helper method to define mock.On call +func (_e *PriceRegistryInterface_Expecter) Address() *PriceRegistryInterface_Address_Call { + return &PriceRegistryInterface_Address_Call{Call: _e.mock.On("Address")} +} + +func (_c *PriceRegistryInterface_Address_Call) Run(run func()) *PriceRegistryInterface_Address_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *PriceRegistryInterface_Address_Call) Return(_a0 common.Address) *PriceRegistryInterface_Address_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *PriceRegistryInterface_Address_Call) RunAndReturn(run func() common.Address) *PriceRegistryInterface_Address_Call { + _c.Call.Return(run) + return _c +} + +// ApplyAuthorizedCallerUpdates provides a mock function with given fields: opts, authorizedCallerArgs +func (_m *PriceRegistryInterface) ApplyAuthorizedCallerUpdates(opts *bind.TransactOpts, authorizedCallerArgs price_registry.AuthorizedCallersAuthorizedCallerArgs) (*types.Transaction, error) { + ret := _m.Called(opts, authorizedCallerArgs) + + if len(ret) == 0 { + panic("no return value specified for ApplyAuthorizedCallerUpdates") + } + + var r0 *types.Transaction + var r1 error + if rf, ok := ret.Get(0).(func(*bind.TransactOpts, price_registry.AuthorizedCallersAuthorizedCallerArgs) (*types.Transaction, error)); ok { + return rf(opts, authorizedCallerArgs) + } + if rf, ok := ret.Get(0).(func(*bind.TransactOpts, price_registry.AuthorizedCallersAuthorizedCallerArgs) *types.Transaction); ok { + r0 = rf(opts, authorizedCallerArgs) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Transaction) + } + } + + if rf, ok := ret.Get(1).(func(*bind.TransactOpts, price_registry.AuthorizedCallersAuthorizedCallerArgs) error); ok { + r1 = rf(opts, authorizedCallerArgs) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_ApplyAuthorizedCallerUpdates_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ApplyAuthorizedCallerUpdates' +type PriceRegistryInterface_ApplyAuthorizedCallerUpdates_Call struct { + *mock.Call +} + +// ApplyAuthorizedCallerUpdates is a helper method to define mock.On call +// - opts *bind.TransactOpts +// - authorizedCallerArgs price_registry.AuthorizedCallersAuthorizedCallerArgs +func (_e *PriceRegistryInterface_Expecter) ApplyAuthorizedCallerUpdates(opts interface{}, authorizedCallerArgs interface{}) *PriceRegistryInterface_ApplyAuthorizedCallerUpdates_Call { + return &PriceRegistryInterface_ApplyAuthorizedCallerUpdates_Call{Call: _e.mock.On("ApplyAuthorizedCallerUpdates", opts, authorizedCallerArgs)} +} + +func (_c *PriceRegistryInterface_ApplyAuthorizedCallerUpdates_Call) Run(run func(opts *bind.TransactOpts, authorizedCallerArgs price_registry.AuthorizedCallersAuthorizedCallerArgs)) *PriceRegistryInterface_ApplyAuthorizedCallerUpdates_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.TransactOpts), args[1].(price_registry.AuthorizedCallersAuthorizedCallerArgs)) + }) + return _c +} + +func (_c *PriceRegistryInterface_ApplyAuthorizedCallerUpdates_Call) Return(_a0 *types.Transaction, _a1 error) *PriceRegistryInterface_ApplyAuthorizedCallerUpdates_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_ApplyAuthorizedCallerUpdates_Call) RunAndReturn(run func(*bind.TransactOpts, price_registry.AuthorizedCallersAuthorizedCallerArgs) (*types.Transaction, error)) *PriceRegistryInterface_ApplyAuthorizedCallerUpdates_Call { + _c.Call.Return(run) + return _c +} + +// ApplyDestChainConfigUpdates provides a mock function with given fields: opts, destChainConfigArgs +func (_m *PriceRegistryInterface) ApplyDestChainConfigUpdates(opts *bind.TransactOpts, destChainConfigArgs []price_registry.PriceRegistryDestChainConfigArgs) (*types.Transaction, error) { + ret := _m.Called(opts, destChainConfigArgs) + + if len(ret) == 0 { + panic("no return value specified for ApplyDestChainConfigUpdates") + } + + var r0 *types.Transaction + var r1 error + if rf, ok := ret.Get(0).(func(*bind.TransactOpts, []price_registry.PriceRegistryDestChainConfigArgs) (*types.Transaction, error)); ok { + return rf(opts, destChainConfigArgs) + } + if rf, ok := ret.Get(0).(func(*bind.TransactOpts, []price_registry.PriceRegistryDestChainConfigArgs) *types.Transaction); ok { + r0 = rf(opts, destChainConfigArgs) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Transaction) + } + } + + if rf, ok := ret.Get(1).(func(*bind.TransactOpts, []price_registry.PriceRegistryDestChainConfigArgs) error); ok { + r1 = rf(opts, destChainConfigArgs) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_ApplyDestChainConfigUpdates_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ApplyDestChainConfigUpdates' +type PriceRegistryInterface_ApplyDestChainConfigUpdates_Call struct { + *mock.Call +} + +// ApplyDestChainConfigUpdates is a helper method to define mock.On call +// - opts *bind.TransactOpts +// - destChainConfigArgs []price_registry.PriceRegistryDestChainConfigArgs +func (_e *PriceRegistryInterface_Expecter) ApplyDestChainConfigUpdates(opts interface{}, destChainConfigArgs interface{}) *PriceRegistryInterface_ApplyDestChainConfigUpdates_Call { + return &PriceRegistryInterface_ApplyDestChainConfigUpdates_Call{Call: _e.mock.On("ApplyDestChainConfigUpdates", opts, destChainConfigArgs)} +} + +func (_c *PriceRegistryInterface_ApplyDestChainConfigUpdates_Call) Run(run func(opts *bind.TransactOpts, destChainConfigArgs []price_registry.PriceRegistryDestChainConfigArgs)) *PriceRegistryInterface_ApplyDestChainConfigUpdates_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.TransactOpts), args[1].([]price_registry.PriceRegistryDestChainConfigArgs)) + }) + return _c +} + +func (_c *PriceRegistryInterface_ApplyDestChainConfigUpdates_Call) Return(_a0 *types.Transaction, _a1 error) *PriceRegistryInterface_ApplyDestChainConfigUpdates_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_ApplyDestChainConfigUpdates_Call) RunAndReturn(run func(*bind.TransactOpts, []price_registry.PriceRegistryDestChainConfigArgs) (*types.Transaction, error)) *PriceRegistryInterface_ApplyDestChainConfigUpdates_Call { + _c.Call.Return(run) + return _c +} + +// ApplyFeeTokensUpdates provides a mock function with given fields: opts, feeTokensToAdd, feeTokensToRemove +func (_m *PriceRegistryInterface) ApplyFeeTokensUpdates(opts *bind.TransactOpts, feeTokensToAdd []common.Address, feeTokensToRemove []common.Address) (*types.Transaction, error) { + ret := _m.Called(opts, feeTokensToAdd, feeTokensToRemove) + + if len(ret) == 0 { + panic("no return value specified for ApplyFeeTokensUpdates") + } + + var r0 *types.Transaction + var r1 error + if rf, ok := ret.Get(0).(func(*bind.TransactOpts, []common.Address, []common.Address) (*types.Transaction, error)); ok { + return rf(opts, feeTokensToAdd, feeTokensToRemove) + } + if rf, ok := ret.Get(0).(func(*bind.TransactOpts, []common.Address, []common.Address) *types.Transaction); ok { + r0 = rf(opts, feeTokensToAdd, feeTokensToRemove) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Transaction) + } + } + + if rf, ok := ret.Get(1).(func(*bind.TransactOpts, []common.Address, []common.Address) error); ok { + r1 = rf(opts, feeTokensToAdd, feeTokensToRemove) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_ApplyFeeTokensUpdates_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ApplyFeeTokensUpdates' +type PriceRegistryInterface_ApplyFeeTokensUpdates_Call struct { + *mock.Call +} + +// ApplyFeeTokensUpdates is a helper method to define mock.On call +// - opts *bind.TransactOpts +// - feeTokensToAdd []common.Address +// - feeTokensToRemove []common.Address +func (_e *PriceRegistryInterface_Expecter) ApplyFeeTokensUpdates(opts interface{}, feeTokensToAdd interface{}, feeTokensToRemove interface{}) *PriceRegistryInterface_ApplyFeeTokensUpdates_Call { + return &PriceRegistryInterface_ApplyFeeTokensUpdates_Call{Call: _e.mock.On("ApplyFeeTokensUpdates", opts, feeTokensToAdd, feeTokensToRemove)} +} + +func (_c *PriceRegistryInterface_ApplyFeeTokensUpdates_Call) Run(run func(opts *bind.TransactOpts, feeTokensToAdd []common.Address, feeTokensToRemove []common.Address)) *PriceRegistryInterface_ApplyFeeTokensUpdates_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.TransactOpts), args[1].([]common.Address), args[2].([]common.Address)) + }) + return _c +} + +func (_c *PriceRegistryInterface_ApplyFeeTokensUpdates_Call) Return(_a0 *types.Transaction, _a1 error) *PriceRegistryInterface_ApplyFeeTokensUpdates_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_ApplyFeeTokensUpdates_Call) RunAndReturn(run func(*bind.TransactOpts, []common.Address, []common.Address) (*types.Transaction, error)) *PriceRegistryInterface_ApplyFeeTokensUpdates_Call { + _c.Call.Return(run) + return _c +} + +// ApplyPremiumMultiplierWeiPerEthUpdates provides a mock function with given fields: opts, premiumMultiplierWeiPerEthArgs +func (_m *PriceRegistryInterface) ApplyPremiumMultiplierWeiPerEthUpdates(opts *bind.TransactOpts, premiumMultiplierWeiPerEthArgs []price_registry.PriceRegistryPremiumMultiplierWeiPerEthArgs) (*types.Transaction, error) { + ret := _m.Called(opts, premiumMultiplierWeiPerEthArgs) + + if len(ret) == 0 { + panic("no return value specified for ApplyPremiumMultiplierWeiPerEthUpdates") + } + + var r0 *types.Transaction + var r1 error + if rf, ok := ret.Get(0).(func(*bind.TransactOpts, []price_registry.PriceRegistryPremiumMultiplierWeiPerEthArgs) (*types.Transaction, error)); ok { + return rf(opts, premiumMultiplierWeiPerEthArgs) + } + if rf, ok := ret.Get(0).(func(*bind.TransactOpts, []price_registry.PriceRegistryPremiumMultiplierWeiPerEthArgs) *types.Transaction); ok { + r0 = rf(opts, premiumMultiplierWeiPerEthArgs) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Transaction) + } + } + + if rf, ok := ret.Get(1).(func(*bind.TransactOpts, []price_registry.PriceRegistryPremiumMultiplierWeiPerEthArgs) error); ok { + r1 = rf(opts, premiumMultiplierWeiPerEthArgs) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_ApplyPremiumMultiplierWeiPerEthUpdates_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ApplyPremiumMultiplierWeiPerEthUpdates' +type PriceRegistryInterface_ApplyPremiumMultiplierWeiPerEthUpdates_Call struct { + *mock.Call +} + +// ApplyPremiumMultiplierWeiPerEthUpdates is a helper method to define mock.On call +// - opts *bind.TransactOpts +// - premiumMultiplierWeiPerEthArgs []price_registry.PriceRegistryPremiumMultiplierWeiPerEthArgs +func (_e *PriceRegistryInterface_Expecter) ApplyPremiumMultiplierWeiPerEthUpdates(opts interface{}, premiumMultiplierWeiPerEthArgs interface{}) *PriceRegistryInterface_ApplyPremiumMultiplierWeiPerEthUpdates_Call { + return &PriceRegistryInterface_ApplyPremiumMultiplierWeiPerEthUpdates_Call{Call: _e.mock.On("ApplyPremiumMultiplierWeiPerEthUpdates", opts, premiumMultiplierWeiPerEthArgs)} +} + +func (_c *PriceRegistryInterface_ApplyPremiumMultiplierWeiPerEthUpdates_Call) Run(run func(opts *bind.TransactOpts, premiumMultiplierWeiPerEthArgs []price_registry.PriceRegistryPremiumMultiplierWeiPerEthArgs)) *PriceRegistryInterface_ApplyPremiumMultiplierWeiPerEthUpdates_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.TransactOpts), args[1].([]price_registry.PriceRegistryPremiumMultiplierWeiPerEthArgs)) + }) + return _c +} + +func (_c *PriceRegistryInterface_ApplyPremiumMultiplierWeiPerEthUpdates_Call) Return(_a0 *types.Transaction, _a1 error) *PriceRegistryInterface_ApplyPremiumMultiplierWeiPerEthUpdates_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_ApplyPremiumMultiplierWeiPerEthUpdates_Call) RunAndReturn(run func(*bind.TransactOpts, []price_registry.PriceRegistryPremiumMultiplierWeiPerEthArgs) (*types.Transaction, error)) *PriceRegistryInterface_ApplyPremiumMultiplierWeiPerEthUpdates_Call { + _c.Call.Return(run) + return _c +} + +// ApplyTokenTransferFeeConfigUpdates provides a mock function with given fields: opts, tokenTransferFeeConfigArgs, tokensToUseDefaultFeeConfigs +func (_m *PriceRegistryInterface) ApplyTokenTransferFeeConfigUpdates(opts *bind.TransactOpts, tokenTransferFeeConfigArgs []price_registry.PriceRegistryTokenTransferFeeConfigArgs, tokensToUseDefaultFeeConfigs []price_registry.PriceRegistryTokenTransferFeeConfigRemoveArgs) (*types.Transaction, error) { + ret := _m.Called(opts, tokenTransferFeeConfigArgs, tokensToUseDefaultFeeConfigs) + + if len(ret) == 0 { + panic("no return value specified for ApplyTokenTransferFeeConfigUpdates") + } + + var r0 *types.Transaction + var r1 error + if rf, ok := ret.Get(0).(func(*bind.TransactOpts, []price_registry.PriceRegistryTokenTransferFeeConfigArgs, []price_registry.PriceRegistryTokenTransferFeeConfigRemoveArgs) (*types.Transaction, error)); ok { + return rf(opts, tokenTransferFeeConfigArgs, tokensToUseDefaultFeeConfigs) + } + if rf, ok := ret.Get(0).(func(*bind.TransactOpts, []price_registry.PriceRegistryTokenTransferFeeConfigArgs, []price_registry.PriceRegistryTokenTransferFeeConfigRemoveArgs) *types.Transaction); ok { + r0 = rf(opts, tokenTransferFeeConfigArgs, tokensToUseDefaultFeeConfigs) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Transaction) + } + } + + if rf, ok := ret.Get(1).(func(*bind.TransactOpts, []price_registry.PriceRegistryTokenTransferFeeConfigArgs, []price_registry.PriceRegistryTokenTransferFeeConfigRemoveArgs) error); ok { + r1 = rf(opts, tokenTransferFeeConfigArgs, tokensToUseDefaultFeeConfigs) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_ApplyTokenTransferFeeConfigUpdates_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ApplyTokenTransferFeeConfigUpdates' +type PriceRegistryInterface_ApplyTokenTransferFeeConfigUpdates_Call struct { + *mock.Call +} + +// ApplyTokenTransferFeeConfigUpdates is a helper method to define mock.On call +// - opts *bind.TransactOpts +// - tokenTransferFeeConfigArgs []price_registry.PriceRegistryTokenTransferFeeConfigArgs +// - tokensToUseDefaultFeeConfigs []price_registry.PriceRegistryTokenTransferFeeConfigRemoveArgs +func (_e *PriceRegistryInterface_Expecter) ApplyTokenTransferFeeConfigUpdates(opts interface{}, tokenTransferFeeConfigArgs interface{}, tokensToUseDefaultFeeConfigs interface{}) *PriceRegistryInterface_ApplyTokenTransferFeeConfigUpdates_Call { + return &PriceRegistryInterface_ApplyTokenTransferFeeConfigUpdates_Call{Call: _e.mock.On("ApplyTokenTransferFeeConfigUpdates", opts, tokenTransferFeeConfigArgs, tokensToUseDefaultFeeConfigs)} +} + +func (_c *PriceRegistryInterface_ApplyTokenTransferFeeConfigUpdates_Call) Run(run func(opts *bind.TransactOpts, tokenTransferFeeConfigArgs []price_registry.PriceRegistryTokenTransferFeeConfigArgs, tokensToUseDefaultFeeConfigs []price_registry.PriceRegistryTokenTransferFeeConfigRemoveArgs)) *PriceRegistryInterface_ApplyTokenTransferFeeConfigUpdates_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.TransactOpts), args[1].([]price_registry.PriceRegistryTokenTransferFeeConfigArgs), args[2].([]price_registry.PriceRegistryTokenTransferFeeConfigRemoveArgs)) + }) + return _c +} + +func (_c *PriceRegistryInterface_ApplyTokenTransferFeeConfigUpdates_Call) Return(_a0 *types.Transaction, _a1 error) *PriceRegistryInterface_ApplyTokenTransferFeeConfigUpdates_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_ApplyTokenTransferFeeConfigUpdates_Call) RunAndReturn(run func(*bind.TransactOpts, []price_registry.PriceRegistryTokenTransferFeeConfigArgs, []price_registry.PriceRegistryTokenTransferFeeConfigRemoveArgs) (*types.Transaction, error)) *PriceRegistryInterface_ApplyTokenTransferFeeConfigUpdates_Call { + _c.Call.Return(run) + return _c +} + +// ConvertTokenAmount provides a mock function with given fields: opts, fromToken, fromTokenAmount, toToken +func (_m *PriceRegistryInterface) ConvertTokenAmount(opts *bind.CallOpts, fromToken common.Address, fromTokenAmount *big.Int, toToken common.Address) (*big.Int, error) { + ret := _m.Called(opts, fromToken, fromTokenAmount, toToken) + + if len(ret) == 0 { + panic("no return value specified for ConvertTokenAmount") + } + + var r0 *big.Int + var r1 error + if rf, ok := ret.Get(0).(func(*bind.CallOpts, common.Address, *big.Int, common.Address) (*big.Int, error)); ok { + return rf(opts, fromToken, fromTokenAmount, toToken) + } + if rf, ok := ret.Get(0).(func(*bind.CallOpts, common.Address, *big.Int, common.Address) *big.Int); ok { + r0 = rf(opts, fromToken, fromTokenAmount, toToken) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + } + + if rf, ok := ret.Get(1).(func(*bind.CallOpts, common.Address, *big.Int, common.Address) error); ok { + r1 = rf(opts, fromToken, fromTokenAmount, toToken) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_ConvertTokenAmount_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ConvertTokenAmount' +type PriceRegistryInterface_ConvertTokenAmount_Call struct { + *mock.Call +} + +// ConvertTokenAmount is a helper method to define mock.On call +// - opts *bind.CallOpts +// - fromToken common.Address +// - fromTokenAmount *big.Int +// - toToken common.Address +func (_e *PriceRegistryInterface_Expecter) ConvertTokenAmount(opts interface{}, fromToken interface{}, fromTokenAmount interface{}, toToken interface{}) *PriceRegistryInterface_ConvertTokenAmount_Call { + return &PriceRegistryInterface_ConvertTokenAmount_Call{Call: _e.mock.On("ConvertTokenAmount", opts, fromToken, fromTokenAmount, toToken)} +} + +func (_c *PriceRegistryInterface_ConvertTokenAmount_Call) Run(run func(opts *bind.CallOpts, fromToken common.Address, fromTokenAmount *big.Int, toToken common.Address)) *PriceRegistryInterface_ConvertTokenAmount_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.CallOpts), args[1].(common.Address), args[2].(*big.Int), args[3].(common.Address)) + }) + return _c +} + +func (_c *PriceRegistryInterface_ConvertTokenAmount_Call) Return(_a0 *big.Int, _a1 error) *PriceRegistryInterface_ConvertTokenAmount_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_ConvertTokenAmount_Call) RunAndReturn(run func(*bind.CallOpts, common.Address, *big.Int, common.Address) (*big.Int, error)) *PriceRegistryInterface_ConvertTokenAmount_Call { + _c.Call.Return(run) + return _c +} + +// FilterAuthorizedCallerAdded provides a mock function with given fields: opts +func (_m *PriceRegistryInterface) FilterAuthorizedCallerAdded(opts *bind.FilterOpts) (*price_registry.PriceRegistryAuthorizedCallerAddedIterator, error) { + ret := _m.Called(opts) + + if len(ret) == 0 { + panic("no return value specified for FilterAuthorizedCallerAdded") + } + + var r0 *price_registry.PriceRegistryAuthorizedCallerAddedIterator + var r1 error + if rf, ok := ret.Get(0).(func(*bind.FilterOpts) (*price_registry.PriceRegistryAuthorizedCallerAddedIterator, error)); ok { + return rf(opts) + } + if rf, ok := ret.Get(0).(func(*bind.FilterOpts) *price_registry.PriceRegistryAuthorizedCallerAddedIterator); ok { + r0 = rf(opts) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryAuthorizedCallerAddedIterator) + } + } + + if rf, ok := ret.Get(1).(func(*bind.FilterOpts) error); ok { + r1 = rf(opts) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_FilterAuthorizedCallerAdded_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FilterAuthorizedCallerAdded' +type PriceRegistryInterface_FilterAuthorizedCallerAdded_Call struct { + *mock.Call +} + +// FilterAuthorizedCallerAdded is a helper method to define mock.On call +// - opts *bind.FilterOpts +func (_e *PriceRegistryInterface_Expecter) FilterAuthorizedCallerAdded(opts interface{}) *PriceRegistryInterface_FilterAuthorizedCallerAdded_Call { + return &PriceRegistryInterface_FilterAuthorizedCallerAdded_Call{Call: _e.mock.On("FilterAuthorizedCallerAdded", opts)} +} + +func (_c *PriceRegistryInterface_FilterAuthorizedCallerAdded_Call) Run(run func(opts *bind.FilterOpts)) *PriceRegistryInterface_FilterAuthorizedCallerAdded_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.FilterOpts)) + }) + return _c +} + +func (_c *PriceRegistryInterface_FilterAuthorizedCallerAdded_Call) Return(_a0 *price_registry.PriceRegistryAuthorizedCallerAddedIterator, _a1 error) *PriceRegistryInterface_FilterAuthorizedCallerAdded_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_FilterAuthorizedCallerAdded_Call) RunAndReturn(run func(*bind.FilterOpts) (*price_registry.PriceRegistryAuthorizedCallerAddedIterator, error)) *PriceRegistryInterface_FilterAuthorizedCallerAdded_Call { + _c.Call.Return(run) + return _c +} + +// FilterAuthorizedCallerRemoved provides a mock function with given fields: opts +func (_m *PriceRegistryInterface) FilterAuthorizedCallerRemoved(opts *bind.FilterOpts) (*price_registry.PriceRegistryAuthorizedCallerRemovedIterator, error) { + ret := _m.Called(opts) + + if len(ret) == 0 { + panic("no return value specified for FilterAuthorizedCallerRemoved") + } + + var r0 *price_registry.PriceRegistryAuthorizedCallerRemovedIterator + var r1 error + if rf, ok := ret.Get(0).(func(*bind.FilterOpts) (*price_registry.PriceRegistryAuthorizedCallerRemovedIterator, error)); ok { + return rf(opts) + } + if rf, ok := ret.Get(0).(func(*bind.FilterOpts) *price_registry.PriceRegistryAuthorizedCallerRemovedIterator); ok { + r0 = rf(opts) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryAuthorizedCallerRemovedIterator) + } + } + + if rf, ok := ret.Get(1).(func(*bind.FilterOpts) error); ok { + r1 = rf(opts) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_FilterAuthorizedCallerRemoved_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FilterAuthorizedCallerRemoved' +type PriceRegistryInterface_FilterAuthorizedCallerRemoved_Call struct { + *mock.Call +} + +// FilterAuthorizedCallerRemoved is a helper method to define mock.On call +// - opts *bind.FilterOpts +func (_e *PriceRegistryInterface_Expecter) FilterAuthorizedCallerRemoved(opts interface{}) *PriceRegistryInterface_FilterAuthorizedCallerRemoved_Call { + return &PriceRegistryInterface_FilterAuthorizedCallerRemoved_Call{Call: _e.mock.On("FilterAuthorizedCallerRemoved", opts)} +} + +func (_c *PriceRegistryInterface_FilterAuthorizedCallerRemoved_Call) Run(run func(opts *bind.FilterOpts)) *PriceRegistryInterface_FilterAuthorizedCallerRemoved_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.FilterOpts)) + }) + return _c +} + +func (_c *PriceRegistryInterface_FilterAuthorizedCallerRemoved_Call) Return(_a0 *price_registry.PriceRegistryAuthorizedCallerRemovedIterator, _a1 error) *PriceRegistryInterface_FilterAuthorizedCallerRemoved_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_FilterAuthorizedCallerRemoved_Call) RunAndReturn(run func(*bind.FilterOpts) (*price_registry.PriceRegistryAuthorizedCallerRemovedIterator, error)) *PriceRegistryInterface_FilterAuthorizedCallerRemoved_Call { + _c.Call.Return(run) + return _c +} + +// FilterDestChainAdded provides a mock function with given fields: opts, destChainSelector +func (_m *PriceRegistryInterface) FilterDestChainAdded(opts *bind.FilterOpts, destChainSelector []uint64) (*price_registry.PriceRegistryDestChainAddedIterator, error) { + ret := _m.Called(opts, destChainSelector) + + if len(ret) == 0 { + panic("no return value specified for FilterDestChainAdded") + } + + var r0 *price_registry.PriceRegistryDestChainAddedIterator + var r1 error + if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []uint64) (*price_registry.PriceRegistryDestChainAddedIterator, error)); ok { + return rf(opts, destChainSelector) + } + if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []uint64) *price_registry.PriceRegistryDestChainAddedIterator); ok { + r0 = rf(opts, destChainSelector) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryDestChainAddedIterator) + } + } + + if rf, ok := ret.Get(1).(func(*bind.FilterOpts, []uint64) error); ok { + r1 = rf(opts, destChainSelector) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_FilterDestChainAdded_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FilterDestChainAdded' +type PriceRegistryInterface_FilterDestChainAdded_Call struct { + *mock.Call +} + +// FilterDestChainAdded is a helper method to define mock.On call +// - opts *bind.FilterOpts +// - destChainSelector []uint64 +func (_e *PriceRegistryInterface_Expecter) FilterDestChainAdded(opts interface{}, destChainSelector interface{}) *PriceRegistryInterface_FilterDestChainAdded_Call { + return &PriceRegistryInterface_FilterDestChainAdded_Call{Call: _e.mock.On("FilterDestChainAdded", opts, destChainSelector)} +} + +func (_c *PriceRegistryInterface_FilterDestChainAdded_Call) Run(run func(opts *bind.FilterOpts, destChainSelector []uint64)) *PriceRegistryInterface_FilterDestChainAdded_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.FilterOpts), args[1].([]uint64)) + }) + return _c +} + +func (_c *PriceRegistryInterface_FilterDestChainAdded_Call) Return(_a0 *price_registry.PriceRegistryDestChainAddedIterator, _a1 error) *PriceRegistryInterface_FilterDestChainAdded_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_FilterDestChainAdded_Call) RunAndReturn(run func(*bind.FilterOpts, []uint64) (*price_registry.PriceRegistryDestChainAddedIterator, error)) *PriceRegistryInterface_FilterDestChainAdded_Call { + _c.Call.Return(run) + return _c +} + +// FilterDestChainConfigUpdated provides a mock function with given fields: opts, destChainSelector +func (_m *PriceRegistryInterface) FilterDestChainConfigUpdated(opts *bind.FilterOpts, destChainSelector []uint64) (*price_registry.PriceRegistryDestChainConfigUpdatedIterator, error) { + ret := _m.Called(opts, destChainSelector) + + if len(ret) == 0 { + panic("no return value specified for FilterDestChainConfigUpdated") + } + + var r0 *price_registry.PriceRegistryDestChainConfigUpdatedIterator + var r1 error + if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []uint64) (*price_registry.PriceRegistryDestChainConfigUpdatedIterator, error)); ok { + return rf(opts, destChainSelector) + } + if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []uint64) *price_registry.PriceRegistryDestChainConfigUpdatedIterator); ok { + r0 = rf(opts, destChainSelector) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryDestChainConfigUpdatedIterator) + } + } + + if rf, ok := ret.Get(1).(func(*bind.FilterOpts, []uint64) error); ok { + r1 = rf(opts, destChainSelector) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_FilterDestChainConfigUpdated_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FilterDestChainConfigUpdated' +type PriceRegistryInterface_FilterDestChainConfigUpdated_Call struct { + *mock.Call +} + +// FilterDestChainConfigUpdated is a helper method to define mock.On call +// - opts *bind.FilterOpts +// - destChainSelector []uint64 +func (_e *PriceRegistryInterface_Expecter) FilterDestChainConfigUpdated(opts interface{}, destChainSelector interface{}) *PriceRegistryInterface_FilterDestChainConfigUpdated_Call { + return &PriceRegistryInterface_FilterDestChainConfigUpdated_Call{Call: _e.mock.On("FilterDestChainConfigUpdated", opts, destChainSelector)} +} + +func (_c *PriceRegistryInterface_FilterDestChainConfigUpdated_Call) Run(run func(opts *bind.FilterOpts, destChainSelector []uint64)) *PriceRegistryInterface_FilterDestChainConfigUpdated_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.FilterOpts), args[1].([]uint64)) + }) + return _c +} + +func (_c *PriceRegistryInterface_FilterDestChainConfigUpdated_Call) Return(_a0 *price_registry.PriceRegistryDestChainConfigUpdatedIterator, _a1 error) *PriceRegistryInterface_FilterDestChainConfigUpdated_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_FilterDestChainConfigUpdated_Call) RunAndReturn(run func(*bind.FilterOpts, []uint64) (*price_registry.PriceRegistryDestChainConfigUpdatedIterator, error)) *PriceRegistryInterface_FilterDestChainConfigUpdated_Call { + _c.Call.Return(run) + return _c +} + +// FilterFeeTokenAdded provides a mock function with given fields: opts, feeToken +func (_m *PriceRegistryInterface) FilterFeeTokenAdded(opts *bind.FilterOpts, feeToken []common.Address) (*price_registry.PriceRegistryFeeTokenAddedIterator, error) { + ret := _m.Called(opts, feeToken) + + if len(ret) == 0 { + panic("no return value specified for FilterFeeTokenAdded") + } + + var r0 *price_registry.PriceRegistryFeeTokenAddedIterator + var r1 error + if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address) (*price_registry.PriceRegistryFeeTokenAddedIterator, error)); ok { + return rf(opts, feeToken) + } + if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address) *price_registry.PriceRegistryFeeTokenAddedIterator); ok { + r0 = rf(opts, feeToken) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryFeeTokenAddedIterator) + } + } + + if rf, ok := ret.Get(1).(func(*bind.FilterOpts, []common.Address) error); ok { + r1 = rf(opts, feeToken) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_FilterFeeTokenAdded_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FilterFeeTokenAdded' +type PriceRegistryInterface_FilterFeeTokenAdded_Call struct { + *mock.Call +} + +// FilterFeeTokenAdded is a helper method to define mock.On call +// - opts *bind.FilterOpts +// - feeToken []common.Address +func (_e *PriceRegistryInterface_Expecter) FilterFeeTokenAdded(opts interface{}, feeToken interface{}) *PriceRegistryInterface_FilterFeeTokenAdded_Call { + return &PriceRegistryInterface_FilterFeeTokenAdded_Call{Call: _e.mock.On("FilterFeeTokenAdded", opts, feeToken)} +} + +func (_c *PriceRegistryInterface_FilterFeeTokenAdded_Call) Run(run func(opts *bind.FilterOpts, feeToken []common.Address)) *PriceRegistryInterface_FilterFeeTokenAdded_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.FilterOpts), args[1].([]common.Address)) + }) + return _c +} + +func (_c *PriceRegistryInterface_FilterFeeTokenAdded_Call) Return(_a0 *price_registry.PriceRegistryFeeTokenAddedIterator, _a1 error) *PriceRegistryInterface_FilterFeeTokenAdded_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_FilterFeeTokenAdded_Call) RunAndReturn(run func(*bind.FilterOpts, []common.Address) (*price_registry.PriceRegistryFeeTokenAddedIterator, error)) *PriceRegistryInterface_FilterFeeTokenAdded_Call { + _c.Call.Return(run) + return _c +} + +// FilterFeeTokenRemoved provides a mock function with given fields: opts, feeToken +func (_m *PriceRegistryInterface) FilterFeeTokenRemoved(opts *bind.FilterOpts, feeToken []common.Address) (*price_registry.PriceRegistryFeeTokenRemovedIterator, error) { + ret := _m.Called(opts, feeToken) + + if len(ret) == 0 { + panic("no return value specified for FilterFeeTokenRemoved") + } + + var r0 *price_registry.PriceRegistryFeeTokenRemovedIterator + var r1 error + if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address) (*price_registry.PriceRegistryFeeTokenRemovedIterator, error)); ok { + return rf(opts, feeToken) + } + if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address) *price_registry.PriceRegistryFeeTokenRemovedIterator); ok { + r0 = rf(opts, feeToken) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryFeeTokenRemovedIterator) + } + } + + if rf, ok := ret.Get(1).(func(*bind.FilterOpts, []common.Address) error); ok { + r1 = rf(opts, feeToken) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_FilterFeeTokenRemoved_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FilterFeeTokenRemoved' +type PriceRegistryInterface_FilterFeeTokenRemoved_Call struct { + *mock.Call +} + +// FilterFeeTokenRemoved is a helper method to define mock.On call +// - opts *bind.FilterOpts +// - feeToken []common.Address +func (_e *PriceRegistryInterface_Expecter) FilterFeeTokenRemoved(opts interface{}, feeToken interface{}) *PriceRegistryInterface_FilterFeeTokenRemoved_Call { + return &PriceRegistryInterface_FilterFeeTokenRemoved_Call{Call: _e.mock.On("FilterFeeTokenRemoved", opts, feeToken)} +} + +func (_c *PriceRegistryInterface_FilterFeeTokenRemoved_Call) Run(run func(opts *bind.FilterOpts, feeToken []common.Address)) *PriceRegistryInterface_FilterFeeTokenRemoved_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.FilterOpts), args[1].([]common.Address)) + }) + return _c +} + +func (_c *PriceRegistryInterface_FilterFeeTokenRemoved_Call) Return(_a0 *price_registry.PriceRegistryFeeTokenRemovedIterator, _a1 error) *PriceRegistryInterface_FilterFeeTokenRemoved_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_FilterFeeTokenRemoved_Call) RunAndReturn(run func(*bind.FilterOpts, []common.Address) (*price_registry.PriceRegistryFeeTokenRemovedIterator, error)) *PriceRegistryInterface_FilterFeeTokenRemoved_Call { + _c.Call.Return(run) + return _c +} + +// FilterOwnershipTransferRequested provides a mock function with given fields: opts, from, to +func (_m *PriceRegistryInterface) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*price_registry.PriceRegistryOwnershipTransferRequestedIterator, error) { + ret := _m.Called(opts, from, to) + + if len(ret) == 0 { + panic("no return value specified for FilterOwnershipTransferRequested") + } + + var r0 *price_registry.PriceRegistryOwnershipTransferRequestedIterator + var r1 error + if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address, []common.Address) (*price_registry.PriceRegistryOwnershipTransferRequestedIterator, error)); ok { + return rf(opts, from, to) + } + if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address, []common.Address) *price_registry.PriceRegistryOwnershipTransferRequestedIterator); ok { + r0 = rf(opts, from, to) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryOwnershipTransferRequestedIterator) + } + } + + if rf, ok := ret.Get(1).(func(*bind.FilterOpts, []common.Address, []common.Address) error); ok { + r1 = rf(opts, from, to) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_FilterOwnershipTransferRequested_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FilterOwnershipTransferRequested' +type PriceRegistryInterface_FilterOwnershipTransferRequested_Call struct { + *mock.Call +} + +// FilterOwnershipTransferRequested is a helper method to define mock.On call +// - opts *bind.FilterOpts +// - from []common.Address +// - to []common.Address +func (_e *PriceRegistryInterface_Expecter) FilterOwnershipTransferRequested(opts interface{}, from interface{}, to interface{}) *PriceRegistryInterface_FilterOwnershipTransferRequested_Call { + return &PriceRegistryInterface_FilterOwnershipTransferRequested_Call{Call: _e.mock.On("FilterOwnershipTransferRequested", opts, from, to)} +} + +func (_c *PriceRegistryInterface_FilterOwnershipTransferRequested_Call) Run(run func(opts *bind.FilterOpts, from []common.Address, to []common.Address)) *PriceRegistryInterface_FilterOwnershipTransferRequested_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.FilterOpts), args[1].([]common.Address), args[2].([]common.Address)) + }) + return _c +} + +func (_c *PriceRegistryInterface_FilterOwnershipTransferRequested_Call) Return(_a0 *price_registry.PriceRegistryOwnershipTransferRequestedIterator, _a1 error) *PriceRegistryInterface_FilterOwnershipTransferRequested_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_FilterOwnershipTransferRequested_Call) RunAndReturn(run func(*bind.FilterOpts, []common.Address, []common.Address) (*price_registry.PriceRegistryOwnershipTransferRequestedIterator, error)) *PriceRegistryInterface_FilterOwnershipTransferRequested_Call { + _c.Call.Return(run) + return _c +} + +// FilterOwnershipTransferred provides a mock function with given fields: opts, from, to +func (_m *PriceRegistryInterface) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*price_registry.PriceRegistryOwnershipTransferredIterator, error) { + ret := _m.Called(opts, from, to) + + if len(ret) == 0 { + panic("no return value specified for FilterOwnershipTransferred") + } + + var r0 *price_registry.PriceRegistryOwnershipTransferredIterator + var r1 error + if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address, []common.Address) (*price_registry.PriceRegistryOwnershipTransferredIterator, error)); ok { + return rf(opts, from, to) + } + if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address, []common.Address) *price_registry.PriceRegistryOwnershipTransferredIterator); ok { + r0 = rf(opts, from, to) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryOwnershipTransferredIterator) + } + } + + if rf, ok := ret.Get(1).(func(*bind.FilterOpts, []common.Address, []common.Address) error); ok { + r1 = rf(opts, from, to) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_FilterOwnershipTransferred_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FilterOwnershipTransferred' +type PriceRegistryInterface_FilterOwnershipTransferred_Call struct { + *mock.Call +} + +// FilterOwnershipTransferred is a helper method to define mock.On call +// - opts *bind.FilterOpts +// - from []common.Address +// - to []common.Address +func (_e *PriceRegistryInterface_Expecter) FilterOwnershipTransferred(opts interface{}, from interface{}, to interface{}) *PriceRegistryInterface_FilterOwnershipTransferred_Call { + return &PriceRegistryInterface_FilterOwnershipTransferred_Call{Call: _e.mock.On("FilterOwnershipTransferred", opts, from, to)} +} + +func (_c *PriceRegistryInterface_FilterOwnershipTransferred_Call) Run(run func(opts *bind.FilterOpts, from []common.Address, to []common.Address)) *PriceRegistryInterface_FilterOwnershipTransferred_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.FilterOpts), args[1].([]common.Address), args[2].([]common.Address)) + }) + return _c +} + +func (_c *PriceRegistryInterface_FilterOwnershipTransferred_Call) Return(_a0 *price_registry.PriceRegistryOwnershipTransferredIterator, _a1 error) *PriceRegistryInterface_FilterOwnershipTransferred_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_FilterOwnershipTransferred_Call) RunAndReturn(run func(*bind.FilterOpts, []common.Address, []common.Address) (*price_registry.PriceRegistryOwnershipTransferredIterator, error)) *PriceRegistryInterface_FilterOwnershipTransferred_Call { + _c.Call.Return(run) + return _c +} + +// FilterPremiumMultiplierWeiPerEthUpdated provides a mock function with given fields: opts, token +func (_m *PriceRegistryInterface) FilterPremiumMultiplierWeiPerEthUpdated(opts *bind.FilterOpts, token []common.Address) (*price_registry.PriceRegistryPremiumMultiplierWeiPerEthUpdatedIterator, error) { + ret := _m.Called(opts, token) + + if len(ret) == 0 { + panic("no return value specified for FilterPremiumMultiplierWeiPerEthUpdated") + } + + var r0 *price_registry.PriceRegistryPremiumMultiplierWeiPerEthUpdatedIterator + var r1 error + if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address) (*price_registry.PriceRegistryPremiumMultiplierWeiPerEthUpdatedIterator, error)); ok { + return rf(opts, token) + } + if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address) *price_registry.PriceRegistryPremiumMultiplierWeiPerEthUpdatedIterator); ok { + r0 = rf(opts, token) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryPremiumMultiplierWeiPerEthUpdatedIterator) + } + } + + if rf, ok := ret.Get(1).(func(*bind.FilterOpts, []common.Address) error); ok { + r1 = rf(opts, token) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_FilterPremiumMultiplierWeiPerEthUpdated_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FilterPremiumMultiplierWeiPerEthUpdated' +type PriceRegistryInterface_FilterPremiumMultiplierWeiPerEthUpdated_Call struct { + *mock.Call +} + +// FilterPremiumMultiplierWeiPerEthUpdated is a helper method to define mock.On call +// - opts *bind.FilterOpts +// - token []common.Address +func (_e *PriceRegistryInterface_Expecter) FilterPremiumMultiplierWeiPerEthUpdated(opts interface{}, token interface{}) *PriceRegistryInterface_FilterPremiumMultiplierWeiPerEthUpdated_Call { + return &PriceRegistryInterface_FilterPremiumMultiplierWeiPerEthUpdated_Call{Call: _e.mock.On("FilterPremiumMultiplierWeiPerEthUpdated", opts, token)} +} + +func (_c *PriceRegistryInterface_FilterPremiumMultiplierWeiPerEthUpdated_Call) Run(run func(opts *bind.FilterOpts, token []common.Address)) *PriceRegistryInterface_FilterPremiumMultiplierWeiPerEthUpdated_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.FilterOpts), args[1].([]common.Address)) + }) + return _c +} + +func (_c *PriceRegistryInterface_FilterPremiumMultiplierWeiPerEthUpdated_Call) Return(_a0 *price_registry.PriceRegistryPremiumMultiplierWeiPerEthUpdatedIterator, _a1 error) *PriceRegistryInterface_FilterPremiumMultiplierWeiPerEthUpdated_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_FilterPremiumMultiplierWeiPerEthUpdated_Call) RunAndReturn(run func(*bind.FilterOpts, []common.Address) (*price_registry.PriceRegistryPremiumMultiplierWeiPerEthUpdatedIterator, error)) *PriceRegistryInterface_FilterPremiumMultiplierWeiPerEthUpdated_Call { + _c.Call.Return(run) + return _c +} + +// FilterPriceFeedPerTokenUpdated provides a mock function with given fields: opts, token +func (_m *PriceRegistryInterface) FilterPriceFeedPerTokenUpdated(opts *bind.FilterOpts, token []common.Address) (*price_registry.PriceRegistryPriceFeedPerTokenUpdatedIterator, error) { + ret := _m.Called(opts, token) + + if len(ret) == 0 { + panic("no return value specified for FilterPriceFeedPerTokenUpdated") + } + + var r0 *price_registry.PriceRegistryPriceFeedPerTokenUpdatedIterator + var r1 error + if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address) (*price_registry.PriceRegistryPriceFeedPerTokenUpdatedIterator, error)); ok { + return rf(opts, token) + } + if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address) *price_registry.PriceRegistryPriceFeedPerTokenUpdatedIterator); ok { + r0 = rf(opts, token) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryPriceFeedPerTokenUpdatedIterator) + } + } + + if rf, ok := ret.Get(1).(func(*bind.FilterOpts, []common.Address) error); ok { + r1 = rf(opts, token) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_FilterPriceFeedPerTokenUpdated_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FilterPriceFeedPerTokenUpdated' +type PriceRegistryInterface_FilterPriceFeedPerTokenUpdated_Call struct { + *mock.Call +} + +// FilterPriceFeedPerTokenUpdated is a helper method to define mock.On call +// - opts *bind.FilterOpts +// - token []common.Address +func (_e *PriceRegistryInterface_Expecter) FilterPriceFeedPerTokenUpdated(opts interface{}, token interface{}) *PriceRegistryInterface_FilterPriceFeedPerTokenUpdated_Call { + return &PriceRegistryInterface_FilterPriceFeedPerTokenUpdated_Call{Call: _e.mock.On("FilterPriceFeedPerTokenUpdated", opts, token)} +} + +func (_c *PriceRegistryInterface_FilterPriceFeedPerTokenUpdated_Call) Run(run func(opts *bind.FilterOpts, token []common.Address)) *PriceRegistryInterface_FilterPriceFeedPerTokenUpdated_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.FilterOpts), args[1].([]common.Address)) + }) + return _c +} + +func (_c *PriceRegistryInterface_FilterPriceFeedPerTokenUpdated_Call) Return(_a0 *price_registry.PriceRegistryPriceFeedPerTokenUpdatedIterator, _a1 error) *PriceRegistryInterface_FilterPriceFeedPerTokenUpdated_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_FilterPriceFeedPerTokenUpdated_Call) RunAndReturn(run func(*bind.FilterOpts, []common.Address) (*price_registry.PriceRegistryPriceFeedPerTokenUpdatedIterator, error)) *PriceRegistryInterface_FilterPriceFeedPerTokenUpdated_Call { + _c.Call.Return(run) + return _c +} + +// FilterTokenTransferFeeConfigDeleted provides a mock function with given fields: opts, destChainSelector, token +func (_m *PriceRegistryInterface) FilterTokenTransferFeeConfigDeleted(opts *bind.FilterOpts, destChainSelector []uint64, token []common.Address) (*price_registry.PriceRegistryTokenTransferFeeConfigDeletedIterator, error) { + ret := _m.Called(opts, destChainSelector, token) + + if len(ret) == 0 { + panic("no return value specified for FilterTokenTransferFeeConfigDeleted") + } + + var r0 *price_registry.PriceRegistryTokenTransferFeeConfigDeletedIterator + var r1 error + if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []uint64, []common.Address) (*price_registry.PriceRegistryTokenTransferFeeConfigDeletedIterator, error)); ok { + return rf(opts, destChainSelector, token) + } + if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []uint64, []common.Address) *price_registry.PriceRegistryTokenTransferFeeConfigDeletedIterator); ok { + r0 = rf(opts, destChainSelector, token) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryTokenTransferFeeConfigDeletedIterator) + } + } + + if rf, ok := ret.Get(1).(func(*bind.FilterOpts, []uint64, []common.Address) error); ok { + r1 = rf(opts, destChainSelector, token) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_FilterTokenTransferFeeConfigDeleted_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FilterTokenTransferFeeConfigDeleted' +type PriceRegistryInterface_FilterTokenTransferFeeConfigDeleted_Call struct { + *mock.Call +} + +// FilterTokenTransferFeeConfigDeleted is a helper method to define mock.On call +// - opts *bind.FilterOpts +// - destChainSelector []uint64 +// - token []common.Address +func (_e *PriceRegistryInterface_Expecter) FilterTokenTransferFeeConfigDeleted(opts interface{}, destChainSelector interface{}, token interface{}) *PriceRegistryInterface_FilterTokenTransferFeeConfigDeleted_Call { + return &PriceRegistryInterface_FilterTokenTransferFeeConfigDeleted_Call{Call: _e.mock.On("FilterTokenTransferFeeConfigDeleted", opts, destChainSelector, token)} +} + +func (_c *PriceRegistryInterface_FilterTokenTransferFeeConfigDeleted_Call) Run(run func(opts *bind.FilterOpts, destChainSelector []uint64, token []common.Address)) *PriceRegistryInterface_FilterTokenTransferFeeConfigDeleted_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.FilterOpts), args[1].([]uint64), args[2].([]common.Address)) + }) + return _c +} + +func (_c *PriceRegistryInterface_FilterTokenTransferFeeConfigDeleted_Call) Return(_a0 *price_registry.PriceRegistryTokenTransferFeeConfigDeletedIterator, _a1 error) *PriceRegistryInterface_FilterTokenTransferFeeConfigDeleted_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_FilterTokenTransferFeeConfigDeleted_Call) RunAndReturn(run func(*bind.FilterOpts, []uint64, []common.Address) (*price_registry.PriceRegistryTokenTransferFeeConfigDeletedIterator, error)) *PriceRegistryInterface_FilterTokenTransferFeeConfigDeleted_Call { + _c.Call.Return(run) + return _c +} + +// FilterTokenTransferFeeConfigUpdated provides a mock function with given fields: opts, destChainSelector, token +func (_m *PriceRegistryInterface) FilterTokenTransferFeeConfigUpdated(opts *bind.FilterOpts, destChainSelector []uint64, token []common.Address) (*price_registry.PriceRegistryTokenTransferFeeConfigUpdatedIterator, error) { + ret := _m.Called(opts, destChainSelector, token) + + if len(ret) == 0 { + panic("no return value specified for FilterTokenTransferFeeConfigUpdated") + } + + var r0 *price_registry.PriceRegistryTokenTransferFeeConfigUpdatedIterator + var r1 error + if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []uint64, []common.Address) (*price_registry.PriceRegistryTokenTransferFeeConfigUpdatedIterator, error)); ok { + return rf(opts, destChainSelector, token) + } + if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []uint64, []common.Address) *price_registry.PriceRegistryTokenTransferFeeConfigUpdatedIterator); ok { + r0 = rf(opts, destChainSelector, token) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryTokenTransferFeeConfigUpdatedIterator) + } + } + + if rf, ok := ret.Get(1).(func(*bind.FilterOpts, []uint64, []common.Address) error); ok { + r1 = rf(opts, destChainSelector, token) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_FilterTokenTransferFeeConfigUpdated_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FilterTokenTransferFeeConfigUpdated' +type PriceRegistryInterface_FilterTokenTransferFeeConfigUpdated_Call struct { + *mock.Call +} + +// FilterTokenTransferFeeConfigUpdated is a helper method to define mock.On call +// - opts *bind.FilterOpts +// - destChainSelector []uint64 +// - token []common.Address +func (_e *PriceRegistryInterface_Expecter) FilterTokenTransferFeeConfigUpdated(opts interface{}, destChainSelector interface{}, token interface{}) *PriceRegistryInterface_FilterTokenTransferFeeConfigUpdated_Call { + return &PriceRegistryInterface_FilterTokenTransferFeeConfigUpdated_Call{Call: _e.mock.On("FilterTokenTransferFeeConfigUpdated", opts, destChainSelector, token)} +} + +func (_c *PriceRegistryInterface_FilterTokenTransferFeeConfigUpdated_Call) Run(run func(opts *bind.FilterOpts, destChainSelector []uint64, token []common.Address)) *PriceRegistryInterface_FilterTokenTransferFeeConfigUpdated_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.FilterOpts), args[1].([]uint64), args[2].([]common.Address)) + }) + return _c +} + +func (_c *PriceRegistryInterface_FilterTokenTransferFeeConfigUpdated_Call) Return(_a0 *price_registry.PriceRegistryTokenTransferFeeConfigUpdatedIterator, _a1 error) *PriceRegistryInterface_FilterTokenTransferFeeConfigUpdated_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_FilterTokenTransferFeeConfigUpdated_Call) RunAndReturn(run func(*bind.FilterOpts, []uint64, []common.Address) (*price_registry.PriceRegistryTokenTransferFeeConfigUpdatedIterator, error)) *PriceRegistryInterface_FilterTokenTransferFeeConfigUpdated_Call { + _c.Call.Return(run) + return _c +} + +// FilterUsdPerTokenUpdated provides a mock function with given fields: opts, token +func (_m *PriceRegistryInterface) FilterUsdPerTokenUpdated(opts *bind.FilterOpts, token []common.Address) (*price_registry.PriceRegistryUsdPerTokenUpdatedIterator, error) { + ret := _m.Called(opts, token) + + if len(ret) == 0 { + panic("no return value specified for FilterUsdPerTokenUpdated") + } + + var r0 *price_registry.PriceRegistryUsdPerTokenUpdatedIterator + var r1 error + if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address) (*price_registry.PriceRegistryUsdPerTokenUpdatedIterator, error)); ok { + return rf(opts, token) + } + if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []common.Address) *price_registry.PriceRegistryUsdPerTokenUpdatedIterator); ok { + r0 = rf(opts, token) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryUsdPerTokenUpdatedIterator) + } + } + + if rf, ok := ret.Get(1).(func(*bind.FilterOpts, []common.Address) error); ok { + r1 = rf(opts, token) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_FilterUsdPerTokenUpdated_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FilterUsdPerTokenUpdated' +type PriceRegistryInterface_FilterUsdPerTokenUpdated_Call struct { + *mock.Call +} + +// FilterUsdPerTokenUpdated is a helper method to define mock.On call +// - opts *bind.FilterOpts +// - token []common.Address +func (_e *PriceRegistryInterface_Expecter) FilterUsdPerTokenUpdated(opts interface{}, token interface{}) *PriceRegistryInterface_FilterUsdPerTokenUpdated_Call { + return &PriceRegistryInterface_FilterUsdPerTokenUpdated_Call{Call: _e.mock.On("FilterUsdPerTokenUpdated", opts, token)} +} + +func (_c *PriceRegistryInterface_FilterUsdPerTokenUpdated_Call) Run(run func(opts *bind.FilterOpts, token []common.Address)) *PriceRegistryInterface_FilterUsdPerTokenUpdated_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.FilterOpts), args[1].([]common.Address)) + }) + return _c +} + +func (_c *PriceRegistryInterface_FilterUsdPerTokenUpdated_Call) Return(_a0 *price_registry.PriceRegistryUsdPerTokenUpdatedIterator, _a1 error) *PriceRegistryInterface_FilterUsdPerTokenUpdated_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_FilterUsdPerTokenUpdated_Call) RunAndReturn(run func(*bind.FilterOpts, []common.Address) (*price_registry.PriceRegistryUsdPerTokenUpdatedIterator, error)) *PriceRegistryInterface_FilterUsdPerTokenUpdated_Call { + _c.Call.Return(run) + return _c +} + +// FilterUsdPerUnitGasUpdated provides a mock function with given fields: opts, destChain +func (_m *PriceRegistryInterface) FilterUsdPerUnitGasUpdated(opts *bind.FilterOpts, destChain []uint64) (*price_registry.PriceRegistryUsdPerUnitGasUpdatedIterator, error) { + ret := _m.Called(opts, destChain) + + if len(ret) == 0 { + panic("no return value specified for FilterUsdPerUnitGasUpdated") + } + + var r0 *price_registry.PriceRegistryUsdPerUnitGasUpdatedIterator + var r1 error + if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []uint64) (*price_registry.PriceRegistryUsdPerUnitGasUpdatedIterator, error)); ok { + return rf(opts, destChain) + } + if rf, ok := ret.Get(0).(func(*bind.FilterOpts, []uint64) *price_registry.PriceRegistryUsdPerUnitGasUpdatedIterator); ok { + r0 = rf(opts, destChain) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryUsdPerUnitGasUpdatedIterator) + } + } + + if rf, ok := ret.Get(1).(func(*bind.FilterOpts, []uint64) error); ok { + r1 = rf(opts, destChain) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_FilterUsdPerUnitGasUpdated_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FilterUsdPerUnitGasUpdated' +type PriceRegistryInterface_FilterUsdPerUnitGasUpdated_Call struct { + *mock.Call +} + +// FilterUsdPerUnitGasUpdated is a helper method to define mock.On call +// - opts *bind.FilterOpts +// - destChain []uint64 +func (_e *PriceRegistryInterface_Expecter) FilterUsdPerUnitGasUpdated(opts interface{}, destChain interface{}) *PriceRegistryInterface_FilterUsdPerUnitGasUpdated_Call { + return &PriceRegistryInterface_FilterUsdPerUnitGasUpdated_Call{Call: _e.mock.On("FilterUsdPerUnitGasUpdated", opts, destChain)} +} + +func (_c *PriceRegistryInterface_FilterUsdPerUnitGasUpdated_Call) Run(run func(opts *bind.FilterOpts, destChain []uint64)) *PriceRegistryInterface_FilterUsdPerUnitGasUpdated_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.FilterOpts), args[1].([]uint64)) + }) + return _c +} + +func (_c *PriceRegistryInterface_FilterUsdPerUnitGasUpdated_Call) Return(_a0 *price_registry.PriceRegistryUsdPerUnitGasUpdatedIterator, _a1 error) *PriceRegistryInterface_FilterUsdPerUnitGasUpdated_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_FilterUsdPerUnitGasUpdated_Call) RunAndReturn(run func(*bind.FilterOpts, []uint64) (*price_registry.PriceRegistryUsdPerUnitGasUpdatedIterator, error)) *PriceRegistryInterface_FilterUsdPerUnitGasUpdated_Call { + _c.Call.Return(run) + return _c +} + +// GetAllAuthorizedCallers provides a mock function with given fields: opts +func (_m *PriceRegistryInterface) GetAllAuthorizedCallers(opts *bind.CallOpts) ([]common.Address, error) { + ret := _m.Called(opts) + + if len(ret) == 0 { + panic("no return value specified for GetAllAuthorizedCallers") + } + + var r0 []common.Address + var r1 error + if rf, ok := ret.Get(0).(func(*bind.CallOpts) ([]common.Address, error)); ok { + return rf(opts) + } + if rf, ok := ret.Get(0).(func(*bind.CallOpts) []common.Address); ok { + r0 = rf(opts) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]common.Address) + } + } + + if rf, ok := ret.Get(1).(func(*bind.CallOpts) error); ok { + r1 = rf(opts) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_GetAllAuthorizedCallers_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetAllAuthorizedCallers' +type PriceRegistryInterface_GetAllAuthorizedCallers_Call struct { + *mock.Call +} + +// GetAllAuthorizedCallers is a helper method to define mock.On call +// - opts *bind.CallOpts +func (_e *PriceRegistryInterface_Expecter) GetAllAuthorizedCallers(opts interface{}) *PriceRegistryInterface_GetAllAuthorizedCallers_Call { + return &PriceRegistryInterface_GetAllAuthorizedCallers_Call{Call: _e.mock.On("GetAllAuthorizedCallers", opts)} +} + +func (_c *PriceRegistryInterface_GetAllAuthorizedCallers_Call) Run(run func(opts *bind.CallOpts)) *PriceRegistryInterface_GetAllAuthorizedCallers_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.CallOpts)) + }) + return _c +} + +func (_c *PriceRegistryInterface_GetAllAuthorizedCallers_Call) Return(_a0 []common.Address, _a1 error) *PriceRegistryInterface_GetAllAuthorizedCallers_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_GetAllAuthorizedCallers_Call) RunAndReturn(run func(*bind.CallOpts) ([]common.Address, error)) *PriceRegistryInterface_GetAllAuthorizedCallers_Call { + _c.Call.Return(run) + return _c +} + +// GetDestChainConfig provides a mock function with given fields: opts, destChainSelector +func (_m *PriceRegistryInterface) GetDestChainConfig(opts *bind.CallOpts, destChainSelector uint64) (price_registry.PriceRegistryDestChainConfig, error) { + ret := _m.Called(opts, destChainSelector) + + if len(ret) == 0 { + panic("no return value specified for GetDestChainConfig") + } + + var r0 price_registry.PriceRegistryDestChainConfig + var r1 error + if rf, ok := ret.Get(0).(func(*bind.CallOpts, uint64) (price_registry.PriceRegistryDestChainConfig, error)); ok { + return rf(opts, destChainSelector) + } + if rf, ok := ret.Get(0).(func(*bind.CallOpts, uint64) price_registry.PriceRegistryDestChainConfig); ok { + r0 = rf(opts, destChainSelector) + } else { + r0 = ret.Get(0).(price_registry.PriceRegistryDestChainConfig) + } + + if rf, ok := ret.Get(1).(func(*bind.CallOpts, uint64) error); ok { + r1 = rf(opts, destChainSelector) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_GetDestChainConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetDestChainConfig' +type PriceRegistryInterface_GetDestChainConfig_Call struct { + *mock.Call +} + +// GetDestChainConfig is a helper method to define mock.On call +// - opts *bind.CallOpts +// - destChainSelector uint64 +func (_e *PriceRegistryInterface_Expecter) GetDestChainConfig(opts interface{}, destChainSelector interface{}) *PriceRegistryInterface_GetDestChainConfig_Call { + return &PriceRegistryInterface_GetDestChainConfig_Call{Call: _e.mock.On("GetDestChainConfig", opts, destChainSelector)} +} + +func (_c *PriceRegistryInterface_GetDestChainConfig_Call) Run(run func(opts *bind.CallOpts, destChainSelector uint64)) *PriceRegistryInterface_GetDestChainConfig_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.CallOpts), args[1].(uint64)) + }) + return _c +} + +func (_c *PriceRegistryInterface_GetDestChainConfig_Call) Return(_a0 price_registry.PriceRegistryDestChainConfig, _a1 error) *PriceRegistryInterface_GetDestChainConfig_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_GetDestChainConfig_Call) RunAndReturn(run func(*bind.CallOpts, uint64) (price_registry.PriceRegistryDestChainConfig, error)) *PriceRegistryInterface_GetDestChainConfig_Call { + _c.Call.Return(run) + return _c +} + +// GetDestinationChainGasPrice provides a mock function with given fields: opts, destChainSelector +func (_m *PriceRegistryInterface) GetDestinationChainGasPrice(opts *bind.CallOpts, destChainSelector uint64) (price_registry.InternalTimestampedPackedUint224, error) { + ret := _m.Called(opts, destChainSelector) + + if len(ret) == 0 { + panic("no return value specified for GetDestinationChainGasPrice") + } + + var r0 price_registry.InternalTimestampedPackedUint224 + var r1 error + if rf, ok := ret.Get(0).(func(*bind.CallOpts, uint64) (price_registry.InternalTimestampedPackedUint224, error)); ok { + return rf(opts, destChainSelector) + } + if rf, ok := ret.Get(0).(func(*bind.CallOpts, uint64) price_registry.InternalTimestampedPackedUint224); ok { + r0 = rf(opts, destChainSelector) + } else { + r0 = ret.Get(0).(price_registry.InternalTimestampedPackedUint224) + } + + if rf, ok := ret.Get(1).(func(*bind.CallOpts, uint64) error); ok { + r1 = rf(opts, destChainSelector) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_GetDestinationChainGasPrice_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetDestinationChainGasPrice' +type PriceRegistryInterface_GetDestinationChainGasPrice_Call struct { + *mock.Call +} + +// GetDestinationChainGasPrice is a helper method to define mock.On call +// - opts *bind.CallOpts +// - destChainSelector uint64 +func (_e *PriceRegistryInterface_Expecter) GetDestinationChainGasPrice(opts interface{}, destChainSelector interface{}) *PriceRegistryInterface_GetDestinationChainGasPrice_Call { + return &PriceRegistryInterface_GetDestinationChainGasPrice_Call{Call: _e.mock.On("GetDestinationChainGasPrice", opts, destChainSelector)} +} + +func (_c *PriceRegistryInterface_GetDestinationChainGasPrice_Call) Run(run func(opts *bind.CallOpts, destChainSelector uint64)) *PriceRegistryInterface_GetDestinationChainGasPrice_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.CallOpts), args[1].(uint64)) + }) + return _c +} + +func (_c *PriceRegistryInterface_GetDestinationChainGasPrice_Call) Return(_a0 price_registry.InternalTimestampedPackedUint224, _a1 error) *PriceRegistryInterface_GetDestinationChainGasPrice_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_GetDestinationChainGasPrice_Call) RunAndReturn(run func(*bind.CallOpts, uint64) (price_registry.InternalTimestampedPackedUint224, error)) *PriceRegistryInterface_GetDestinationChainGasPrice_Call { + _c.Call.Return(run) + return _c +} + +// GetFeeTokens provides a mock function with given fields: opts +func (_m *PriceRegistryInterface) GetFeeTokens(opts *bind.CallOpts) ([]common.Address, error) { + ret := _m.Called(opts) + + if len(ret) == 0 { + panic("no return value specified for GetFeeTokens") + } + + var r0 []common.Address + var r1 error + if rf, ok := ret.Get(0).(func(*bind.CallOpts) ([]common.Address, error)); ok { + return rf(opts) + } + if rf, ok := ret.Get(0).(func(*bind.CallOpts) []common.Address); ok { + r0 = rf(opts) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]common.Address) + } + } + + if rf, ok := ret.Get(1).(func(*bind.CallOpts) error); ok { + r1 = rf(opts) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_GetFeeTokens_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetFeeTokens' +type PriceRegistryInterface_GetFeeTokens_Call struct { + *mock.Call +} + +// GetFeeTokens is a helper method to define mock.On call +// - opts *bind.CallOpts +func (_e *PriceRegistryInterface_Expecter) GetFeeTokens(opts interface{}) *PriceRegistryInterface_GetFeeTokens_Call { + return &PriceRegistryInterface_GetFeeTokens_Call{Call: _e.mock.On("GetFeeTokens", opts)} +} + +func (_c *PriceRegistryInterface_GetFeeTokens_Call) Run(run func(opts *bind.CallOpts)) *PriceRegistryInterface_GetFeeTokens_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.CallOpts)) + }) + return _c +} + +func (_c *PriceRegistryInterface_GetFeeTokens_Call) Return(_a0 []common.Address, _a1 error) *PriceRegistryInterface_GetFeeTokens_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_GetFeeTokens_Call) RunAndReturn(run func(*bind.CallOpts) ([]common.Address, error)) *PriceRegistryInterface_GetFeeTokens_Call { + _c.Call.Return(run) + return _c +} + +// GetPremiumMultiplierWeiPerEth provides a mock function with given fields: opts, token +func (_m *PriceRegistryInterface) GetPremiumMultiplierWeiPerEth(opts *bind.CallOpts, token common.Address) (uint64, error) { + ret := _m.Called(opts, token) + + if len(ret) == 0 { + panic("no return value specified for GetPremiumMultiplierWeiPerEth") + } + + var r0 uint64 + var r1 error + if rf, ok := ret.Get(0).(func(*bind.CallOpts, common.Address) (uint64, error)); ok { + return rf(opts, token) + } + if rf, ok := ret.Get(0).(func(*bind.CallOpts, common.Address) uint64); ok { + r0 = rf(opts, token) + } else { + r0 = ret.Get(0).(uint64) + } + + if rf, ok := ret.Get(1).(func(*bind.CallOpts, common.Address) error); ok { + r1 = rf(opts, token) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_GetPremiumMultiplierWeiPerEth_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPremiumMultiplierWeiPerEth' +type PriceRegistryInterface_GetPremiumMultiplierWeiPerEth_Call struct { + *mock.Call +} + +// GetPremiumMultiplierWeiPerEth is a helper method to define mock.On call +// - opts *bind.CallOpts +// - token common.Address +func (_e *PriceRegistryInterface_Expecter) GetPremiumMultiplierWeiPerEth(opts interface{}, token interface{}) *PriceRegistryInterface_GetPremiumMultiplierWeiPerEth_Call { + return &PriceRegistryInterface_GetPremiumMultiplierWeiPerEth_Call{Call: _e.mock.On("GetPremiumMultiplierWeiPerEth", opts, token)} +} + +func (_c *PriceRegistryInterface_GetPremiumMultiplierWeiPerEth_Call) Run(run func(opts *bind.CallOpts, token common.Address)) *PriceRegistryInterface_GetPremiumMultiplierWeiPerEth_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.CallOpts), args[1].(common.Address)) + }) + return _c +} + +func (_c *PriceRegistryInterface_GetPremiumMultiplierWeiPerEth_Call) Return(_a0 uint64, _a1 error) *PriceRegistryInterface_GetPremiumMultiplierWeiPerEth_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_GetPremiumMultiplierWeiPerEth_Call) RunAndReturn(run func(*bind.CallOpts, common.Address) (uint64, error)) *PriceRegistryInterface_GetPremiumMultiplierWeiPerEth_Call { + _c.Call.Return(run) + return _c +} + +// GetStaticConfig provides a mock function with given fields: opts +func (_m *PriceRegistryInterface) GetStaticConfig(opts *bind.CallOpts) (price_registry.PriceRegistryStaticConfig, error) { + ret := _m.Called(opts) + + if len(ret) == 0 { + panic("no return value specified for GetStaticConfig") + } + + var r0 price_registry.PriceRegistryStaticConfig + var r1 error + if rf, ok := ret.Get(0).(func(*bind.CallOpts) (price_registry.PriceRegistryStaticConfig, error)); ok { + return rf(opts) + } + if rf, ok := ret.Get(0).(func(*bind.CallOpts) price_registry.PriceRegistryStaticConfig); ok { + r0 = rf(opts) + } else { + r0 = ret.Get(0).(price_registry.PriceRegistryStaticConfig) + } + + if rf, ok := ret.Get(1).(func(*bind.CallOpts) error); ok { + r1 = rf(opts) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_GetStaticConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetStaticConfig' +type PriceRegistryInterface_GetStaticConfig_Call struct { + *mock.Call +} + +// GetStaticConfig is a helper method to define mock.On call +// - opts *bind.CallOpts +func (_e *PriceRegistryInterface_Expecter) GetStaticConfig(opts interface{}) *PriceRegistryInterface_GetStaticConfig_Call { + return &PriceRegistryInterface_GetStaticConfig_Call{Call: _e.mock.On("GetStaticConfig", opts)} +} + +func (_c *PriceRegistryInterface_GetStaticConfig_Call) Run(run func(opts *bind.CallOpts)) *PriceRegistryInterface_GetStaticConfig_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.CallOpts)) + }) + return _c +} + +func (_c *PriceRegistryInterface_GetStaticConfig_Call) Return(_a0 price_registry.PriceRegistryStaticConfig, _a1 error) *PriceRegistryInterface_GetStaticConfig_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_GetStaticConfig_Call) RunAndReturn(run func(*bind.CallOpts) (price_registry.PriceRegistryStaticConfig, error)) *PriceRegistryInterface_GetStaticConfig_Call { + _c.Call.Return(run) + return _c +} + +// GetTokenAndGasPrices provides a mock function with given fields: opts, token, destChainSelector +func (_m *PriceRegistryInterface) GetTokenAndGasPrices(opts *bind.CallOpts, token common.Address, destChainSelector uint64) (price_registry.GetTokenAndGasPrices, error) { + ret := _m.Called(opts, token, destChainSelector) + + if len(ret) == 0 { + panic("no return value specified for GetTokenAndGasPrices") + } + + var r0 price_registry.GetTokenAndGasPrices + var r1 error + if rf, ok := ret.Get(0).(func(*bind.CallOpts, common.Address, uint64) (price_registry.GetTokenAndGasPrices, error)); ok { + return rf(opts, token, destChainSelector) + } + if rf, ok := ret.Get(0).(func(*bind.CallOpts, common.Address, uint64) price_registry.GetTokenAndGasPrices); ok { + r0 = rf(opts, token, destChainSelector) + } else { + r0 = ret.Get(0).(price_registry.GetTokenAndGasPrices) + } + + if rf, ok := ret.Get(1).(func(*bind.CallOpts, common.Address, uint64) error); ok { + r1 = rf(opts, token, destChainSelector) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_GetTokenAndGasPrices_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTokenAndGasPrices' +type PriceRegistryInterface_GetTokenAndGasPrices_Call struct { + *mock.Call +} + +// GetTokenAndGasPrices is a helper method to define mock.On call +// - opts *bind.CallOpts +// - token common.Address +// - destChainSelector uint64 +func (_e *PriceRegistryInterface_Expecter) GetTokenAndGasPrices(opts interface{}, token interface{}, destChainSelector interface{}) *PriceRegistryInterface_GetTokenAndGasPrices_Call { + return &PriceRegistryInterface_GetTokenAndGasPrices_Call{Call: _e.mock.On("GetTokenAndGasPrices", opts, token, destChainSelector)} +} + +func (_c *PriceRegistryInterface_GetTokenAndGasPrices_Call) Run(run func(opts *bind.CallOpts, token common.Address, destChainSelector uint64)) *PriceRegistryInterface_GetTokenAndGasPrices_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.CallOpts), args[1].(common.Address), args[2].(uint64)) + }) + return _c +} + +func (_c *PriceRegistryInterface_GetTokenAndGasPrices_Call) Return(_a0 price_registry.GetTokenAndGasPrices, _a1 error) *PriceRegistryInterface_GetTokenAndGasPrices_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_GetTokenAndGasPrices_Call) RunAndReturn(run func(*bind.CallOpts, common.Address, uint64) (price_registry.GetTokenAndGasPrices, error)) *PriceRegistryInterface_GetTokenAndGasPrices_Call { + _c.Call.Return(run) + return _c +} + +// GetTokenPrice provides a mock function with given fields: opts, token +func (_m *PriceRegistryInterface) GetTokenPrice(opts *bind.CallOpts, token common.Address) (price_registry.InternalTimestampedPackedUint224, error) { + ret := _m.Called(opts, token) + + if len(ret) == 0 { + panic("no return value specified for GetTokenPrice") + } + + var r0 price_registry.InternalTimestampedPackedUint224 + var r1 error + if rf, ok := ret.Get(0).(func(*bind.CallOpts, common.Address) (price_registry.InternalTimestampedPackedUint224, error)); ok { + return rf(opts, token) + } + if rf, ok := ret.Get(0).(func(*bind.CallOpts, common.Address) price_registry.InternalTimestampedPackedUint224); ok { + r0 = rf(opts, token) + } else { + r0 = ret.Get(0).(price_registry.InternalTimestampedPackedUint224) + } + + if rf, ok := ret.Get(1).(func(*bind.CallOpts, common.Address) error); ok { + r1 = rf(opts, token) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_GetTokenPrice_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTokenPrice' +type PriceRegistryInterface_GetTokenPrice_Call struct { + *mock.Call +} + +// GetTokenPrice is a helper method to define mock.On call +// - opts *bind.CallOpts +// - token common.Address +func (_e *PriceRegistryInterface_Expecter) GetTokenPrice(opts interface{}, token interface{}) *PriceRegistryInterface_GetTokenPrice_Call { + return &PriceRegistryInterface_GetTokenPrice_Call{Call: _e.mock.On("GetTokenPrice", opts, token)} +} + +func (_c *PriceRegistryInterface_GetTokenPrice_Call) Run(run func(opts *bind.CallOpts, token common.Address)) *PriceRegistryInterface_GetTokenPrice_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.CallOpts), args[1].(common.Address)) + }) + return _c +} + +func (_c *PriceRegistryInterface_GetTokenPrice_Call) Return(_a0 price_registry.InternalTimestampedPackedUint224, _a1 error) *PriceRegistryInterface_GetTokenPrice_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_GetTokenPrice_Call) RunAndReturn(run func(*bind.CallOpts, common.Address) (price_registry.InternalTimestampedPackedUint224, error)) *PriceRegistryInterface_GetTokenPrice_Call { + _c.Call.Return(run) + return _c +} + +// GetTokenPriceFeedConfig provides a mock function with given fields: opts, token +func (_m *PriceRegistryInterface) GetTokenPriceFeedConfig(opts *bind.CallOpts, token common.Address) (price_registry.IPriceRegistryTokenPriceFeedConfig, error) { + ret := _m.Called(opts, token) + + if len(ret) == 0 { + panic("no return value specified for GetTokenPriceFeedConfig") + } + + var r0 price_registry.IPriceRegistryTokenPriceFeedConfig + var r1 error + if rf, ok := ret.Get(0).(func(*bind.CallOpts, common.Address) (price_registry.IPriceRegistryTokenPriceFeedConfig, error)); ok { + return rf(opts, token) + } + if rf, ok := ret.Get(0).(func(*bind.CallOpts, common.Address) price_registry.IPriceRegistryTokenPriceFeedConfig); ok { + r0 = rf(opts, token) + } else { + r0 = ret.Get(0).(price_registry.IPriceRegistryTokenPriceFeedConfig) + } + + if rf, ok := ret.Get(1).(func(*bind.CallOpts, common.Address) error); ok { + r1 = rf(opts, token) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_GetTokenPriceFeedConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTokenPriceFeedConfig' +type PriceRegistryInterface_GetTokenPriceFeedConfig_Call struct { + *mock.Call +} + +// GetTokenPriceFeedConfig is a helper method to define mock.On call +// - opts *bind.CallOpts +// - token common.Address +func (_e *PriceRegistryInterface_Expecter) GetTokenPriceFeedConfig(opts interface{}, token interface{}) *PriceRegistryInterface_GetTokenPriceFeedConfig_Call { + return &PriceRegistryInterface_GetTokenPriceFeedConfig_Call{Call: _e.mock.On("GetTokenPriceFeedConfig", opts, token)} +} + +func (_c *PriceRegistryInterface_GetTokenPriceFeedConfig_Call) Run(run func(opts *bind.CallOpts, token common.Address)) *PriceRegistryInterface_GetTokenPriceFeedConfig_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.CallOpts), args[1].(common.Address)) + }) + return _c +} + +func (_c *PriceRegistryInterface_GetTokenPriceFeedConfig_Call) Return(_a0 price_registry.IPriceRegistryTokenPriceFeedConfig, _a1 error) *PriceRegistryInterface_GetTokenPriceFeedConfig_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_GetTokenPriceFeedConfig_Call) RunAndReturn(run func(*bind.CallOpts, common.Address) (price_registry.IPriceRegistryTokenPriceFeedConfig, error)) *PriceRegistryInterface_GetTokenPriceFeedConfig_Call { + _c.Call.Return(run) + return _c +} + +// GetTokenPrices provides a mock function with given fields: opts, tokens +func (_m *PriceRegistryInterface) GetTokenPrices(opts *bind.CallOpts, tokens []common.Address) ([]price_registry.InternalTimestampedPackedUint224, error) { + ret := _m.Called(opts, tokens) + + if len(ret) == 0 { + panic("no return value specified for GetTokenPrices") + } + + var r0 []price_registry.InternalTimestampedPackedUint224 + var r1 error + if rf, ok := ret.Get(0).(func(*bind.CallOpts, []common.Address) ([]price_registry.InternalTimestampedPackedUint224, error)); ok { + return rf(opts, tokens) + } + if rf, ok := ret.Get(0).(func(*bind.CallOpts, []common.Address) []price_registry.InternalTimestampedPackedUint224); ok { + r0 = rf(opts, tokens) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]price_registry.InternalTimestampedPackedUint224) + } + } + + if rf, ok := ret.Get(1).(func(*bind.CallOpts, []common.Address) error); ok { + r1 = rf(opts, tokens) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_GetTokenPrices_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTokenPrices' +type PriceRegistryInterface_GetTokenPrices_Call struct { + *mock.Call +} + +// GetTokenPrices is a helper method to define mock.On call +// - opts *bind.CallOpts +// - tokens []common.Address +func (_e *PriceRegistryInterface_Expecter) GetTokenPrices(opts interface{}, tokens interface{}) *PriceRegistryInterface_GetTokenPrices_Call { + return &PriceRegistryInterface_GetTokenPrices_Call{Call: _e.mock.On("GetTokenPrices", opts, tokens)} +} + +func (_c *PriceRegistryInterface_GetTokenPrices_Call) Run(run func(opts *bind.CallOpts, tokens []common.Address)) *PriceRegistryInterface_GetTokenPrices_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.CallOpts), args[1].([]common.Address)) + }) + return _c +} + +func (_c *PriceRegistryInterface_GetTokenPrices_Call) Return(_a0 []price_registry.InternalTimestampedPackedUint224, _a1 error) *PriceRegistryInterface_GetTokenPrices_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_GetTokenPrices_Call) RunAndReturn(run func(*bind.CallOpts, []common.Address) ([]price_registry.InternalTimestampedPackedUint224, error)) *PriceRegistryInterface_GetTokenPrices_Call { + _c.Call.Return(run) + return _c +} + +// GetTokenTransferFeeConfig provides a mock function with given fields: opts, destChainSelector, token +func (_m *PriceRegistryInterface) GetTokenTransferFeeConfig(opts *bind.CallOpts, destChainSelector uint64, token common.Address) (price_registry.PriceRegistryTokenTransferFeeConfig, error) { + ret := _m.Called(opts, destChainSelector, token) + + if len(ret) == 0 { + panic("no return value specified for GetTokenTransferFeeConfig") + } + + var r0 price_registry.PriceRegistryTokenTransferFeeConfig + var r1 error + if rf, ok := ret.Get(0).(func(*bind.CallOpts, uint64, common.Address) (price_registry.PriceRegistryTokenTransferFeeConfig, error)); ok { + return rf(opts, destChainSelector, token) + } + if rf, ok := ret.Get(0).(func(*bind.CallOpts, uint64, common.Address) price_registry.PriceRegistryTokenTransferFeeConfig); ok { + r0 = rf(opts, destChainSelector, token) + } else { + r0 = ret.Get(0).(price_registry.PriceRegistryTokenTransferFeeConfig) + } + + if rf, ok := ret.Get(1).(func(*bind.CallOpts, uint64, common.Address) error); ok { + r1 = rf(opts, destChainSelector, token) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_GetTokenTransferFeeConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTokenTransferFeeConfig' +type PriceRegistryInterface_GetTokenTransferFeeConfig_Call struct { + *mock.Call +} + +// GetTokenTransferFeeConfig is a helper method to define mock.On call +// - opts *bind.CallOpts +// - destChainSelector uint64 +// - token common.Address +func (_e *PriceRegistryInterface_Expecter) GetTokenTransferFeeConfig(opts interface{}, destChainSelector interface{}, token interface{}) *PriceRegistryInterface_GetTokenTransferFeeConfig_Call { + return &PriceRegistryInterface_GetTokenTransferFeeConfig_Call{Call: _e.mock.On("GetTokenTransferFeeConfig", opts, destChainSelector, token)} +} + +func (_c *PriceRegistryInterface_GetTokenTransferFeeConfig_Call) Run(run func(opts *bind.CallOpts, destChainSelector uint64, token common.Address)) *PriceRegistryInterface_GetTokenTransferFeeConfig_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.CallOpts), args[1].(uint64), args[2].(common.Address)) + }) + return _c +} + +func (_c *PriceRegistryInterface_GetTokenTransferFeeConfig_Call) Return(_a0 price_registry.PriceRegistryTokenTransferFeeConfig, _a1 error) *PriceRegistryInterface_GetTokenTransferFeeConfig_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_GetTokenTransferFeeConfig_Call) RunAndReturn(run func(*bind.CallOpts, uint64, common.Address) (price_registry.PriceRegistryTokenTransferFeeConfig, error)) *PriceRegistryInterface_GetTokenTransferFeeConfig_Call { + _c.Call.Return(run) + return _c +} + +// GetValidatedFee provides a mock function with given fields: opts, destChainSelector, message +func (_m *PriceRegistryInterface) GetValidatedFee(opts *bind.CallOpts, destChainSelector uint64, message price_registry.ClientEVM2AnyMessage) (*big.Int, error) { + ret := _m.Called(opts, destChainSelector, message) + + if len(ret) == 0 { + panic("no return value specified for GetValidatedFee") + } + + var r0 *big.Int + var r1 error + if rf, ok := ret.Get(0).(func(*bind.CallOpts, uint64, price_registry.ClientEVM2AnyMessage) (*big.Int, error)); ok { + return rf(opts, destChainSelector, message) + } + if rf, ok := ret.Get(0).(func(*bind.CallOpts, uint64, price_registry.ClientEVM2AnyMessage) *big.Int); ok { + r0 = rf(opts, destChainSelector, message) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + } + + if rf, ok := ret.Get(1).(func(*bind.CallOpts, uint64, price_registry.ClientEVM2AnyMessage) error); ok { + r1 = rf(opts, destChainSelector, message) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_GetValidatedFee_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetValidatedFee' +type PriceRegistryInterface_GetValidatedFee_Call struct { + *mock.Call +} + +// GetValidatedFee is a helper method to define mock.On call +// - opts *bind.CallOpts +// - destChainSelector uint64 +// - message price_registry.ClientEVM2AnyMessage +func (_e *PriceRegistryInterface_Expecter) GetValidatedFee(opts interface{}, destChainSelector interface{}, message interface{}) *PriceRegistryInterface_GetValidatedFee_Call { + return &PriceRegistryInterface_GetValidatedFee_Call{Call: _e.mock.On("GetValidatedFee", opts, destChainSelector, message)} +} + +func (_c *PriceRegistryInterface_GetValidatedFee_Call) Run(run func(opts *bind.CallOpts, destChainSelector uint64, message price_registry.ClientEVM2AnyMessage)) *PriceRegistryInterface_GetValidatedFee_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.CallOpts), args[1].(uint64), args[2].(price_registry.ClientEVM2AnyMessage)) + }) + return _c +} + +func (_c *PriceRegistryInterface_GetValidatedFee_Call) Return(_a0 *big.Int, _a1 error) *PriceRegistryInterface_GetValidatedFee_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_GetValidatedFee_Call) RunAndReturn(run func(*bind.CallOpts, uint64, price_registry.ClientEVM2AnyMessage) (*big.Int, error)) *PriceRegistryInterface_GetValidatedFee_Call { + _c.Call.Return(run) + return _c +} + +// GetValidatedTokenPrice provides a mock function with given fields: opts, token +func (_m *PriceRegistryInterface) GetValidatedTokenPrice(opts *bind.CallOpts, token common.Address) (*big.Int, error) { + ret := _m.Called(opts, token) + + if len(ret) == 0 { + panic("no return value specified for GetValidatedTokenPrice") + } + + var r0 *big.Int + var r1 error + if rf, ok := ret.Get(0).(func(*bind.CallOpts, common.Address) (*big.Int, error)); ok { + return rf(opts, token) + } + if rf, ok := ret.Get(0).(func(*bind.CallOpts, common.Address) *big.Int); ok { + r0 = rf(opts, token) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + } + + if rf, ok := ret.Get(1).(func(*bind.CallOpts, common.Address) error); ok { + r1 = rf(opts, token) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_GetValidatedTokenPrice_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetValidatedTokenPrice' +type PriceRegistryInterface_GetValidatedTokenPrice_Call struct { + *mock.Call +} + +// GetValidatedTokenPrice is a helper method to define mock.On call +// - opts *bind.CallOpts +// - token common.Address +func (_e *PriceRegistryInterface_Expecter) GetValidatedTokenPrice(opts interface{}, token interface{}) *PriceRegistryInterface_GetValidatedTokenPrice_Call { + return &PriceRegistryInterface_GetValidatedTokenPrice_Call{Call: _e.mock.On("GetValidatedTokenPrice", opts, token)} +} + +func (_c *PriceRegistryInterface_GetValidatedTokenPrice_Call) Run(run func(opts *bind.CallOpts, token common.Address)) *PriceRegistryInterface_GetValidatedTokenPrice_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.CallOpts), args[1].(common.Address)) + }) + return _c +} + +func (_c *PriceRegistryInterface_GetValidatedTokenPrice_Call) Return(_a0 *big.Int, _a1 error) *PriceRegistryInterface_GetValidatedTokenPrice_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_GetValidatedTokenPrice_Call) RunAndReturn(run func(*bind.CallOpts, common.Address) (*big.Int, error)) *PriceRegistryInterface_GetValidatedTokenPrice_Call { + _c.Call.Return(run) + return _c +} + +// Owner provides a mock function with given fields: opts +func (_m *PriceRegistryInterface) Owner(opts *bind.CallOpts) (common.Address, error) { + ret := _m.Called(opts) + + if len(ret) == 0 { + panic("no return value specified for Owner") + } + + var r0 common.Address + var r1 error + if rf, ok := ret.Get(0).(func(*bind.CallOpts) (common.Address, error)); ok { + return rf(opts) + } + if rf, ok := ret.Get(0).(func(*bind.CallOpts) common.Address); ok { + r0 = rf(opts) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(common.Address) + } + } + + if rf, ok := ret.Get(1).(func(*bind.CallOpts) error); ok { + r1 = rf(opts) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_Owner_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Owner' +type PriceRegistryInterface_Owner_Call struct { + *mock.Call +} + +// Owner is a helper method to define mock.On call +// - opts *bind.CallOpts +func (_e *PriceRegistryInterface_Expecter) Owner(opts interface{}) *PriceRegistryInterface_Owner_Call { + return &PriceRegistryInterface_Owner_Call{Call: _e.mock.On("Owner", opts)} +} + +func (_c *PriceRegistryInterface_Owner_Call) Run(run func(opts *bind.CallOpts)) *PriceRegistryInterface_Owner_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.CallOpts)) + }) + return _c +} + +func (_c *PriceRegistryInterface_Owner_Call) Return(_a0 common.Address, _a1 error) *PriceRegistryInterface_Owner_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_Owner_Call) RunAndReturn(run func(*bind.CallOpts) (common.Address, error)) *PriceRegistryInterface_Owner_Call { + _c.Call.Return(run) + return _c +} + +// ParseAuthorizedCallerAdded provides a mock function with given fields: log +func (_m *PriceRegistryInterface) ParseAuthorizedCallerAdded(log types.Log) (*price_registry.PriceRegistryAuthorizedCallerAdded, error) { + ret := _m.Called(log) + + if len(ret) == 0 { + panic("no return value specified for ParseAuthorizedCallerAdded") + } + + var r0 *price_registry.PriceRegistryAuthorizedCallerAdded + var r1 error + if rf, ok := ret.Get(0).(func(types.Log) (*price_registry.PriceRegistryAuthorizedCallerAdded, error)); ok { + return rf(log) + } + if rf, ok := ret.Get(0).(func(types.Log) *price_registry.PriceRegistryAuthorizedCallerAdded); ok { + r0 = rf(log) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryAuthorizedCallerAdded) + } + } + + if rf, ok := ret.Get(1).(func(types.Log) error); ok { + r1 = rf(log) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_ParseAuthorizedCallerAdded_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ParseAuthorizedCallerAdded' +type PriceRegistryInterface_ParseAuthorizedCallerAdded_Call struct { + *mock.Call +} + +// ParseAuthorizedCallerAdded is a helper method to define mock.On call +// - log types.Log +func (_e *PriceRegistryInterface_Expecter) ParseAuthorizedCallerAdded(log interface{}) *PriceRegistryInterface_ParseAuthorizedCallerAdded_Call { + return &PriceRegistryInterface_ParseAuthorizedCallerAdded_Call{Call: _e.mock.On("ParseAuthorizedCallerAdded", log)} +} + +func (_c *PriceRegistryInterface_ParseAuthorizedCallerAdded_Call) Run(run func(log types.Log)) *PriceRegistryInterface_ParseAuthorizedCallerAdded_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(types.Log)) + }) + return _c +} + +func (_c *PriceRegistryInterface_ParseAuthorizedCallerAdded_Call) Return(_a0 *price_registry.PriceRegistryAuthorizedCallerAdded, _a1 error) *PriceRegistryInterface_ParseAuthorizedCallerAdded_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_ParseAuthorizedCallerAdded_Call) RunAndReturn(run func(types.Log) (*price_registry.PriceRegistryAuthorizedCallerAdded, error)) *PriceRegistryInterface_ParseAuthorizedCallerAdded_Call { + _c.Call.Return(run) + return _c +} + +// ParseAuthorizedCallerRemoved provides a mock function with given fields: log +func (_m *PriceRegistryInterface) ParseAuthorizedCallerRemoved(log types.Log) (*price_registry.PriceRegistryAuthorizedCallerRemoved, error) { + ret := _m.Called(log) + + if len(ret) == 0 { + panic("no return value specified for ParseAuthorizedCallerRemoved") + } + + var r0 *price_registry.PriceRegistryAuthorizedCallerRemoved + var r1 error + if rf, ok := ret.Get(0).(func(types.Log) (*price_registry.PriceRegistryAuthorizedCallerRemoved, error)); ok { + return rf(log) + } + if rf, ok := ret.Get(0).(func(types.Log) *price_registry.PriceRegistryAuthorizedCallerRemoved); ok { + r0 = rf(log) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryAuthorizedCallerRemoved) + } + } + + if rf, ok := ret.Get(1).(func(types.Log) error); ok { + r1 = rf(log) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_ParseAuthorizedCallerRemoved_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ParseAuthorizedCallerRemoved' +type PriceRegistryInterface_ParseAuthorizedCallerRemoved_Call struct { + *mock.Call +} + +// ParseAuthorizedCallerRemoved is a helper method to define mock.On call +// - log types.Log +func (_e *PriceRegistryInterface_Expecter) ParseAuthorizedCallerRemoved(log interface{}) *PriceRegistryInterface_ParseAuthorizedCallerRemoved_Call { + return &PriceRegistryInterface_ParseAuthorizedCallerRemoved_Call{Call: _e.mock.On("ParseAuthorizedCallerRemoved", log)} +} + +func (_c *PriceRegistryInterface_ParseAuthorizedCallerRemoved_Call) Run(run func(log types.Log)) *PriceRegistryInterface_ParseAuthorizedCallerRemoved_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(types.Log)) + }) + return _c +} + +func (_c *PriceRegistryInterface_ParseAuthorizedCallerRemoved_Call) Return(_a0 *price_registry.PriceRegistryAuthorizedCallerRemoved, _a1 error) *PriceRegistryInterface_ParseAuthorizedCallerRemoved_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_ParseAuthorizedCallerRemoved_Call) RunAndReturn(run func(types.Log) (*price_registry.PriceRegistryAuthorizedCallerRemoved, error)) *PriceRegistryInterface_ParseAuthorizedCallerRemoved_Call { + _c.Call.Return(run) + return _c +} + +// ParseDestChainAdded provides a mock function with given fields: log +func (_m *PriceRegistryInterface) ParseDestChainAdded(log types.Log) (*price_registry.PriceRegistryDestChainAdded, error) { + ret := _m.Called(log) + + if len(ret) == 0 { + panic("no return value specified for ParseDestChainAdded") + } + + var r0 *price_registry.PriceRegistryDestChainAdded + var r1 error + if rf, ok := ret.Get(0).(func(types.Log) (*price_registry.PriceRegistryDestChainAdded, error)); ok { + return rf(log) + } + if rf, ok := ret.Get(0).(func(types.Log) *price_registry.PriceRegistryDestChainAdded); ok { + r0 = rf(log) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryDestChainAdded) + } + } + + if rf, ok := ret.Get(1).(func(types.Log) error); ok { + r1 = rf(log) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_ParseDestChainAdded_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ParseDestChainAdded' +type PriceRegistryInterface_ParseDestChainAdded_Call struct { + *mock.Call +} + +// ParseDestChainAdded is a helper method to define mock.On call +// - log types.Log +func (_e *PriceRegistryInterface_Expecter) ParseDestChainAdded(log interface{}) *PriceRegistryInterface_ParseDestChainAdded_Call { + return &PriceRegistryInterface_ParseDestChainAdded_Call{Call: _e.mock.On("ParseDestChainAdded", log)} +} + +func (_c *PriceRegistryInterface_ParseDestChainAdded_Call) Run(run func(log types.Log)) *PriceRegistryInterface_ParseDestChainAdded_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(types.Log)) + }) + return _c +} + +func (_c *PriceRegistryInterface_ParseDestChainAdded_Call) Return(_a0 *price_registry.PriceRegistryDestChainAdded, _a1 error) *PriceRegistryInterface_ParseDestChainAdded_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_ParseDestChainAdded_Call) RunAndReturn(run func(types.Log) (*price_registry.PriceRegistryDestChainAdded, error)) *PriceRegistryInterface_ParseDestChainAdded_Call { + _c.Call.Return(run) + return _c +} + +// ParseDestChainConfigUpdated provides a mock function with given fields: log +func (_m *PriceRegistryInterface) ParseDestChainConfigUpdated(log types.Log) (*price_registry.PriceRegistryDestChainConfigUpdated, error) { + ret := _m.Called(log) + + if len(ret) == 0 { + panic("no return value specified for ParseDestChainConfigUpdated") + } + + var r0 *price_registry.PriceRegistryDestChainConfigUpdated + var r1 error + if rf, ok := ret.Get(0).(func(types.Log) (*price_registry.PriceRegistryDestChainConfigUpdated, error)); ok { + return rf(log) + } + if rf, ok := ret.Get(0).(func(types.Log) *price_registry.PriceRegistryDestChainConfigUpdated); ok { + r0 = rf(log) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryDestChainConfigUpdated) + } + } + + if rf, ok := ret.Get(1).(func(types.Log) error); ok { + r1 = rf(log) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_ParseDestChainConfigUpdated_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ParseDestChainConfigUpdated' +type PriceRegistryInterface_ParseDestChainConfigUpdated_Call struct { + *mock.Call +} + +// ParseDestChainConfigUpdated is a helper method to define mock.On call +// - log types.Log +func (_e *PriceRegistryInterface_Expecter) ParseDestChainConfigUpdated(log interface{}) *PriceRegistryInterface_ParseDestChainConfigUpdated_Call { + return &PriceRegistryInterface_ParseDestChainConfigUpdated_Call{Call: _e.mock.On("ParseDestChainConfigUpdated", log)} +} + +func (_c *PriceRegistryInterface_ParseDestChainConfigUpdated_Call) Run(run func(log types.Log)) *PriceRegistryInterface_ParseDestChainConfigUpdated_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(types.Log)) + }) + return _c +} + +func (_c *PriceRegistryInterface_ParseDestChainConfigUpdated_Call) Return(_a0 *price_registry.PriceRegistryDestChainConfigUpdated, _a1 error) *PriceRegistryInterface_ParseDestChainConfigUpdated_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_ParseDestChainConfigUpdated_Call) RunAndReturn(run func(types.Log) (*price_registry.PriceRegistryDestChainConfigUpdated, error)) *PriceRegistryInterface_ParseDestChainConfigUpdated_Call { + _c.Call.Return(run) + return _c +} + +// ParseFeeTokenAdded provides a mock function with given fields: log +func (_m *PriceRegistryInterface) ParseFeeTokenAdded(log types.Log) (*price_registry.PriceRegistryFeeTokenAdded, error) { + ret := _m.Called(log) + + if len(ret) == 0 { + panic("no return value specified for ParseFeeTokenAdded") + } + + var r0 *price_registry.PriceRegistryFeeTokenAdded + var r1 error + if rf, ok := ret.Get(0).(func(types.Log) (*price_registry.PriceRegistryFeeTokenAdded, error)); ok { + return rf(log) + } + if rf, ok := ret.Get(0).(func(types.Log) *price_registry.PriceRegistryFeeTokenAdded); ok { + r0 = rf(log) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryFeeTokenAdded) + } + } + + if rf, ok := ret.Get(1).(func(types.Log) error); ok { + r1 = rf(log) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_ParseFeeTokenAdded_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ParseFeeTokenAdded' +type PriceRegistryInterface_ParseFeeTokenAdded_Call struct { + *mock.Call +} + +// ParseFeeTokenAdded is a helper method to define mock.On call +// - log types.Log +func (_e *PriceRegistryInterface_Expecter) ParseFeeTokenAdded(log interface{}) *PriceRegistryInterface_ParseFeeTokenAdded_Call { + return &PriceRegistryInterface_ParseFeeTokenAdded_Call{Call: _e.mock.On("ParseFeeTokenAdded", log)} +} + +func (_c *PriceRegistryInterface_ParseFeeTokenAdded_Call) Run(run func(log types.Log)) *PriceRegistryInterface_ParseFeeTokenAdded_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(types.Log)) + }) + return _c +} + +func (_c *PriceRegistryInterface_ParseFeeTokenAdded_Call) Return(_a0 *price_registry.PriceRegistryFeeTokenAdded, _a1 error) *PriceRegistryInterface_ParseFeeTokenAdded_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_ParseFeeTokenAdded_Call) RunAndReturn(run func(types.Log) (*price_registry.PriceRegistryFeeTokenAdded, error)) *PriceRegistryInterface_ParseFeeTokenAdded_Call { + _c.Call.Return(run) + return _c +} + +// ParseFeeTokenRemoved provides a mock function with given fields: log +func (_m *PriceRegistryInterface) ParseFeeTokenRemoved(log types.Log) (*price_registry.PriceRegistryFeeTokenRemoved, error) { + ret := _m.Called(log) + + if len(ret) == 0 { + panic("no return value specified for ParseFeeTokenRemoved") + } + + var r0 *price_registry.PriceRegistryFeeTokenRemoved + var r1 error + if rf, ok := ret.Get(0).(func(types.Log) (*price_registry.PriceRegistryFeeTokenRemoved, error)); ok { + return rf(log) + } + if rf, ok := ret.Get(0).(func(types.Log) *price_registry.PriceRegistryFeeTokenRemoved); ok { + r0 = rf(log) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryFeeTokenRemoved) + } + } + + if rf, ok := ret.Get(1).(func(types.Log) error); ok { + r1 = rf(log) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_ParseFeeTokenRemoved_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ParseFeeTokenRemoved' +type PriceRegistryInterface_ParseFeeTokenRemoved_Call struct { + *mock.Call +} + +// ParseFeeTokenRemoved is a helper method to define mock.On call +// - log types.Log +func (_e *PriceRegistryInterface_Expecter) ParseFeeTokenRemoved(log interface{}) *PriceRegistryInterface_ParseFeeTokenRemoved_Call { + return &PriceRegistryInterface_ParseFeeTokenRemoved_Call{Call: _e.mock.On("ParseFeeTokenRemoved", log)} +} + +func (_c *PriceRegistryInterface_ParseFeeTokenRemoved_Call) Run(run func(log types.Log)) *PriceRegistryInterface_ParseFeeTokenRemoved_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(types.Log)) + }) + return _c +} + +func (_c *PriceRegistryInterface_ParseFeeTokenRemoved_Call) Return(_a0 *price_registry.PriceRegistryFeeTokenRemoved, _a1 error) *PriceRegistryInterface_ParseFeeTokenRemoved_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_ParseFeeTokenRemoved_Call) RunAndReturn(run func(types.Log) (*price_registry.PriceRegistryFeeTokenRemoved, error)) *PriceRegistryInterface_ParseFeeTokenRemoved_Call { + _c.Call.Return(run) + return _c +} + +// ParseLog provides a mock function with given fields: log +func (_m *PriceRegistryInterface) ParseLog(log types.Log) (generated.AbigenLog, error) { + ret := _m.Called(log) + + if len(ret) == 0 { + panic("no return value specified for ParseLog") + } + + var r0 generated.AbigenLog + var r1 error + if rf, ok := ret.Get(0).(func(types.Log) (generated.AbigenLog, error)); ok { + return rf(log) + } + if rf, ok := ret.Get(0).(func(types.Log) generated.AbigenLog); ok { + r0 = rf(log) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(generated.AbigenLog) + } + } + + if rf, ok := ret.Get(1).(func(types.Log) error); ok { + r1 = rf(log) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_ParseLog_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ParseLog' +type PriceRegistryInterface_ParseLog_Call struct { + *mock.Call +} + +// ParseLog is a helper method to define mock.On call +// - log types.Log +func (_e *PriceRegistryInterface_Expecter) ParseLog(log interface{}) *PriceRegistryInterface_ParseLog_Call { + return &PriceRegistryInterface_ParseLog_Call{Call: _e.mock.On("ParseLog", log)} +} + +func (_c *PriceRegistryInterface_ParseLog_Call) Run(run func(log types.Log)) *PriceRegistryInterface_ParseLog_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(types.Log)) + }) + return _c +} + +func (_c *PriceRegistryInterface_ParseLog_Call) Return(_a0 generated.AbigenLog, _a1 error) *PriceRegistryInterface_ParseLog_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_ParseLog_Call) RunAndReturn(run func(types.Log) (generated.AbigenLog, error)) *PriceRegistryInterface_ParseLog_Call { + _c.Call.Return(run) + return _c +} + +// ParseOwnershipTransferRequested provides a mock function with given fields: log +func (_m *PriceRegistryInterface) ParseOwnershipTransferRequested(log types.Log) (*price_registry.PriceRegistryOwnershipTransferRequested, error) { + ret := _m.Called(log) + + if len(ret) == 0 { + panic("no return value specified for ParseOwnershipTransferRequested") + } + + var r0 *price_registry.PriceRegistryOwnershipTransferRequested + var r1 error + if rf, ok := ret.Get(0).(func(types.Log) (*price_registry.PriceRegistryOwnershipTransferRequested, error)); ok { + return rf(log) + } + if rf, ok := ret.Get(0).(func(types.Log) *price_registry.PriceRegistryOwnershipTransferRequested); ok { + r0 = rf(log) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryOwnershipTransferRequested) + } + } + + if rf, ok := ret.Get(1).(func(types.Log) error); ok { + r1 = rf(log) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_ParseOwnershipTransferRequested_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ParseOwnershipTransferRequested' +type PriceRegistryInterface_ParseOwnershipTransferRequested_Call struct { + *mock.Call +} + +// ParseOwnershipTransferRequested is a helper method to define mock.On call +// - log types.Log +func (_e *PriceRegistryInterface_Expecter) ParseOwnershipTransferRequested(log interface{}) *PriceRegistryInterface_ParseOwnershipTransferRequested_Call { + return &PriceRegistryInterface_ParseOwnershipTransferRequested_Call{Call: _e.mock.On("ParseOwnershipTransferRequested", log)} +} + +func (_c *PriceRegistryInterface_ParseOwnershipTransferRequested_Call) Run(run func(log types.Log)) *PriceRegistryInterface_ParseOwnershipTransferRequested_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(types.Log)) + }) + return _c +} + +func (_c *PriceRegistryInterface_ParseOwnershipTransferRequested_Call) Return(_a0 *price_registry.PriceRegistryOwnershipTransferRequested, _a1 error) *PriceRegistryInterface_ParseOwnershipTransferRequested_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_ParseOwnershipTransferRequested_Call) RunAndReturn(run func(types.Log) (*price_registry.PriceRegistryOwnershipTransferRequested, error)) *PriceRegistryInterface_ParseOwnershipTransferRequested_Call { + _c.Call.Return(run) + return _c +} + +// ParseOwnershipTransferred provides a mock function with given fields: log +func (_m *PriceRegistryInterface) ParseOwnershipTransferred(log types.Log) (*price_registry.PriceRegistryOwnershipTransferred, error) { + ret := _m.Called(log) + + if len(ret) == 0 { + panic("no return value specified for ParseOwnershipTransferred") + } + + var r0 *price_registry.PriceRegistryOwnershipTransferred + var r1 error + if rf, ok := ret.Get(0).(func(types.Log) (*price_registry.PriceRegistryOwnershipTransferred, error)); ok { + return rf(log) + } + if rf, ok := ret.Get(0).(func(types.Log) *price_registry.PriceRegistryOwnershipTransferred); ok { + r0 = rf(log) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryOwnershipTransferred) + } + } + + if rf, ok := ret.Get(1).(func(types.Log) error); ok { + r1 = rf(log) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_ParseOwnershipTransferred_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ParseOwnershipTransferred' +type PriceRegistryInterface_ParseOwnershipTransferred_Call struct { + *mock.Call +} + +// ParseOwnershipTransferred is a helper method to define mock.On call +// - log types.Log +func (_e *PriceRegistryInterface_Expecter) ParseOwnershipTransferred(log interface{}) *PriceRegistryInterface_ParseOwnershipTransferred_Call { + return &PriceRegistryInterface_ParseOwnershipTransferred_Call{Call: _e.mock.On("ParseOwnershipTransferred", log)} +} + +func (_c *PriceRegistryInterface_ParseOwnershipTransferred_Call) Run(run func(log types.Log)) *PriceRegistryInterface_ParseOwnershipTransferred_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(types.Log)) + }) + return _c +} + +func (_c *PriceRegistryInterface_ParseOwnershipTransferred_Call) Return(_a0 *price_registry.PriceRegistryOwnershipTransferred, _a1 error) *PriceRegistryInterface_ParseOwnershipTransferred_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_ParseOwnershipTransferred_Call) RunAndReturn(run func(types.Log) (*price_registry.PriceRegistryOwnershipTransferred, error)) *PriceRegistryInterface_ParseOwnershipTransferred_Call { + _c.Call.Return(run) + return _c +} + +// ParsePremiumMultiplierWeiPerEthUpdated provides a mock function with given fields: log +func (_m *PriceRegistryInterface) ParsePremiumMultiplierWeiPerEthUpdated(log types.Log) (*price_registry.PriceRegistryPremiumMultiplierWeiPerEthUpdated, error) { + ret := _m.Called(log) + + if len(ret) == 0 { + panic("no return value specified for ParsePremiumMultiplierWeiPerEthUpdated") + } + + var r0 *price_registry.PriceRegistryPremiumMultiplierWeiPerEthUpdated + var r1 error + if rf, ok := ret.Get(0).(func(types.Log) (*price_registry.PriceRegistryPremiumMultiplierWeiPerEthUpdated, error)); ok { + return rf(log) + } + if rf, ok := ret.Get(0).(func(types.Log) *price_registry.PriceRegistryPremiumMultiplierWeiPerEthUpdated); ok { + r0 = rf(log) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryPremiumMultiplierWeiPerEthUpdated) + } + } + + if rf, ok := ret.Get(1).(func(types.Log) error); ok { + r1 = rf(log) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_ParsePremiumMultiplierWeiPerEthUpdated_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ParsePremiumMultiplierWeiPerEthUpdated' +type PriceRegistryInterface_ParsePremiumMultiplierWeiPerEthUpdated_Call struct { + *mock.Call +} + +// ParsePremiumMultiplierWeiPerEthUpdated is a helper method to define mock.On call +// - log types.Log +func (_e *PriceRegistryInterface_Expecter) ParsePremiumMultiplierWeiPerEthUpdated(log interface{}) *PriceRegistryInterface_ParsePremiumMultiplierWeiPerEthUpdated_Call { + return &PriceRegistryInterface_ParsePremiumMultiplierWeiPerEthUpdated_Call{Call: _e.mock.On("ParsePremiumMultiplierWeiPerEthUpdated", log)} +} + +func (_c *PriceRegistryInterface_ParsePremiumMultiplierWeiPerEthUpdated_Call) Run(run func(log types.Log)) *PriceRegistryInterface_ParsePremiumMultiplierWeiPerEthUpdated_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(types.Log)) + }) + return _c +} + +func (_c *PriceRegistryInterface_ParsePremiumMultiplierWeiPerEthUpdated_Call) Return(_a0 *price_registry.PriceRegistryPremiumMultiplierWeiPerEthUpdated, _a1 error) *PriceRegistryInterface_ParsePremiumMultiplierWeiPerEthUpdated_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_ParsePremiumMultiplierWeiPerEthUpdated_Call) RunAndReturn(run func(types.Log) (*price_registry.PriceRegistryPremiumMultiplierWeiPerEthUpdated, error)) *PriceRegistryInterface_ParsePremiumMultiplierWeiPerEthUpdated_Call { + _c.Call.Return(run) + return _c +} + +// ParsePriceFeedPerTokenUpdated provides a mock function with given fields: log +func (_m *PriceRegistryInterface) ParsePriceFeedPerTokenUpdated(log types.Log) (*price_registry.PriceRegistryPriceFeedPerTokenUpdated, error) { + ret := _m.Called(log) + + if len(ret) == 0 { + panic("no return value specified for ParsePriceFeedPerTokenUpdated") + } + + var r0 *price_registry.PriceRegistryPriceFeedPerTokenUpdated + var r1 error + if rf, ok := ret.Get(0).(func(types.Log) (*price_registry.PriceRegistryPriceFeedPerTokenUpdated, error)); ok { + return rf(log) + } + if rf, ok := ret.Get(0).(func(types.Log) *price_registry.PriceRegistryPriceFeedPerTokenUpdated); ok { + r0 = rf(log) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryPriceFeedPerTokenUpdated) + } + } + + if rf, ok := ret.Get(1).(func(types.Log) error); ok { + r1 = rf(log) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_ParsePriceFeedPerTokenUpdated_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ParsePriceFeedPerTokenUpdated' +type PriceRegistryInterface_ParsePriceFeedPerTokenUpdated_Call struct { + *mock.Call +} + +// ParsePriceFeedPerTokenUpdated is a helper method to define mock.On call +// - log types.Log +func (_e *PriceRegistryInterface_Expecter) ParsePriceFeedPerTokenUpdated(log interface{}) *PriceRegistryInterface_ParsePriceFeedPerTokenUpdated_Call { + return &PriceRegistryInterface_ParsePriceFeedPerTokenUpdated_Call{Call: _e.mock.On("ParsePriceFeedPerTokenUpdated", log)} +} + +func (_c *PriceRegistryInterface_ParsePriceFeedPerTokenUpdated_Call) Run(run func(log types.Log)) *PriceRegistryInterface_ParsePriceFeedPerTokenUpdated_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(types.Log)) + }) + return _c +} + +func (_c *PriceRegistryInterface_ParsePriceFeedPerTokenUpdated_Call) Return(_a0 *price_registry.PriceRegistryPriceFeedPerTokenUpdated, _a1 error) *PriceRegistryInterface_ParsePriceFeedPerTokenUpdated_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_ParsePriceFeedPerTokenUpdated_Call) RunAndReturn(run func(types.Log) (*price_registry.PriceRegistryPriceFeedPerTokenUpdated, error)) *PriceRegistryInterface_ParsePriceFeedPerTokenUpdated_Call { + _c.Call.Return(run) + return _c +} + +// ParseTokenTransferFeeConfigDeleted provides a mock function with given fields: log +func (_m *PriceRegistryInterface) ParseTokenTransferFeeConfigDeleted(log types.Log) (*price_registry.PriceRegistryTokenTransferFeeConfigDeleted, error) { + ret := _m.Called(log) + + if len(ret) == 0 { + panic("no return value specified for ParseTokenTransferFeeConfigDeleted") + } + + var r0 *price_registry.PriceRegistryTokenTransferFeeConfigDeleted + var r1 error + if rf, ok := ret.Get(0).(func(types.Log) (*price_registry.PriceRegistryTokenTransferFeeConfigDeleted, error)); ok { + return rf(log) + } + if rf, ok := ret.Get(0).(func(types.Log) *price_registry.PriceRegistryTokenTransferFeeConfigDeleted); ok { + r0 = rf(log) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryTokenTransferFeeConfigDeleted) + } + } + + if rf, ok := ret.Get(1).(func(types.Log) error); ok { + r1 = rf(log) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_ParseTokenTransferFeeConfigDeleted_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ParseTokenTransferFeeConfigDeleted' +type PriceRegistryInterface_ParseTokenTransferFeeConfigDeleted_Call struct { + *mock.Call +} + +// ParseTokenTransferFeeConfigDeleted is a helper method to define mock.On call +// - log types.Log +func (_e *PriceRegistryInterface_Expecter) ParseTokenTransferFeeConfigDeleted(log interface{}) *PriceRegistryInterface_ParseTokenTransferFeeConfigDeleted_Call { + return &PriceRegistryInterface_ParseTokenTransferFeeConfigDeleted_Call{Call: _e.mock.On("ParseTokenTransferFeeConfigDeleted", log)} +} + +func (_c *PriceRegistryInterface_ParseTokenTransferFeeConfigDeleted_Call) Run(run func(log types.Log)) *PriceRegistryInterface_ParseTokenTransferFeeConfigDeleted_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(types.Log)) + }) + return _c +} + +func (_c *PriceRegistryInterface_ParseTokenTransferFeeConfigDeleted_Call) Return(_a0 *price_registry.PriceRegistryTokenTransferFeeConfigDeleted, _a1 error) *PriceRegistryInterface_ParseTokenTransferFeeConfigDeleted_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_ParseTokenTransferFeeConfigDeleted_Call) RunAndReturn(run func(types.Log) (*price_registry.PriceRegistryTokenTransferFeeConfigDeleted, error)) *PriceRegistryInterface_ParseTokenTransferFeeConfigDeleted_Call { + _c.Call.Return(run) + return _c +} + +// ParseTokenTransferFeeConfigUpdated provides a mock function with given fields: log +func (_m *PriceRegistryInterface) ParseTokenTransferFeeConfigUpdated(log types.Log) (*price_registry.PriceRegistryTokenTransferFeeConfigUpdated, error) { + ret := _m.Called(log) + + if len(ret) == 0 { + panic("no return value specified for ParseTokenTransferFeeConfigUpdated") + } + + var r0 *price_registry.PriceRegistryTokenTransferFeeConfigUpdated + var r1 error + if rf, ok := ret.Get(0).(func(types.Log) (*price_registry.PriceRegistryTokenTransferFeeConfigUpdated, error)); ok { + return rf(log) + } + if rf, ok := ret.Get(0).(func(types.Log) *price_registry.PriceRegistryTokenTransferFeeConfigUpdated); ok { + r0 = rf(log) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryTokenTransferFeeConfigUpdated) + } + } + + if rf, ok := ret.Get(1).(func(types.Log) error); ok { + r1 = rf(log) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_ParseTokenTransferFeeConfigUpdated_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ParseTokenTransferFeeConfigUpdated' +type PriceRegistryInterface_ParseTokenTransferFeeConfigUpdated_Call struct { + *mock.Call +} + +// ParseTokenTransferFeeConfigUpdated is a helper method to define mock.On call +// - log types.Log +func (_e *PriceRegistryInterface_Expecter) ParseTokenTransferFeeConfigUpdated(log interface{}) *PriceRegistryInterface_ParseTokenTransferFeeConfigUpdated_Call { + return &PriceRegistryInterface_ParseTokenTransferFeeConfigUpdated_Call{Call: _e.mock.On("ParseTokenTransferFeeConfigUpdated", log)} +} + +func (_c *PriceRegistryInterface_ParseTokenTransferFeeConfigUpdated_Call) Run(run func(log types.Log)) *PriceRegistryInterface_ParseTokenTransferFeeConfigUpdated_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(types.Log)) + }) + return _c +} + +func (_c *PriceRegistryInterface_ParseTokenTransferFeeConfigUpdated_Call) Return(_a0 *price_registry.PriceRegistryTokenTransferFeeConfigUpdated, _a1 error) *PriceRegistryInterface_ParseTokenTransferFeeConfigUpdated_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_ParseTokenTransferFeeConfigUpdated_Call) RunAndReturn(run func(types.Log) (*price_registry.PriceRegistryTokenTransferFeeConfigUpdated, error)) *PriceRegistryInterface_ParseTokenTransferFeeConfigUpdated_Call { + _c.Call.Return(run) + return _c +} + +// ParseUsdPerTokenUpdated provides a mock function with given fields: log +func (_m *PriceRegistryInterface) ParseUsdPerTokenUpdated(log types.Log) (*price_registry.PriceRegistryUsdPerTokenUpdated, error) { + ret := _m.Called(log) + + if len(ret) == 0 { + panic("no return value specified for ParseUsdPerTokenUpdated") + } + + var r0 *price_registry.PriceRegistryUsdPerTokenUpdated + var r1 error + if rf, ok := ret.Get(0).(func(types.Log) (*price_registry.PriceRegistryUsdPerTokenUpdated, error)); ok { + return rf(log) + } + if rf, ok := ret.Get(0).(func(types.Log) *price_registry.PriceRegistryUsdPerTokenUpdated); ok { + r0 = rf(log) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryUsdPerTokenUpdated) + } + } + + if rf, ok := ret.Get(1).(func(types.Log) error); ok { + r1 = rf(log) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_ParseUsdPerTokenUpdated_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ParseUsdPerTokenUpdated' +type PriceRegistryInterface_ParseUsdPerTokenUpdated_Call struct { + *mock.Call +} + +// ParseUsdPerTokenUpdated is a helper method to define mock.On call +// - log types.Log +func (_e *PriceRegistryInterface_Expecter) ParseUsdPerTokenUpdated(log interface{}) *PriceRegistryInterface_ParseUsdPerTokenUpdated_Call { + return &PriceRegistryInterface_ParseUsdPerTokenUpdated_Call{Call: _e.mock.On("ParseUsdPerTokenUpdated", log)} +} + +func (_c *PriceRegistryInterface_ParseUsdPerTokenUpdated_Call) Run(run func(log types.Log)) *PriceRegistryInterface_ParseUsdPerTokenUpdated_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(types.Log)) + }) + return _c +} + +func (_c *PriceRegistryInterface_ParseUsdPerTokenUpdated_Call) Return(_a0 *price_registry.PriceRegistryUsdPerTokenUpdated, _a1 error) *PriceRegistryInterface_ParseUsdPerTokenUpdated_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_ParseUsdPerTokenUpdated_Call) RunAndReturn(run func(types.Log) (*price_registry.PriceRegistryUsdPerTokenUpdated, error)) *PriceRegistryInterface_ParseUsdPerTokenUpdated_Call { + _c.Call.Return(run) + return _c +} + +// ParseUsdPerUnitGasUpdated provides a mock function with given fields: log +func (_m *PriceRegistryInterface) ParseUsdPerUnitGasUpdated(log types.Log) (*price_registry.PriceRegistryUsdPerUnitGasUpdated, error) { + ret := _m.Called(log) + + if len(ret) == 0 { + panic("no return value specified for ParseUsdPerUnitGasUpdated") + } + + var r0 *price_registry.PriceRegistryUsdPerUnitGasUpdated + var r1 error + if rf, ok := ret.Get(0).(func(types.Log) (*price_registry.PriceRegistryUsdPerUnitGasUpdated, error)); ok { + return rf(log) + } + if rf, ok := ret.Get(0).(func(types.Log) *price_registry.PriceRegistryUsdPerUnitGasUpdated); ok { + r0 = rf(log) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*price_registry.PriceRegistryUsdPerUnitGasUpdated) + } + } + + if rf, ok := ret.Get(1).(func(types.Log) error); ok { + r1 = rf(log) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_ParseUsdPerUnitGasUpdated_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ParseUsdPerUnitGasUpdated' +type PriceRegistryInterface_ParseUsdPerUnitGasUpdated_Call struct { + *mock.Call +} + +// ParseUsdPerUnitGasUpdated is a helper method to define mock.On call +// - log types.Log +func (_e *PriceRegistryInterface_Expecter) ParseUsdPerUnitGasUpdated(log interface{}) *PriceRegistryInterface_ParseUsdPerUnitGasUpdated_Call { + return &PriceRegistryInterface_ParseUsdPerUnitGasUpdated_Call{Call: _e.mock.On("ParseUsdPerUnitGasUpdated", log)} +} + +func (_c *PriceRegistryInterface_ParseUsdPerUnitGasUpdated_Call) Run(run func(log types.Log)) *PriceRegistryInterface_ParseUsdPerUnitGasUpdated_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(types.Log)) + }) + return _c +} + +func (_c *PriceRegistryInterface_ParseUsdPerUnitGasUpdated_Call) Return(_a0 *price_registry.PriceRegistryUsdPerUnitGasUpdated, _a1 error) *PriceRegistryInterface_ParseUsdPerUnitGasUpdated_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_ParseUsdPerUnitGasUpdated_Call) RunAndReturn(run func(types.Log) (*price_registry.PriceRegistryUsdPerUnitGasUpdated, error)) *PriceRegistryInterface_ParseUsdPerUnitGasUpdated_Call { + _c.Call.Return(run) + return _c +} + +// ProcessMessageArgs provides a mock function with given fields: opts, destChainSelector, feeToken, feeTokenAmount, extraArgs +func (_m *PriceRegistryInterface) ProcessMessageArgs(opts *bind.CallOpts, destChainSelector uint64, feeToken common.Address, feeTokenAmount *big.Int, extraArgs []byte) (price_registry.ProcessMessageArgs, error) { + ret := _m.Called(opts, destChainSelector, feeToken, feeTokenAmount, extraArgs) + + if len(ret) == 0 { + panic("no return value specified for ProcessMessageArgs") + } + + var r0 price_registry.ProcessMessageArgs + var r1 error + if rf, ok := ret.Get(0).(func(*bind.CallOpts, uint64, common.Address, *big.Int, []byte) (price_registry.ProcessMessageArgs, error)); ok { + return rf(opts, destChainSelector, feeToken, feeTokenAmount, extraArgs) + } + if rf, ok := ret.Get(0).(func(*bind.CallOpts, uint64, common.Address, *big.Int, []byte) price_registry.ProcessMessageArgs); ok { + r0 = rf(opts, destChainSelector, feeToken, feeTokenAmount, extraArgs) + } else { + r0 = ret.Get(0).(price_registry.ProcessMessageArgs) + } + + if rf, ok := ret.Get(1).(func(*bind.CallOpts, uint64, common.Address, *big.Int, []byte) error); ok { + r1 = rf(opts, destChainSelector, feeToken, feeTokenAmount, extraArgs) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_ProcessMessageArgs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ProcessMessageArgs' +type PriceRegistryInterface_ProcessMessageArgs_Call struct { + *mock.Call +} + +// ProcessMessageArgs is a helper method to define mock.On call +// - opts *bind.CallOpts +// - destChainSelector uint64 +// - feeToken common.Address +// - feeTokenAmount *big.Int +// - extraArgs []byte +func (_e *PriceRegistryInterface_Expecter) ProcessMessageArgs(opts interface{}, destChainSelector interface{}, feeToken interface{}, feeTokenAmount interface{}, extraArgs interface{}) *PriceRegistryInterface_ProcessMessageArgs_Call { + return &PriceRegistryInterface_ProcessMessageArgs_Call{Call: _e.mock.On("ProcessMessageArgs", opts, destChainSelector, feeToken, feeTokenAmount, extraArgs)} +} + +func (_c *PriceRegistryInterface_ProcessMessageArgs_Call) Run(run func(opts *bind.CallOpts, destChainSelector uint64, feeToken common.Address, feeTokenAmount *big.Int, extraArgs []byte)) *PriceRegistryInterface_ProcessMessageArgs_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.CallOpts), args[1].(uint64), args[2].(common.Address), args[3].(*big.Int), args[4].([]byte)) + }) + return _c +} + +func (_c *PriceRegistryInterface_ProcessMessageArgs_Call) Return(_a0 price_registry.ProcessMessageArgs, _a1 error) *PriceRegistryInterface_ProcessMessageArgs_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_ProcessMessageArgs_Call) RunAndReturn(run func(*bind.CallOpts, uint64, common.Address, *big.Int, []byte) (price_registry.ProcessMessageArgs, error)) *PriceRegistryInterface_ProcessMessageArgs_Call { + _c.Call.Return(run) + return _c +} + +// TransferOwnership provides a mock function with given fields: opts, to +func (_m *PriceRegistryInterface) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { + ret := _m.Called(opts, to) + + if len(ret) == 0 { + panic("no return value specified for TransferOwnership") + } + + var r0 *types.Transaction + var r1 error + if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address) (*types.Transaction, error)); ok { + return rf(opts, to) + } + if rf, ok := ret.Get(0).(func(*bind.TransactOpts, common.Address) *types.Transaction); ok { + r0 = rf(opts, to) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Transaction) + } + } + + if rf, ok := ret.Get(1).(func(*bind.TransactOpts, common.Address) error); ok { + r1 = rf(opts, to) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_TransferOwnership_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TransferOwnership' +type PriceRegistryInterface_TransferOwnership_Call struct { + *mock.Call +} + +// TransferOwnership is a helper method to define mock.On call +// - opts *bind.TransactOpts +// - to common.Address +func (_e *PriceRegistryInterface_Expecter) TransferOwnership(opts interface{}, to interface{}) *PriceRegistryInterface_TransferOwnership_Call { + return &PriceRegistryInterface_TransferOwnership_Call{Call: _e.mock.On("TransferOwnership", opts, to)} +} + +func (_c *PriceRegistryInterface_TransferOwnership_Call) Run(run func(opts *bind.TransactOpts, to common.Address)) *PriceRegistryInterface_TransferOwnership_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.TransactOpts), args[1].(common.Address)) + }) + return _c +} + +func (_c *PriceRegistryInterface_TransferOwnership_Call) Return(_a0 *types.Transaction, _a1 error) *PriceRegistryInterface_TransferOwnership_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_TransferOwnership_Call) RunAndReturn(run func(*bind.TransactOpts, common.Address) (*types.Transaction, error)) *PriceRegistryInterface_TransferOwnership_Call { + _c.Call.Return(run) + return _c +} + +// TypeAndVersion provides a mock function with given fields: opts +func (_m *PriceRegistryInterface) TypeAndVersion(opts *bind.CallOpts) (string, error) { + ret := _m.Called(opts) + + if len(ret) == 0 { + panic("no return value specified for TypeAndVersion") + } + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(*bind.CallOpts) (string, error)); ok { + return rf(opts) + } + if rf, ok := ret.Get(0).(func(*bind.CallOpts) string); ok { + r0 = rf(opts) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(*bind.CallOpts) error); ok { + r1 = rf(opts) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_TypeAndVersion_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TypeAndVersion' +type PriceRegistryInterface_TypeAndVersion_Call struct { + *mock.Call +} + +// TypeAndVersion is a helper method to define mock.On call +// - opts *bind.CallOpts +func (_e *PriceRegistryInterface_Expecter) TypeAndVersion(opts interface{}) *PriceRegistryInterface_TypeAndVersion_Call { + return &PriceRegistryInterface_TypeAndVersion_Call{Call: _e.mock.On("TypeAndVersion", opts)} +} + +func (_c *PriceRegistryInterface_TypeAndVersion_Call) Run(run func(opts *bind.CallOpts)) *PriceRegistryInterface_TypeAndVersion_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.CallOpts)) + }) + return _c +} + +func (_c *PriceRegistryInterface_TypeAndVersion_Call) Return(_a0 string, _a1 error) *PriceRegistryInterface_TypeAndVersion_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_TypeAndVersion_Call) RunAndReturn(run func(*bind.CallOpts) (string, error)) *PriceRegistryInterface_TypeAndVersion_Call { + _c.Call.Return(run) + return _c +} + +// UpdatePrices provides a mock function with given fields: opts, priceUpdates +func (_m *PriceRegistryInterface) UpdatePrices(opts *bind.TransactOpts, priceUpdates price_registry.InternalPriceUpdates) (*types.Transaction, error) { + ret := _m.Called(opts, priceUpdates) + + if len(ret) == 0 { + panic("no return value specified for UpdatePrices") + } + + var r0 *types.Transaction + var r1 error + if rf, ok := ret.Get(0).(func(*bind.TransactOpts, price_registry.InternalPriceUpdates) (*types.Transaction, error)); ok { + return rf(opts, priceUpdates) + } + if rf, ok := ret.Get(0).(func(*bind.TransactOpts, price_registry.InternalPriceUpdates) *types.Transaction); ok { + r0 = rf(opts, priceUpdates) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Transaction) + } + } + + if rf, ok := ret.Get(1).(func(*bind.TransactOpts, price_registry.InternalPriceUpdates) error); ok { + r1 = rf(opts, priceUpdates) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_UpdatePrices_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdatePrices' +type PriceRegistryInterface_UpdatePrices_Call struct { + *mock.Call +} + +// UpdatePrices is a helper method to define mock.On call +// - opts *bind.TransactOpts +// - priceUpdates price_registry.InternalPriceUpdates +func (_e *PriceRegistryInterface_Expecter) UpdatePrices(opts interface{}, priceUpdates interface{}) *PriceRegistryInterface_UpdatePrices_Call { + return &PriceRegistryInterface_UpdatePrices_Call{Call: _e.mock.On("UpdatePrices", opts, priceUpdates)} +} + +func (_c *PriceRegistryInterface_UpdatePrices_Call) Run(run func(opts *bind.TransactOpts, priceUpdates price_registry.InternalPriceUpdates)) *PriceRegistryInterface_UpdatePrices_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.TransactOpts), args[1].(price_registry.InternalPriceUpdates)) + }) + return _c +} + +func (_c *PriceRegistryInterface_UpdatePrices_Call) Return(_a0 *types.Transaction, _a1 error) *PriceRegistryInterface_UpdatePrices_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_UpdatePrices_Call) RunAndReturn(run func(*bind.TransactOpts, price_registry.InternalPriceUpdates) (*types.Transaction, error)) *PriceRegistryInterface_UpdatePrices_Call { + _c.Call.Return(run) + return _c +} + +// UpdateTokenPriceFeeds provides a mock function with given fields: opts, tokenPriceFeedUpdates +func (_m *PriceRegistryInterface) UpdateTokenPriceFeeds(opts *bind.TransactOpts, tokenPriceFeedUpdates []price_registry.PriceRegistryTokenPriceFeedUpdate) (*types.Transaction, error) { + ret := _m.Called(opts, tokenPriceFeedUpdates) + + if len(ret) == 0 { + panic("no return value specified for UpdateTokenPriceFeeds") + } + + var r0 *types.Transaction + var r1 error + if rf, ok := ret.Get(0).(func(*bind.TransactOpts, []price_registry.PriceRegistryTokenPriceFeedUpdate) (*types.Transaction, error)); ok { + return rf(opts, tokenPriceFeedUpdates) + } + if rf, ok := ret.Get(0).(func(*bind.TransactOpts, []price_registry.PriceRegistryTokenPriceFeedUpdate) *types.Transaction); ok { + r0 = rf(opts, tokenPriceFeedUpdates) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Transaction) + } + } + + if rf, ok := ret.Get(1).(func(*bind.TransactOpts, []price_registry.PriceRegistryTokenPriceFeedUpdate) error); ok { + r1 = rf(opts, tokenPriceFeedUpdates) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_UpdateTokenPriceFeeds_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateTokenPriceFeeds' +type PriceRegistryInterface_UpdateTokenPriceFeeds_Call struct { + *mock.Call +} + +// UpdateTokenPriceFeeds is a helper method to define mock.On call +// - opts *bind.TransactOpts +// - tokenPriceFeedUpdates []price_registry.PriceRegistryTokenPriceFeedUpdate +func (_e *PriceRegistryInterface_Expecter) UpdateTokenPriceFeeds(opts interface{}, tokenPriceFeedUpdates interface{}) *PriceRegistryInterface_UpdateTokenPriceFeeds_Call { + return &PriceRegistryInterface_UpdateTokenPriceFeeds_Call{Call: _e.mock.On("UpdateTokenPriceFeeds", opts, tokenPriceFeedUpdates)} +} + +func (_c *PriceRegistryInterface_UpdateTokenPriceFeeds_Call) Run(run func(opts *bind.TransactOpts, tokenPriceFeedUpdates []price_registry.PriceRegistryTokenPriceFeedUpdate)) *PriceRegistryInterface_UpdateTokenPriceFeeds_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.TransactOpts), args[1].([]price_registry.PriceRegistryTokenPriceFeedUpdate)) + }) + return _c +} + +func (_c *PriceRegistryInterface_UpdateTokenPriceFeeds_Call) Return(_a0 *types.Transaction, _a1 error) *PriceRegistryInterface_UpdateTokenPriceFeeds_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_UpdateTokenPriceFeeds_Call) RunAndReturn(run func(*bind.TransactOpts, []price_registry.PriceRegistryTokenPriceFeedUpdate) (*types.Transaction, error)) *PriceRegistryInterface_UpdateTokenPriceFeeds_Call { + _c.Call.Return(run) + return _c +} + +// ValidatePoolReturnData provides a mock function with given fields: opts, destChainSelector, rampTokenAmounts, sourceTokenAmounts +func (_m *PriceRegistryInterface) ValidatePoolReturnData(opts *bind.CallOpts, destChainSelector uint64, rampTokenAmounts []price_registry.InternalRampTokenAmount, sourceTokenAmounts []price_registry.ClientEVMTokenAmount) error { + ret := _m.Called(opts, destChainSelector, rampTokenAmounts, sourceTokenAmounts) + + if len(ret) == 0 { + panic("no return value specified for ValidatePoolReturnData") + } + + var r0 error + if rf, ok := ret.Get(0).(func(*bind.CallOpts, uint64, []price_registry.InternalRampTokenAmount, []price_registry.ClientEVMTokenAmount) error); ok { + r0 = rf(opts, destChainSelector, rampTokenAmounts, sourceTokenAmounts) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// PriceRegistryInterface_ValidatePoolReturnData_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ValidatePoolReturnData' +type PriceRegistryInterface_ValidatePoolReturnData_Call struct { + *mock.Call +} + +// ValidatePoolReturnData is a helper method to define mock.On call +// - opts *bind.CallOpts +// - destChainSelector uint64 +// - rampTokenAmounts []price_registry.InternalRampTokenAmount +// - sourceTokenAmounts []price_registry.ClientEVMTokenAmount +func (_e *PriceRegistryInterface_Expecter) ValidatePoolReturnData(opts interface{}, destChainSelector interface{}, rampTokenAmounts interface{}, sourceTokenAmounts interface{}) *PriceRegistryInterface_ValidatePoolReturnData_Call { + return &PriceRegistryInterface_ValidatePoolReturnData_Call{Call: _e.mock.On("ValidatePoolReturnData", opts, destChainSelector, rampTokenAmounts, sourceTokenAmounts)} +} + +func (_c *PriceRegistryInterface_ValidatePoolReturnData_Call) Run(run func(opts *bind.CallOpts, destChainSelector uint64, rampTokenAmounts []price_registry.InternalRampTokenAmount, sourceTokenAmounts []price_registry.ClientEVMTokenAmount)) *PriceRegistryInterface_ValidatePoolReturnData_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.CallOpts), args[1].(uint64), args[2].([]price_registry.InternalRampTokenAmount), args[3].([]price_registry.ClientEVMTokenAmount)) + }) + return _c +} + +func (_c *PriceRegistryInterface_ValidatePoolReturnData_Call) Return(_a0 error) *PriceRegistryInterface_ValidatePoolReturnData_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *PriceRegistryInterface_ValidatePoolReturnData_Call) RunAndReturn(run func(*bind.CallOpts, uint64, []price_registry.InternalRampTokenAmount, []price_registry.ClientEVMTokenAmount) error) *PriceRegistryInterface_ValidatePoolReturnData_Call { + _c.Call.Return(run) + return _c +} + +// WatchAuthorizedCallerAdded provides a mock function with given fields: opts, sink +func (_m *PriceRegistryInterface) WatchAuthorizedCallerAdded(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryAuthorizedCallerAdded) (event.Subscription, error) { + ret := _m.Called(opts, sink) + + if len(ret) == 0 { + panic("no return value specified for WatchAuthorizedCallerAdded") + } + + var r0 event.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryAuthorizedCallerAdded) (event.Subscription, error)); ok { + return rf(opts, sink) + } + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryAuthorizedCallerAdded) event.Subscription); ok { + r0 = rf(opts, sink) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(event.Subscription) + } + } + + if rf, ok := ret.Get(1).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryAuthorizedCallerAdded) error); ok { + r1 = rf(opts, sink) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_WatchAuthorizedCallerAdded_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WatchAuthorizedCallerAdded' +type PriceRegistryInterface_WatchAuthorizedCallerAdded_Call struct { + *mock.Call +} + +// WatchAuthorizedCallerAdded is a helper method to define mock.On call +// - opts *bind.WatchOpts +// - sink chan<- *price_registry.PriceRegistryAuthorizedCallerAdded +func (_e *PriceRegistryInterface_Expecter) WatchAuthorizedCallerAdded(opts interface{}, sink interface{}) *PriceRegistryInterface_WatchAuthorizedCallerAdded_Call { + return &PriceRegistryInterface_WatchAuthorizedCallerAdded_Call{Call: _e.mock.On("WatchAuthorizedCallerAdded", opts, sink)} +} + +func (_c *PriceRegistryInterface_WatchAuthorizedCallerAdded_Call) Run(run func(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryAuthorizedCallerAdded)) *PriceRegistryInterface_WatchAuthorizedCallerAdded_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.WatchOpts), args[1].(chan<- *price_registry.PriceRegistryAuthorizedCallerAdded)) + }) + return _c +} + +func (_c *PriceRegistryInterface_WatchAuthorizedCallerAdded_Call) Return(_a0 event.Subscription, _a1 error) *PriceRegistryInterface_WatchAuthorizedCallerAdded_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_WatchAuthorizedCallerAdded_Call) RunAndReturn(run func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryAuthorizedCallerAdded) (event.Subscription, error)) *PriceRegistryInterface_WatchAuthorizedCallerAdded_Call { + _c.Call.Return(run) + return _c +} + +// WatchAuthorizedCallerRemoved provides a mock function with given fields: opts, sink +func (_m *PriceRegistryInterface) WatchAuthorizedCallerRemoved(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryAuthorizedCallerRemoved) (event.Subscription, error) { + ret := _m.Called(opts, sink) + + if len(ret) == 0 { + panic("no return value specified for WatchAuthorizedCallerRemoved") + } + + var r0 event.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryAuthorizedCallerRemoved) (event.Subscription, error)); ok { + return rf(opts, sink) + } + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryAuthorizedCallerRemoved) event.Subscription); ok { + r0 = rf(opts, sink) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(event.Subscription) + } + } + + if rf, ok := ret.Get(1).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryAuthorizedCallerRemoved) error); ok { + r1 = rf(opts, sink) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_WatchAuthorizedCallerRemoved_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WatchAuthorizedCallerRemoved' +type PriceRegistryInterface_WatchAuthorizedCallerRemoved_Call struct { + *mock.Call +} + +// WatchAuthorizedCallerRemoved is a helper method to define mock.On call +// - opts *bind.WatchOpts +// - sink chan<- *price_registry.PriceRegistryAuthorizedCallerRemoved +func (_e *PriceRegistryInterface_Expecter) WatchAuthorizedCallerRemoved(opts interface{}, sink interface{}) *PriceRegistryInterface_WatchAuthorizedCallerRemoved_Call { + return &PriceRegistryInterface_WatchAuthorizedCallerRemoved_Call{Call: _e.mock.On("WatchAuthorizedCallerRemoved", opts, sink)} +} + +func (_c *PriceRegistryInterface_WatchAuthorizedCallerRemoved_Call) Run(run func(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryAuthorizedCallerRemoved)) *PriceRegistryInterface_WatchAuthorizedCallerRemoved_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.WatchOpts), args[1].(chan<- *price_registry.PriceRegistryAuthorizedCallerRemoved)) + }) + return _c +} + +func (_c *PriceRegistryInterface_WatchAuthorizedCallerRemoved_Call) Return(_a0 event.Subscription, _a1 error) *PriceRegistryInterface_WatchAuthorizedCallerRemoved_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_WatchAuthorizedCallerRemoved_Call) RunAndReturn(run func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryAuthorizedCallerRemoved) (event.Subscription, error)) *PriceRegistryInterface_WatchAuthorizedCallerRemoved_Call { + _c.Call.Return(run) + return _c +} + +// WatchDestChainAdded provides a mock function with given fields: opts, sink, destChainSelector +func (_m *PriceRegistryInterface) WatchDestChainAdded(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryDestChainAdded, destChainSelector []uint64) (event.Subscription, error) { + ret := _m.Called(opts, sink, destChainSelector) + + if len(ret) == 0 { + panic("no return value specified for WatchDestChainAdded") + } + + var r0 event.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryDestChainAdded, []uint64) (event.Subscription, error)); ok { + return rf(opts, sink, destChainSelector) + } + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryDestChainAdded, []uint64) event.Subscription); ok { + r0 = rf(opts, sink, destChainSelector) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(event.Subscription) + } + } + + if rf, ok := ret.Get(1).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryDestChainAdded, []uint64) error); ok { + r1 = rf(opts, sink, destChainSelector) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_WatchDestChainAdded_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WatchDestChainAdded' +type PriceRegistryInterface_WatchDestChainAdded_Call struct { + *mock.Call +} + +// WatchDestChainAdded is a helper method to define mock.On call +// - opts *bind.WatchOpts +// - sink chan<- *price_registry.PriceRegistryDestChainAdded +// - destChainSelector []uint64 +func (_e *PriceRegistryInterface_Expecter) WatchDestChainAdded(opts interface{}, sink interface{}, destChainSelector interface{}) *PriceRegistryInterface_WatchDestChainAdded_Call { + return &PriceRegistryInterface_WatchDestChainAdded_Call{Call: _e.mock.On("WatchDestChainAdded", opts, sink, destChainSelector)} +} + +func (_c *PriceRegistryInterface_WatchDestChainAdded_Call) Run(run func(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryDestChainAdded, destChainSelector []uint64)) *PriceRegistryInterface_WatchDestChainAdded_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.WatchOpts), args[1].(chan<- *price_registry.PriceRegistryDestChainAdded), args[2].([]uint64)) + }) + return _c +} + +func (_c *PriceRegistryInterface_WatchDestChainAdded_Call) Return(_a0 event.Subscription, _a1 error) *PriceRegistryInterface_WatchDestChainAdded_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_WatchDestChainAdded_Call) RunAndReturn(run func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryDestChainAdded, []uint64) (event.Subscription, error)) *PriceRegistryInterface_WatchDestChainAdded_Call { + _c.Call.Return(run) + return _c +} + +// WatchDestChainConfigUpdated provides a mock function with given fields: opts, sink, destChainSelector +func (_m *PriceRegistryInterface) WatchDestChainConfigUpdated(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryDestChainConfigUpdated, destChainSelector []uint64) (event.Subscription, error) { + ret := _m.Called(opts, sink, destChainSelector) + + if len(ret) == 0 { + panic("no return value specified for WatchDestChainConfigUpdated") + } + + var r0 event.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryDestChainConfigUpdated, []uint64) (event.Subscription, error)); ok { + return rf(opts, sink, destChainSelector) + } + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryDestChainConfigUpdated, []uint64) event.Subscription); ok { + r0 = rf(opts, sink, destChainSelector) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(event.Subscription) + } + } + + if rf, ok := ret.Get(1).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryDestChainConfigUpdated, []uint64) error); ok { + r1 = rf(opts, sink, destChainSelector) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_WatchDestChainConfigUpdated_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WatchDestChainConfigUpdated' +type PriceRegistryInterface_WatchDestChainConfigUpdated_Call struct { + *mock.Call +} + +// WatchDestChainConfigUpdated is a helper method to define mock.On call +// - opts *bind.WatchOpts +// - sink chan<- *price_registry.PriceRegistryDestChainConfigUpdated +// - destChainSelector []uint64 +func (_e *PriceRegistryInterface_Expecter) WatchDestChainConfigUpdated(opts interface{}, sink interface{}, destChainSelector interface{}) *PriceRegistryInterface_WatchDestChainConfigUpdated_Call { + return &PriceRegistryInterface_WatchDestChainConfigUpdated_Call{Call: _e.mock.On("WatchDestChainConfigUpdated", opts, sink, destChainSelector)} +} + +func (_c *PriceRegistryInterface_WatchDestChainConfigUpdated_Call) Run(run func(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryDestChainConfigUpdated, destChainSelector []uint64)) *PriceRegistryInterface_WatchDestChainConfigUpdated_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.WatchOpts), args[1].(chan<- *price_registry.PriceRegistryDestChainConfigUpdated), args[2].([]uint64)) + }) + return _c +} + +func (_c *PriceRegistryInterface_WatchDestChainConfigUpdated_Call) Return(_a0 event.Subscription, _a1 error) *PriceRegistryInterface_WatchDestChainConfigUpdated_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_WatchDestChainConfigUpdated_Call) RunAndReturn(run func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryDestChainConfigUpdated, []uint64) (event.Subscription, error)) *PriceRegistryInterface_WatchDestChainConfigUpdated_Call { + _c.Call.Return(run) + return _c +} + +// WatchFeeTokenAdded provides a mock function with given fields: opts, sink, feeToken +func (_m *PriceRegistryInterface) WatchFeeTokenAdded(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryFeeTokenAdded, feeToken []common.Address) (event.Subscription, error) { + ret := _m.Called(opts, sink, feeToken) + + if len(ret) == 0 { + panic("no return value specified for WatchFeeTokenAdded") + } + + var r0 event.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryFeeTokenAdded, []common.Address) (event.Subscription, error)); ok { + return rf(opts, sink, feeToken) + } + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryFeeTokenAdded, []common.Address) event.Subscription); ok { + r0 = rf(opts, sink, feeToken) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(event.Subscription) + } + } + + if rf, ok := ret.Get(1).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryFeeTokenAdded, []common.Address) error); ok { + r1 = rf(opts, sink, feeToken) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_WatchFeeTokenAdded_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WatchFeeTokenAdded' +type PriceRegistryInterface_WatchFeeTokenAdded_Call struct { + *mock.Call +} + +// WatchFeeTokenAdded is a helper method to define mock.On call +// - opts *bind.WatchOpts +// - sink chan<- *price_registry.PriceRegistryFeeTokenAdded +// - feeToken []common.Address +func (_e *PriceRegistryInterface_Expecter) WatchFeeTokenAdded(opts interface{}, sink interface{}, feeToken interface{}) *PriceRegistryInterface_WatchFeeTokenAdded_Call { + return &PriceRegistryInterface_WatchFeeTokenAdded_Call{Call: _e.mock.On("WatchFeeTokenAdded", opts, sink, feeToken)} +} + +func (_c *PriceRegistryInterface_WatchFeeTokenAdded_Call) Run(run func(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryFeeTokenAdded, feeToken []common.Address)) *PriceRegistryInterface_WatchFeeTokenAdded_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.WatchOpts), args[1].(chan<- *price_registry.PriceRegistryFeeTokenAdded), args[2].([]common.Address)) + }) + return _c +} + +func (_c *PriceRegistryInterface_WatchFeeTokenAdded_Call) Return(_a0 event.Subscription, _a1 error) *PriceRegistryInterface_WatchFeeTokenAdded_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_WatchFeeTokenAdded_Call) RunAndReturn(run func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryFeeTokenAdded, []common.Address) (event.Subscription, error)) *PriceRegistryInterface_WatchFeeTokenAdded_Call { + _c.Call.Return(run) + return _c +} + +// WatchFeeTokenRemoved provides a mock function with given fields: opts, sink, feeToken +func (_m *PriceRegistryInterface) WatchFeeTokenRemoved(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryFeeTokenRemoved, feeToken []common.Address) (event.Subscription, error) { + ret := _m.Called(opts, sink, feeToken) + + if len(ret) == 0 { + panic("no return value specified for WatchFeeTokenRemoved") + } + + var r0 event.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryFeeTokenRemoved, []common.Address) (event.Subscription, error)); ok { + return rf(opts, sink, feeToken) + } + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryFeeTokenRemoved, []common.Address) event.Subscription); ok { + r0 = rf(opts, sink, feeToken) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(event.Subscription) + } + } + + if rf, ok := ret.Get(1).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryFeeTokenRemoved, []common.Address) error); ok { + r1 = rf(opts, sink, feeToken) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_WatchFeeTokenRemoved_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WatchFeeTokenRemoved' +type PriceRegistryInterface_WatchFeeTokenRemoved_Call struct { + *mock.Call +} + +// WatchFeeTokenRemoved is a helper method to define mock.On call +// - opts *bind.WatchOpts +// - sink chan<- *price_registry.PriceRegistryFeeTokenRemoved +// - feeToken []common.Address +func (_e *PriceRegistryInterface_Expecter) WatchFeeTokenRemoved(opts interface{}, sink interface{}, feeToken interface{}) *PriceRegistryInterface_WatchFeeTokenRemoved_Call { + return &PriceRegistryInterface_WatchFeeTokenRemoved_Call{Call: _e.mock.On("WatchFeeTokenRemoved", opts, sink, feeToken)} +} + +func (_c *PriceRegistryInterface_WatchFeeTokenRemoved_Call) Run(run func(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryFeeTokenRemoved, feeToken []common.Address)) *PriceRegistryInterface_WatchFeeTokenRemoved_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.WatchOpts), args[1].(chan<- *price_registry.PriceRegistryFeeTokenRemoved), args[2].([]common.Address)) + }) + return _c +} + +func (_c *PriceRegistryInterface_WatchFeeTokenRemoved_Call) Return(_a0 event.Subscription, _a1 error) *PriceRegistryInterface_WatchFeeTokenRemoved_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_WatchFeeTokenRemoved_Call) RunAndReturn(run func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryFeeTokenRemoved, []common.Address) (event.Subscription, error)) *PriceRegistryInterface_WatchFeeTokenRemoved_Call { + _c.Call.Return(run) + return _c +} + +// WatchOwnershipTransferRequested provides a mock function with given fields: opts, sink, from, to +func (_m *PriceRegistryInterface) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { + ret := _m.Called(opts, sink, from, to) + + if len(ret) == 0 { + panic("no return value specified for WatchOwnershipTransferRequested") + } + + var r0 event.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryOwnershipTransferRequested, []common.Address, []common.Address) (event.Subscription, error)); ok { + return rf(opts, sink, from, to) + } + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryOwnershipTransferRequested, []common.Address, []common.Address) event.Subscription); ok { + r0 = rf(opts, sink, from, to) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(event.Subscription) + } + } + + if rf, ok := ret.Get(1).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryOwnershipTransferRequested, []common.Address, []common.Address) error); ok { + r1 = rf(opts, sink, from, to) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_WatchOwnershipTransferRequested_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WatchOwnershipTransferRequested' +type PriceRegistryInterface_WatchOwnershipTransferRequested_Call struct { + *mock.Call +} + +// WatchOwnershipTransferRequested is a helper method to define mock.On call +// - opts *bind.WatchOpts +// - sink chan<- *price_registry.PriceRegistryOwnershipTransferRequested +// - from []common.Address +// - to []common.Address +func (_e *PriceRegistryInterface_Expecter) WatchOwnershipTransferRequested(opts interface{}, sink interface{}, from interface{}, to interface{}) *PriceRegistryInterface_WatchOwnershipTransferRequested_Call { + return &PriceRegistryInterface_WatchOwnershipTransferRequested_Call{Call: _e.mock.On("WatchOwnershipTransferRequested", opts, sink, from, to)} +} + +func (_c *PriceRegistryInterface_WatchOwnershipTransferRequested_Call) Run(run func(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryOwnershipTransferRequested, from []common.Address, to []common.Address)) *PriceRegistryInterface_WatchOwnershipTransferRequested_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.WatchOpts), args[1].(chan<- *price_registry.PriceRegistryOwnershipTransferRequested), args[2].([]common.Address), args[3].([]common.Address)) + }) + return _c +} + +func (_c *PriceRegistryInterface_WatchOwnershipTransferRequested_Call) Return(_a0 event.Subscription, _a1 error) *PriceRegistryInterface_WatchOwnershipTransferRequested_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_WatchOwnershipTransferRequested_Call) RunAndReturn(run func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryOwnershipTransferRequested, []common.Address, []common.Address) (event.Subscription, error)) *PriceRegistryInterface_WatchOwnershipTransferRequested_Call { + _c.Call.Return(run) + return _c +} + +// WatchOwnershipTransferred provides a mock function with given fields: opts, sink, from, to +func (_m *PriceRegistryInterface) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { + ret := _m.Called(opts, sink, from, to) + + if len(ret) == 0 { + panic("no return value specified for WatchOwnershipTransferred") + } + + var r0 event.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryOwnershipTransferred, []common.Address, []common.Address) (event.Subscription, error)); ok { + return rf(opts, sink, from, to) + } + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryOwnershipTransferred, []common.Address, []common.Address) event.Subscription); ok { + r0 = rf(opts, sink, from, to) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(event.Subscription) + } + } + + if rf, ok := ret.Get(1).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryOwnershipTransferred, []common.Address, []common.Address) error); ok { + r1 = rf(opts, sink, from, to) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_WatchOwnershipTransferred_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WatchOwnershipTransferred' +type PriceRegistryInterface_WatchOwnershipTransferred_Call struct { + *mock.Call +} + +// WatchOwnershipTransferred is a helper method to define mock.On call +// - opts *bind.WatchOpts +// - sink chan<- *price_registry.PriceRegistryOwnershipTransferred +// - from []common.Address +// - to []common.Address +func (_e *PriceRegistryInterface_Expecter) WatchOwnershipTransferred(opts interface{}, sink interface{}, from interface{}, to interface{}) *PriceRegistryInterface_WatchOwnershipTransferred_Call { + return &PriceRegistryInterface_WatchOwnershipTransferred_Call{Call: _e.mock.On("WatchOwnershipTransferred", opts, sink, from, to)} +} + +func (_c *PriceRegistryInterface_WatchOwnershipTransferred_Call) Run(run func(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryOwnershipTransferred, from []common.Address, to []common.Address)) *PriceRegistryInterface_WatchOwnershipTransferred_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.WatchOpts), args[1].(chan<- *price_registry.PriceRegistryOwnershipTransferred), args[2].([]common.Address), args[3].([]common.Address)) + }) + return _c +} + +func (_c *PriceRegistryInterface_WatchOwnershipTransferred_Call) Return(_a0 event.Subscription, _a1 error) *PriceRegistryInterface_WatchOwnershipTransferred_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_WatchOwnershipTransferred_Call) RunAndReturn(run func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryOwnershipTransferred, []common.Address, []common.Address) (event.Subscription, error)) *PriceRegistryInterface_WatchOwnershipTransferred_Call { + _c.Call.Return(run) + return _c +} + +// WatchPremiumMultiplierWeiPerEthUpdated provides a mock function with given fields: opts, sink, token +func (_m *PriceRegistryInterface) WatchPremiumMultiplierWeiPerEthUpdated(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryPremiumMultiplierWeiPerEthUpdated, token []common.Address) (event.Subscription, error) { + ret := _m.Called(opts, sink, token) + + if len(ret) == 0 { + panic("no return value specified for WatchPremiumMultiplierWeiPerEthUpdated") + } + + var r0 event.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryPremiumMultiplierWeiPerEthUpdated, []common.Address) (event.Subscription, error)); ok { + return rf(opts, sink, token) + } + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryPremiumMultiplierWeiPerEthUpdated, []common.Address) event.Subscription); ok { + r0 = rf(opts, sink, token) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(event.Subscription) + } + } + + if rf, ok := ret.Get(1).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryPremiumMultiplierWeiPerEthUpdated, []common.Address) error); ok { + r1 = rf(opts, sink, token) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_WatchPremiumMultiplierWeiPerEthUpdated_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WatchPremiumMultiplierWeiPerEthUpdated' +type PriceRegistryInterface_WatchPremiumMultiplierWeiPerEthUpdated_Call struct { + *mock.Call +} + +// WatchPremiumMultiplierWeiPerEthUpdated is a helper method to define mock.On call +// - opts *bind.WatchOpts +// - sink chan<- *price_registry.PriceRegistryPremiumMultiplierWeiPerEthUpdated +// - token []common.Address +func (_e *PriceRegistryInterface_Expecter) WatchPremiumMultiplierWeiPerEthUpdated(opts interface{}, sink interface{}, token interface{}) *PriceRegistryInterface_WatchPremiumMultiplierWeiPerEthUpdated_Call { + return &PriceRegistryInterface_WatchPremiumMultiplierWeiPerEthUpdated_Call{Call: _e.mock.On("WatchPremiumMultiplierWeiPerEthUpdated", opts, sink, token)} +} + +func (_c *PriceRegistryInterface_WatchPremiumMultiplierWeiPerEthUpdated_Call) Run(run func(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryPremiumMultiplierWeiPerEthUpdated, token []common.Address)) *PriceRegistryInterface_WatchPremiumMultiplierWeiPerEthUpdated_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.WatchOpts), args[1].(chan<- *price_registry.PriceRegistryPremiumMultiplierWeiPerEthUpdated), args[2].([]common.Address)) + }) + return _c +} + +func (_c *PriceRegistryInterface_WatchPremiumMultiplierWeiPerEthUpdated_Call) Return(_a0 event.Subscription, _a1 error) *PriceRegistryInterface_WatchPremiumMultiplierWeiPerEthUpdated_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_WatchPremiumMultiplierWeiPerEthUpdated_Call) RunAndReturn(run func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryPremiumMultiplierWeiPerEthUpdated, []common.Address) (event.Subscription, error)) *PriceRegistryInterface_WatchPremiumMultiplierWeiPerEthUpdated_Call { + _c.Call.Return(run) + return _c +} + +// WatchPriceFeedPerTokenUpdated provides a mock function with given fields: opts, sink, token +func (_m *PriceRegistryInterface) WatchPriceFeedPerTokenUpdated(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryPriceFeedPerTokenUpdated, token []common.Address) (event.Subscription, error) { + ret := _m.Called(opts, sink, token) + + if len(ret) == 0 { + panic("no return value specified for WatchPriceFeedPerTokenUpdated") + } + + var r0 event.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryPriceFeedPerTokenUpdated, []common.Address) (event.Subscription, error)); ok { + return rf(opts, sink, token) + } + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryPriceFeedPerTokenUpdated, []common.Address) event.Subscription); ok { + r0 = rf(opts, sink, token) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(event.Subscription) + } + } + + if rf, ok := ret.Get(1).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryPriceFeedPerTokenUpdated, []common.Address) error); ok { + r1 = rf(opts, sink, token) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_WatchPriceFeedPerTokenUpdated_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WatchPriceFeedPerTokenUpdated' +type PriceRegistryInterface_WatchPriceFeedPerTokenUpdated_Call struct { + *mock.Call +} + +// WatchPriceFeedPerTokenUpdated is a helper method to define mock.On call +// - opts *bind.WatchOpts +// - sink chan<- *price_registry.PriceRegistryPriceFeedPerTokenUpdated +// - token []common.Address +func (_e *PriceRegistryInterface_Expecter) WatchPriceFeedPerTokenUpdated(opts interface{}, sink interface{}, token interface{}) *PriceRegistryInterface_WatchPriceFeedPerTokenUpdated_Call { + return &PriceRegistryInterface_WatchPriceFeedPerTokenUpdated_Call{Call: _e.mock.On("WatchPriceFeedPerTokenUpdated", opts, sink, token)} +} + +func (_c *PriceRegistryInterface_WatchPriceFeedPerTokenUpdated_Call) Run(run func(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryPriceFeedPerTokenUpdated, token []common.Address)) *PriceRegistryInterface_WatchPriceFeedPerTokenUpdated_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.WatchOpts), args[1].(chan<- *price_registry.PriceRegistryPriceFeedPerTokenUpdated), args[2].([]common.Address)) + }) + return _c +} + +func (_c *PriceRegistryInterface_WatchPriceFeedPerTokenUpdated_Call) Return(_a0 event.Subscription, _a1 error) *PriceRegistryInterface_WatchPriceFeedPerTokenUpdated_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_WatchPriceFeedPerTokenUpdated_Call) RunAndReturn(run func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryPriceFeedPerTokenUpdated, []common.Address) (event.Subscription, error)) *PriceRegistryInterface_WatchPriceFeedPerTokenUpdated_Call { + _c.Call.Return(run) + return _c +} + +// WatchTokenTransferFeeConfigDeleted provides a mock function with given fields: opts, sink, destChainSelector, token +func (_m *PriceRegistryInterface) WatchTokenTransferFeeConfigDeleted(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryTokenTransferFeeConfigDeleted, destChainSelector []uint64, token []common.Address) (event.Subscription, error) { + ret := _m.Called(opts, sink, destChainSelector, token) + + if len(ret) == 0 { + panic("no return value specified for WatchTokenTransferFeeConfigDeleted") + } + + var r0 event.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryTokenTransferFeeConfigDeleted, []uint64, []common.Address) (event.Subscription, error)); ok { + return rf(opts, sink, destChainSelector, token) + } + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryTokenTransferFeeConfigDeleted, []uint64, []common.Address) event.Subscription); ok { + r0 = rf(opts, sink, destChainSelector, token) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(event.Subscription) + } + } + + if rf, ok := ret.Get(1).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryTokenTransferFeeConfigDeleted, []uint64, []common.Address) error); ok { + r1 = rf(opts, sink, destChainSelector, token) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_WatchTokenTransferFeeConfigDeleted_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WatchTokenTransferFeeConfigDeleted' +type PriceRegistryInterface_WatchTokenTransferFeeConfigDeleted_Call struct { + *mock.Call +} + +// WatchTokenTransferFeeConfigDeleted is a helper method to define mock.On call +// - opts *bind.WatchOpts +// - sink chan<- *price_registry.PriceRegistryTokenTransferFeeConfigDeleted +// - destChainSelector []uint64 +// - token []common.Address +func (_e *PriceRegistryInterface_Expecter) WatchTokenTransferFeeConfigDeleted(opts interface{}, sink interface{}, destChainSelector interface{}, token interface{}) *PriceRegistryInterface_WatchTokenTransferFeeConfigDeleted_Call { + return &PriceRegistryInterface_WatchTokenTransferFeeConfigDeleted_Call{Call: _e.mock.On("WatchTokenTransferFeeConfigDeleted", opts, sink, destChainSelector, token)} +} + +func (_c *PriceRegistryInterface_WatchTokenTransferFeeConfigDeleted_Call) Run(run func(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryTokenTransferFeeConfigDeleted, destChainSelector []uint64, token []common.Address)) *PriceRegistryInterface_WatchTokenTransferFeeConfigDeleted_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.WatchOpts), args[1].(chan<- *price_registry.PriceRegistryTokenTransferFeeConfigDeleted), args[2].([]uint64), args[3].([]common.Address)) + }) + return _c +} + +func (_c *PriceRegistryInterface_WatchTokenTransferFeeConfigDeleted_Call) Return(_a0 event.Subscription, _a1 error) *PriceRegistryInterface_WatchTokenTransferFeeConfigDeleted_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_WatchTokenTransferFeeConfigDeleted_Call) RunAndReturn(run func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryTokenTransferFeeConfigDeleted, []uint64, []common.Address) (event.Subscription, error)) *PriceRegistryInterface_WatchTokenTransferFeeConfigDeleted_Call { + _c.Call.Return(run) + return _c +} + +// WatchTokenTransferFeeConfigUpdated provides a mock function with given fields: opts, sink, destChainSelector, token +func (_m *PriceRegistryInterface) WatchTokenTransferFeeConfigUpdated(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryTokenTransferFeeConfigUpdated, destChainSelector []uint64, token []common.Address) (event.Subscription, error) { + ret := _m.Called(opts, sink, destChainSelector, token) + + if len(ret) == 0 { + panic("no return value specified for WatchTokenTransferFeeConfigUpdated") + } + + var r0 event.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryTokenTransferFeeConfigUpdated, []uint64, []common.Address) (event.Subscription, error)); ok { + return rf(opts, sink, destChainSelector, token) + } + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryTokenTransferFeeConfigUpdated, []uint64, []common.Address) event.Subscription); ok { + r0 = rf(opts, sink, destChainSelector, token) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(event.Subscription) + } + } + + if rf, ok := ret.Get(1).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryTokenTransferFeeConfigUpdated, []uint64, []common.Address) error); ok { + r1 = rf(opts, sink, destChainSelector, token) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_WatchTokenTransferFeeConfigUpdated_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WatchTokenTransferFeeConfigUpdated' +type PriceRegistryInterface_WatchTokenTransferFeeConfigUpdated_Call struct { + *mock.Call +} + +// WatchTokenTransferFeeConfigUpdated is a helper method to define mock.On call +// - opts *bind.WatchOpts +// - sink chan<- *price_registry.PriceRegistryTokenTransferFeeConfigUpdated +// - destChainSelector []uint64 +// - token []common.Address +func (_e *PriceRegistryInterface_Expecter) WatchTokenTransferFeeConfigUpdated(opts interface{}, sink interface{}, destChainSelector interface{}, token interface{}) *PriceRegistryInterface_WatchTokenTransferFeeConfigUpdated_Call { + return &PriceRegistryInterface_WatchTokenTransferFeeConfigUpdated_Call{Call: _e.mock.On("WatchTokenTransferFeeConfigUpdated", opts, sink, destChainSelector, token)} +} + +func (_c *PriceRegistryInterface_WatchTokenTransferFeeConfigUpdated_Call) Run(run func(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryTokenTransferFeeConfigUpdated, destChainSelector []uint64, token []common.Address)) *PriceRegistryInterface_WatchTokenTransferFeeConfigUpdated_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.WatchOpts), args[1].(chan<- *price_registry.PriceRegistryTokenTransferFeeConfigUpdated), args[2].([]uint64), args[3].([]common.Address)) + }) + return _c +} + +func (_c *PriceRegistryInterface_WatchTokenTransferFeeConfigUpdated_Call) Return(_a0 event.Subscription, _a1 error) *PriceRegistryInterface_WatchTokenTransferFeeConfigUpdated_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_WatchTokenTransferFeeConfigUpdated_Call) RunAndReturn(run func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryTokenTransferFeeConfigUpdated, []uint64, []common.Address) (event.Subscription, error)) *PriceRegistryInterface_WatchTokenTransferFeeConfigUpdated_Call { + _c.Call.Return(run) + return _c +} + +// WatchUsdPerTokenUpdated provides a mock function with given fields: opts, sink, token +func (_m *PriceRegistryInterface) WatchUsdPerTokenUpdated(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryUsdPerTokenUpdated, token []common.Address) (event.Subscription, error) { + ret := _m.Called(opts, sink, token) + + if len(ret) == 0 { + panic("no return value specified for WatchUsdPerTokenUpdated") + } + + var r0 event.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryUsdPerTokenUpdated, []common.Address) (event.Subscription, error)); ok { + return rf(opts, sink, token) + } + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryUsdPerTokenUpdated, []common.Address) event.Subscription); ok { + r0 = rf(opts, sink, token) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(event.Subscription) + } + } + + if rf, ok := ret.Get(1).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryUsdPerTokenUpdated, []common.Address) error); ok { + r1 = rf(opts, sink, token) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_WatchUsdPerTokenUpdated_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WatchUsdPerTokenUpdated' +type PriceRegistryInterface_WatchUsdPerTokenUpdated_Call struct { + *mock.Call +} + +// WatchUsdPerTokenUpdated is a helper method to define mock.On call +// - opts *bind.WatchOpts +// - sink chan<- *price_registry.PriceRegistryUsdPerTokenUpdated +// - token []common.Address +func (_e *PriceRegistryInterface_Expecter) WatchUsdPerTokenUpdated(opts interface{}, sink interface{}, token interface{}) *PriceRegistryInterface_WatchUsdPerTokenUpdated_Call { + return &PriceRegistryInterface_WatchUsdPerTokenUpdated_Call{Call: _e.mock.On("WatchUsdPerTokenUpdated", opts, sink, token)} +} + +func (_c *PriceRegistryInterface_WatchUsdPerTokenUpdated_Call) Run(run func(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryUsdPerTokenUpdated, token []common.Address)) *PriceRegistryInterface_WatchUsdPerTokenUpdated_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.WatchOpts), args[1].(chan<- *price_registry.PriceRegistryUsdPerTokenUpdated), args[2].([]common.Address)) + }) + return _c +} + +func (_c *PriceRegistryInterface_WatchUsdPerTokenUpdated_Call) Return(_a0 event.Subscription, _a1 error) *PriceRegistryInterface_WatchUsdPerTokenUpdated_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_WatchUsdPerTokenUpdated_Call) RunAndReturn(run func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryUsdPerTokenUpdated, []common.Address) (event.Subscription, error)) *PriceRegistryInterface_WatchUsdPerTokenUpdated_Call { + _c.Call.Return(run) + return _c +} + +// WatchUsdPerUnitGasUpdated provides a mock function with given fields: opts, sink, destChain +func (_m *PriceRegistryInterface) WatchUsdPerUnitGasUpdated(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryUsdPerUnitGasUpdated, destChain []uint64) (event.Subscription, error) { + ret := _m.Called(opts, sink, destChain) + + if len(ret) == 0 { + panic("no return value specified for WatchUsdPerUnitGasUpdated") + } + + var r0 event.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryUsdPerUnitGasUpdated, []uint64) (event.Subscription, error)); ok { + return rf(opts, sink, destChain) + } + if rf, ok := ret.Get(0).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryUsdPerUnitGasUpdated, []uint64) event.Subscription); ok { + r0 = rf(opts, sink, destChain) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(event.Subscription) + } + } + + if rf, ok := ret.Get(1).(func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryUsdPerUnitGasUpdated, []uint64) error); ok { + r1 = rf(opts, sink, destChain) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PriceRegistryInterface_WatchUsdPerUnitGasUpdated_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'WatchUsdPerUnitGasUpdated' +type PriceRegistryInterface_WatchUsdPerUnitGasUpdated_Call struct { + *mock.Call +} + +// WatchUsdPerUnitGasUpdated is a helper method to define mock.On call +// - opts *bind.WatchOpts +// - sink chan<- *price_registry.PriceRegistryUsdPerUnitGasUpdated +// - destChain []uint64 +func (_e *PriceRegistryInterface_Expecter) WatchUsdPerUnitGasUpdated(opts interface{}, sink interface{}, destChain interface{}) *PriceRegistryInterface_WatchUsdPerUnitGasUpdated_Call { + return &PriceRegistryInterface_WatchUsdPerUnitGasUpdated_Call{Call: _e.mock.On("WatchUsdPerUnitGasUpdated", opts, sink, destChain)} +} + +func (_c *PriceRegistryInterface_WatchUsdPerUnitGasUpdated_Call) Run(run func(opts *bind.WatchOpts, sink chan<- *price_registry.PriceRegistryUsdPerUnitGasUpdated, destChain []uint64)) *PriceRegistryInterface_WatchUsdPerUnitGasUpdated_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.WatchOpts), args[1].(chan<- *price_registry.PriceRegistryUsdPerUnitGasUpdated), args[2].([]uint64)) + }) + return _c +} + +func (_c *PriceRegistryInterface_WatchUsdPerUnitGasUpdated_Call) Return(_a0 event.Subscription, _a1 error) *PriceRegistryInterface_WatchUsdPerUnitGasUpdated_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *PriceRegistryInterface_WatchUsdPerUnitGasUpdated_Call) RunAndReturn(run func(*bind.WatchOpts, chan<- *price_registry.PriceRegistryUsdPerUnitGasUpdated, []uint64) (event.Subscription, error)) *PriceRegistryInterface_WatchUsdPerUnitGasUpdated_Call { + _c.Call.Return(run) + return _c +} + +// NewPriceRegistryInterface creates a new instance of PriceRegistryInterface. 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 NewPriceRegistryInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *PriceRegistryInterface { + mock := &PriceRegistryInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/gethwrappers/generated/arbitrum_module/arbitrum_module.go b/core/gethwrappers/generated/arbitrum_module/arbitrum_module.go index 537fbe2154..ca9b77d563 100644 --- a/core/gethwrappers/generated/arbitrum_module/arbitrum_module.go +++ b/core/gethwrappers/generated/arbitrum_module/arbitrum_module.go @@ -29,8 +29,8 @@ var ( ) 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: "0x608060405234801561001057600080fd5b5061041b806100206000396000f3fe608060405234801561001057600080fd5b50600436106100675760003560e01c806357e871e71161005057806357e871e71461009a57806385df51fd146100a2578063de9ee35e146100b557600080fd5b8063125441401461006c57806318b8f61314610092575b600080fd5b61007f61007a366004610333565b6100cb565b6040519081526020015b60405180910390f35b61007f610158565b61007f6101cf565b61007f6100b0366004610333565b61021d565b6040805161138881526000602082015201610089565b600080606c73ffffffffffffffffffffffffffffffffffffffff166341b247a86040518163ffffffff1660e01b815260040160c060405180830381865afa15801561011a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061013e919061034c565b50505050915050828161015191906103c5565b9392505050565b6000606c73ffffffffffffffffffffffffffffffffffffffff1663c6f7de0e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156101a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101ca91906103e2565b905090565b6000606473ffffffffffffffffffffffffffffffffffffffff1663a3b1b31d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156101a6573d6000803e3d6000fd5b600080606473ffffffffffffffffffffffffffffffffffffffff1663a3b1b31d6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561026c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061029091906103e2565b905080831015806102ab57506101006102a984836103fb565b115b156102b95750600092915050565b6040517f2b407a8200000000000000000000000000000000000000000000000000000000815260048101849052606490632b407a8290602401602060405180830381865afa15801561030f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061015191906103e2565b60006020828403121561034557600080fd5b5035919050565b60008060008060008060c0878903121561036557600080fd5b865195506020870151945060408701519350606087015192506080870151915060a087015190509295509295509295565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820281158282048414176103dc576103dc610396565b92915050565b6000602082840312156103f457600080fd5b5051919050565b818103818111156103dc576103dc61039656fea164736f6c6343000813000a", + 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\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"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: "0x608060405234801561001057600080fd5b5061044a806100206000396000f3fe608060405234801561001057600080fd5b50600436106100675760003560e01c80637810d12a116100505780637810d12a1461009a57806385df51fd146100ad578063de9ee35e146100c057600080fd5b8063125441401461006c57806357e871e714610092575b600080fd5b61007f61007a366004610368565b6100d6565b6040519081526020015b60405180910390f35b61007f610163565b61007f6100a8366004610368565b6101da565b61007f6100bb366004610368565b610252565b6040805161138881526000602082015201610089565b600080606c73ffffffffffffffffffffffffffffffffffffffff166341b247a86040518163ffffffff1660e01b815260040160c060405180830381865afa158015610125573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101499190610381565b50505050915050828161015c91906103fa565b9392505050565b6000606473ffffffffffffffffffffffffffffffffffffffff1663a3b1b31d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156101b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101d59190610411565b905090565b6000606c73ffffffffffffffffffffffffffffffffffffffff1663c6f7de0e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610228573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061024c9190610411565b92915050565b600080606473ffffffffffffffffffffffffffffffffffffffff1663a3b1b31d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102c59190610411565b905080831015806102e057506101006102de848361042a565b115b156102ee5750600092915050565b6040517f2b407a8200000000000000000000000000000000000000000000000000000000815260048101849052606490632b407a8290602401602060405180830381865afa158015610344573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061015c9190610411565b60006020828403121561037a57600080fd5b5035919050565b60008060008060008060c0878903121561039a57600080fd5b865195506020870151945060408701519350606087015192506080870151915060a087015190509295509295509295565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808202811582820484141761024c5761024c6103cb565b60006020828403121561042357600080fd5b5051919050565b8181038181111561024c5761024c6103cb56fea164736f6c6343000813000a", } var ArbitrumModuleABI = ArbitrumModuleMetaData.ABI @@ -213,9 +213,9 @@ func (_ArbitrumModule *ArbitrumModuleCallerSession) BlockNumber() (*big.Int, err return _ArbitrumModule.Contract.BlockNumber(&_ArbitrumModule.CallOpts) } -func (_ArbitrumModule *ArbitrumModuleCaller) GetCurrentL1Fee(opts *bind.CallOpts) (*big.Int, error) { +func (_ArbitrumModule *ArbitrumModuleCaller) GetCurrentL1Fee(opts *bind.CallOpts, arg0 *big.Int) (*big.Int, error) { var out []interface{} - err := _ArbitrumModule.contract.Call(opts, &out, "getCurrentL1Fee") + err := _ArbitrumModule.contract.Call(opts, &out, "getCurrentL1Fee", arg0) if err != nil { return *new(*big.Int), err @@ -227,12 +227,12 @@ func (_ArbitrumModule *ArbitrumModuleCaller) GetCurrentL1Fee(opts *bind.CallOpts } -func (_ArbitrumModule *ArbitrumModuleSession) GetCurrentL1Fee() (*big.Int, error) { - return _ArbitrumModule.Contract.GetCurrentL1Fee(&_ArbitrumModule.CallOpts) +func (_ArbitrumModule *ArbitrumModuleSession) GetCurrentL1Fee(arg0 *big.Int) (*big.Int, error) { + return _ArbitrumModule.Contract.GetCurrentL1Fee(&_ArbitrumModule.CallOpts, arg0) } -func (_ArbitrumModule *ArbitrumModuleCallerSession) GetCurrentL1Fee() (*big.Int, error) { - return _ArbitrumModule.Contract.GetCurrentL1Fee(&_ArbitrumModule.CallOpts) +func (_ArbitrumModule *ArbitrumModuleCallerSession) GetCurrentL1Fee(arg0 *big.Int) (*big.Int, error) { + return _ArbitrumModule.Contract.GetCurrentL1Fee(&_ArbitrumModule.CallOpts, arg0) } func (_ArbitrumModule *ArbitrumModuleCaller) GetGasOverhead(opts *bind.CallOpts) (GetGasOverhead, @@ -301,7 +301,7 @@ type ArbitrumModuleInterface interface { BlockNumber(opts *bind.CallOpts) (*big.Int, error) - GetCurrentL1Fee(opts *bind.CallOpts) (*big.Int, error) + GetCurrentL1Fee(opts *bind.CallOpts, arg0 *big.Int) (*big.Int, error) GetGasOverhead(opts *bind.CallOpts) (GetGasOverhead, diff --git a/core/gethwrappers/generated/automation_registry_logic_b_wrapper_2_3/automation_registry_logic_b_wrapper_2_3.go b/core/gethwrappers/generated/automation_registry_logic_b_wrapper_2_3/automation_registry_logic_b_wrapper_2_3.go index acbf11155d..c2a808d554 100644 --- a/core/gethwrappers/generated/automation_registry_logic_b_wrapper_2_3/automation_registry_logic_b_wrapper_2_3.go +++ b/core/gethwrappers/generated/automation_registry_logic_b_wrapper_2_3/automation_registry_logic_b_wrapper_2_3.go @@ -57,7 +57,7 @@ type AutomationRegistryBase23PaymentReceipt struct { var AutomationRegistryLogicBMetaData = &bind.MetaData{ ABI: "[{\"inputs\":[{\"internalType\":\"contractAutomationRegistryLogicC2_3\",\"name\":\"logicC\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ArrayHasNoEntries\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotCancel\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CheckDataExceedsLimit\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ConfigDigestMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateEntry\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitOutsideRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfFaultyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IndexOutOfRange\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"available\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requested\",\"type\":\"uint256\"}],\"name\":\"InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientLinkLiquidity\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidDataLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidFeed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRecipient\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReport\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTransmitter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTrigger\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTriggerType\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MigrationNotPermitted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MustSettleOffchain\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MustSettleOnchain\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotAContract\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveTransmitters\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByLINKToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrRegistrar\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByUpkeepPrivilegeManager\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyFinanceAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyPausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlySimulatedBackend\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyUnpausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ParameterLengthError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReentrantCall\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RegistryPaused\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedTransmitter\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"reason\",\"type\":\"bytes\"}],\"name\":\"TargetCheckReverted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TranscoderNotSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TransferFailed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepAlreadyExists\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepCancelled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotCanceled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotNeeded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ValueNotChanged\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"AdminPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"gasFeePPB\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"flatFeeMilliCents\",\"type\":\"uint24\"}],\"indexed\":false,\"internalType\":\"structAutomationRegistryBase2_3.BillingOverrides\",\"name\":\"overrides\",\"type\":\"tuple\"}],\"name\":\"BillingConfigOverridden\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"BillingConfigOverrideRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"contractIERC20Metadata\",\"name\":\"token\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"gasFeePPB\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"flatFeeMilliCents\",\"type\":\"uint24\"},{\"internalType\":\"contractAggregatorV3Interface\",\"name\":\"priceFeed\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"decimals\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"fallbackPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint96\",\"name\":\"minSpend\",\"type\":\"uint96\"}],\"indexed\":false,\"internalType\":\"structAutomationRegistryBase2_3.BillingConfig\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"BillingConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"CancelledUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newModule\",\"type\":\"address\"}],\"name\":\"ChainSpecificModuleUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"dedupKey\",\"type\":\"bytes32\"}],\"name\":\"DedupKeyAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"assetAddress\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"FeesWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"FundsAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"FundsWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"InsufficientFundsUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"payees\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"payments\",\"type\":\"uint256[]\"}],\"name\":\"NOPsSettledOffchain\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"payees\",\"type\":\"address[]\"}],\"name\":\"PayeesUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"payee\",\"type\":\"address\"}],\"name\":\"PaymentWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"ReorgedUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"StaleUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"atBlockHeight\",\"type\":\"uint64\"}],\"name\":\"UpkeepCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint96\",\"name\":\"gasChargeInBillingToken\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"premiumInBillingToken\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"gasReimbursementInJuels\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"premiumInJuels\",\"type\":\"uint96\"},{\"internalType\":\"contractIERC20Metadata\",\"name\":\"billingToken\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"linkUSD\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"nativeUSD\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"billingUSD\",\"type\":\"uint96\"}],\"indexed\":false,\"internalType\":\"structAutomationRegistryBase2_3.PaymentReceipt\",\"name\":\"receipt\",\"type\":\"tuple\"}],\"name\":\"UpkeepCharged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"newCheckData\",\"type\":\"bytes\"}],\"name\":\"UpkeepCheckDataSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"gasLimit\",\"type\":\"uint96\"}],\"name\":\"UpkeepGasLimitSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"remainingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"UpkeepMigrated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepOffchainConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepPaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"totalPayment\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasOverhead\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"UpkeepPerformed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"startingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"importedFrom\",\"type\":\"address\"}],\"name\":\"UpkeepReceived\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"performGas\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"}],\"name\":\"UpkeepRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepTriggerConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepUnpaused\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"acceptUpkeepAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"addFunds\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"values\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes\",\"name\":\"extraData\",\"type\":\"bytes\"}],\"name\":\"checkCallback\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"upkeepNeeded\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"},{\"internalType\":\"enumAutomationRegistryBase2_3.UpkeepFailureReason\",\"name\":\"upkeepFailureReason\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"triggerData\",\"type\":\"bytes\"}],\"name\":\"checkUpkeep\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"upkeepNeeded\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"},{\"internalType\":\"enumAutomationRegistryBase2_3.UpkeepFailureReason\",\"name\":\"upkeepFailureReason\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fastGasWei\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"linkUSD\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"checkUpkeep\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"upkeepNeeded\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"},{\"internalType\":\"enumAutomationRegistryBase2_3.UpkeepFailureReason\",\"name\":\"upkeepFailureReason\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fastGasWei\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"linkUSD\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"payload\",\"type\":\"bytes\"}],\"name\":\"executeCallback\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"upkeepNeeded\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"},{\"internalType\":\"enumAutomationRegistryBase2_3.UpkeepFailureReason\",\"name\":\"upkeepFailureReason\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"fallbackTo\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"pauseUpkeep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"removeBillingOverrides\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"gasFeePPB\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"flatFeeMilliCents\",\"type\":\"uint24\"}],\"internalType\":\"structAutomationRegistryBase2_3.BillingOverrides\",\"name\":\"billingOverrides\",\"type\":\"tuple\"}],\"name\":\"setBillingOverrides\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"newCheckData\",\"type\":\"bytes\"}],\"name\":\"setUpkeepCheckData\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"}],\"name\":\"setUpkeepGasLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"name\":\"setUpkeepOffchainConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"}],\"name\":\"setUpkeepTriggerConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"}],\"name\":\"simulatePerformUpkeep\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"proposed\",\"type\":\"address\"}],\"name\":\"transferUpkeepAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"unpauseUpkeep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contractIERC20Metadata\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"withdrawERC20Fees\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"withdrawFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"withdrawLink\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x6101806040523480156200001257600080fd5b5060405162005d2238038062005d2283398101604081905262000035916200062f565b80816001600160a01b031663ca30e6036040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000075573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200009b91906200062f565b826001600160a01b031663226cf83c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620000da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200010091906200062f565b836001600160a01b031663614486af6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200013f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200016591906200062f565b846001600160a01b0316636709d0e56040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001a4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001ca91906200062f565b856001600160a01b0316635425d8ac6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000209573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200022f91906200062f565b866001600160a01b031663a08714c06040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200026e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200029491906200062f565b876001600160a01b031663c5b964e06040518163ffffffff1660e01b8152600401602060405180830381865afa158015620002d3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002f9919062000656565b886001600160a01b031663ac4dc59a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000338573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200035e91906200062f565b3380600081620003b55760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620003e857620003e8816200056b565b5050506001600160a01b0380891660805287811660a05286811660c05285811660e052848116610100528316610120526025805483919060ff19166001838181111562000439576200043962000679565b0217905550806001600160a01b0316610140816001600160a01b03168152505060c0516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200049a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620004c091906200068f565b60ff1660a0516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000504573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200052a91906200068f565b60ff16146200054c576040516301f86e1760e41b815260040160405180910390fd5b5050506001600160a01b039095166101605250620006b4945050505050565b336001600160a01b03821603620005c55760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401620003ac565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6001600160a01b03811681146200062c57600080fd5b50565b6000602082840312156200064257600080fd5b81516200064f8162000616565b9392505050565b6000602082840312156200066957600080fd5b8151600281106200064f57600080fd5b634e487b7160e01b600052602160045260246000fd5b600060208284031215620006a257600080fd5b815160ff811681146200064f57600080fd5b60805160a05160c05160e051610100516101205161014051610160516155e86200073a60003960008181610182015261022f0152600081816120e7015261228d01526000612bb30152600050506000612ee901526000613c5d01526000612fc3015260008181610c4701528181610d0801528181610dd30152612c7401526155e86000f3fe6080604052600436106101805760003560e01c80638765ecbe116100d6578063aed2e9291161007f578063ce7dc5b411610059578063ce7dc5b4146104b1578063f2fde38b146104d1578063f7d334ba146104f157610180565b8063aed2e9291461043a578063b148ab6b14610471578063cd7f71b51461049157610180565b8063948108f7116100b0578063948108f7146103e7578063a72aa27e146103fa578063a86e17811461041a57610180565b80638765ecbe1461037c5780638da5cb5b1461039c5780638dcf0fe7146103c757610180565b806354b7faae1161013857806371fae17f1161011257806371fae17f14610327578063744bfe611461034757806379ba50971461036757610180565b806354b7faae146102b457806368d369d8146102d457806371791aa0146102f457610180565b8063349e8cca11610169578063349e8cca146102205780634ee88d35146102745780635165f2f51461029457610180565b80631a2af011146101c757806329c5efad146101e7575b7f00000000000000000000000000000000000000000000000000000000000000003660008037600080366000845af43d6000803e8080156101c0573d6000f35b3d6000fd5b005b3480156101d357600080fd5b506101c56101e2366004614490565b610511565b3480156101f357600080fd5b50610207610202366004614604565b610617565b6040516102179493929190614723565b60405180910390f35b34801561022c57600080fd5b507f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610217565b34801561028057600080fd5b506101c561028f3660046147a5565b6108f5565b3480156102a057600080fd5b506101c56102af3660046147f1565b610957565b3480156102c057600080fd5b506101c56102cf36600461480a565b610b09565b3480156102e057600080fd5b506101c56102ef366004614836565b610d7c565b34801561030057600080fd5b5061031461030f366004614604565b61102a565b6040516102179796959493929190614877565b34801561033357600080fd5b506101c56103423660046147f1565b61178e565b34801561035357600080fd5b506101c5610362366004614490565b611824565b34801561037357600080fd5b506101c5611c9c565b34801561038857600080fd5b506101c56103973660046147f1565b611d99565b3480156103a857600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff1661024f565b3480156103d357600080fd5b506101c56103e23660046147a5565b611f4e565b6101c56103f53660046148c5565b611fa3565b34801561040657600080fd5b506101c561041536600461490d565b612365565b34801561042657600080fd5b506101c5610435366004614932565b612464565b34801561044657600080fd5b5061045a6104553660046147a5565b612545565b604080519215158352602083019190915201610217565b34801561047d57600080fd5b506101c561048c3660046147f1565b6126f0565b34801561049d57600080fd5b506101c56104ac3660046147a5565b61291d565b3480156104bd57600080fd5b506102076104cc3660046149ac565b6129d4565b3480156104dd57600080fd5b506101c56104ec366004614a8e565b612a96565b3480156104fd57600080fd5b5061031461050c3660046147f1565b612aaa565b61051a82612ae6565b3373ffffffffffffffffffffffffffffffffffffffff821603610569576040517f8c8728c700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526006602052604090205473ffffffffffffffffffffffffffffffffffffffff8281169116146106135760008281526006602052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff851690811790915590519091339185917fb1cbb2c4b8480034c27e06da5f096b8233a8fd4497028593a41ff6df79726b3591a45b5050565b60006060600080610626612b9b565b600086815260046020908152604091829020825161012081018452815460ff8082161515835261010080830490911615159483019490945263ffffffff620100008204811695830195909552660100000000000081048516606083015273ffffffffffffffffffffffffffffffffffffffff6a01000000000000000000009091048116608083015260018301546fffffffffffffffffffffffffffffffff811660a08401526bffffffffffffffffffffffff70010000000000000000000000000000000082041660c08401527c0100000000000000000000000000000000000000000000000000000000900490941660e0820152600290910154909216908201525a9150600080826080015173ffffffffffffffffffffffffffffffffffffffff1663f00e6a2a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561077c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107a09190614abb565b73ffffffffffffffffffffffffffffffffffffffff16601660000160149054906101000a900463ffffffff1663ffffffff16896040516107e09190614ad8565b60006040518083038160008787f1925050503d806000811461081e576040519150601f19603f3d011682016040523d82523d6000602084013e610823565b606091505b50915091505a6108339085614b23565b93508161085c5760006040518060200160405280600081525060079650965096505050506108ec565b808060200190518101906108709190614b8b565b90975095508661089c5760006040518060200160405280600081525060049650965096505050506108ec565b60185486517401000000000000000000000000000000000000000090910463ffffffff1610156108e85760006040518060200160405280600081525060059650965096505050506108ec565b5050505b92959194509250565b6108fe83612ae6565b6000838152601d60205260409020610917828483614c70565b50827f2b72ac786c97e68dbab71023ed6f2bdbfc80ad9bb7808941929229d71b7d5664838360405161094a929190614dd4565b60405180910390a2505050565b61096081612ae6565b600081815260046020908152604091829020825161012081018452815460ff808216151580845261010080840490921615159584019590955262010000820463ffffffff9081169684019690965266010000000000008204861660608401526a010000000000000000000090910473ffffffffffffffffffffffffffffffffffffffff908116608084015260018401546fffffffffffffffffffffffffffffffff811660a085015270010000000000000000000000000000000081046bffffffffffffffffffffffff1660c08501527c0100000000000000000000000000000000000000000000000000000000900490951660e083015260029092015490931690830152610a9a576040517f1b88a78400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082815260046020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055610ad9600283612c0c565b5060405182907f7bada562044eb163f6b4003c4553e4e62825344c0418eea087bed5ee05a4745690600090a25050565b610b11612c21565b73ffffffffffffffffffffffffffffffffffffffff8216610b5e576040517f9c8d2cd200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610b68612c72565b90506000811215610bb4576040517fcf47918100000000000000000000000000000000000000000000000000000000815260006004820152602481018390526044015b60405180910390fd5b80821115610bf8576040517fcf4791810000000000000000000000000000000000000000000000000000000081526004810182905260248101839052604401610bab565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8481166004830152602482018490526000917f00000000000000000000000000000000000000000000000000000000000000009091169063a9059cbb906044016020604051808303816000875af1158015610c92573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cb69190614de8565b905080610cef576040517f90b8ec1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8373ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff167f5e110f8bc8a20b65dcc87f224bdf1cc039346e267118bae2739847f07321ffa885604051610d6e91815260200190565b60405180910390a350505050565b610d84612c21565b73ffffffffffffffffffffffffffffffffffffffff8216610dd1576040517f9c8d2cd200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610e56576040517fc1ab6dc100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610e60612c72565b128015610e835750600060255460ff166001811115610e8157610e816146b9565b145b15610eba576040517f981bb6a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff83166000818152602160205260408082205490517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152919290916370a0823190602401602060405180830381865afa158015610f36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f5a9190614e03565b610f649190614b23565b905080821115610faa576040517fcf4791810000000000000000000000000000000000000000000000000000000081526004810182905260248101839052604401610bab565b610fcb73ffffffffffffffffffffffffffffffffffffffff85168484612d41565b8273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f5e110f8bc8a20b65dcc87f224bdf1cc039346e267118bae2739847f07321ffa884604051610d6e91815260200190565b60006060600080600080600061103e612b9b565b60006110498a612e1a565b905060006014604051806101200160405290816000820160009054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff166bffffffffffffffffffffffff16815260200160008201600c9054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160109054906101000a900462ffffff1662ffffff1662ffffff1681526020016000820160139054906101000a900461ffff1661ffff1661ffff1681526020016000820160159054906101000a900460ff1660ff1660ff1681526020016000820160169054906101000a900460ff161515151581526020016000820160179054906101000a900460ff161515151581526020016000820160189054906101000a900460ff161515151581526020016001820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152505090506000600460008d8152602001908152602001600020604051806101200160405290816000820160009054906101000a900460ff161515151581526020016000820160019054906101000a900460ff161515151581526020016000820160029054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160069054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200160008201600a9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820160009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff1681526020016001820160109054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff166bffffffffffffffffffffffff16815260200160018201601c9054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016002820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152505090506000808360a001511561140c5750506040805160208101825260008082529290910151919a5098506009975089965063ffffffff169450859350839250611782915050565b606083015163ffffffff908116146114565750506040805160208101825260008082529290910151919a5098506001975089965063ffffffff169450859350839250611782915050565b8251156114955750506040805160208101825260008082529290910151919a5098506002975089965063ffffffff169450859350839250611782915050565b61149e84612ec5565b8094508198508299505050506114c38e858786604001518b8b888a61010001516130b7565b9050806bffffffffffffffffffffffff168360c001516bffffffffffffffffffffffff1610156115255750506040805160208101825260008082529290910151919a5098506006975089965063ffffffff169450859350839250611782915050565b505060006115348d858e613427565b90505a9750600080836080015173ffffffffffffffffffffffffffffffffffffffff1663f00e6a2a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561158b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115af9190614abb565b73ffffffffffffffffffffffffffffffffffffffff16601660000160149054906101000a900463ffffffff1663ffffffff16846040516115ef9190614ad8565b60006040518083038160008787f1925050503d806000811461162d576040519150601f19603f3d011682016040523d82523d6000602084013e611632565b606091505b50915091505a611642908b614b23565b9950816116c8576018548151780100000000000000000000000000000000000000000000000090910463ffffffff1610156116a75750506040805160208101825260008082529390910151929b509950600898505063ffffffff169450611782915050565b604090930151929a50600399505063ffffffff909116955061178292505050565b808060200190518101906116dc9190614b8b565b909d509b508c6117165750506040805160208101825260008082529390910151929b509950600498505063ffffffff169450611782915050565b6018548c517401000000000000000000000000000000000000000090910463ffffffff1610156117705750506040805160208101825260008082529390910151929b509950600598505063ffffffff169450611782915050565b5050506040015163ffffffff16945050505b92959891949750929550565b611796613607565b600081815260046020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055602390915280822080547fffffffffffffffffffffffffffffffffffffffffffffffffff000000000000001690555182917f97d0ef3f46a56168af653f547bdb6f77ec2b1d7d9bc6ba0193c2b340ec68064a91a250565b60145477010000000000000000000000000000000000000000000000900460ff161561187c576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601480547fffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffff167701000000000000000000000000000000000000000000000017905573ffffffffffffffffffffffffffffffffffffffff811661190b576040517f9c8d2cd200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000828152600460209081526040808320815161012081018352815460ff8082161515835261010080830490911615158387015263ffffffff620100008304811684870152660100000000000083048116606085015273ffffffffffffffffffffffffffffffffffffffff6a01000000000000000000009093048316608085015260018501546fffffffffffffffffffffffffffffffff811660a08601526bffffffffffffffffffffffff70010000000000000000000000000000000082041660c08601527c010000000000000000000000000000000000000000000000000000000090041660e084015260029093015481169282019290925286855260059093529220549091163314611a4b576040517fa47c170600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601554604080517f57e871e7000000000000000000000000000000000000000000000000000000008152905173ffffffffffffffffffffffffffffffffffffffff909216916357e871e7916004808201926020929091908290030181865afa158015611abb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611adf9190614e03565b816060015163ffffffff161115611b22576040517fff84e5dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526004602090815260408083206001015461010085015173ffffffffffffffffffffffffffffffffffffffff1684526021909252909120547001000000000000000000000000000000009091046bffffffffffffffffffffffff1690611b8d908290614b23565b6101008301805173ffffffffffffffffffffffffffffffffffffffff908116600090815260216020908152604080832095909555888252600490529290922060010180547fffffffff000000000000000000000000ffffffffffffffffffffffffffffffff16905551611c109116846bffffffffffffffffffffffff8416612d41565b604080516bffffffffffffffffffffffff8316815273ffffffffffffffffffffffffffffffffffffffff8516602082015285917ff3b5906e5672f3e524854103bcafbbdba80dbdfeca2c35e116127b1060a68318910160405180910390a25050601480547fffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffff1690555050565b60015473ffffffffffffffffffffffffffffffffffffffff163314611d1d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610bab565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b611da281612ae6565b600081815260046020908152604091829020825161012081018452815460ff808216158015845261010080840490921615159584019590955262010000820463ffffffff9081169684019690965266010000000000008204861660608401526a010000000000000000000090910473ffffffffffffffffffffffffffffffffffffffff908116608084015260018401546fffffffffffffffffffffffffffffffff811660a085015270010000000000000000000000000000000081046bffffffffffffffffffffffff1660c08501527c0100000000000000000000000000000000000000000000000000000000900490951660e083015260029092015490931690830152611edc576040517f514b6c2400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082815260046020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055611f1e600283613658565b5060405182907f8ab10247ce168c27748e656ecf852b951fcaac790c18106b19aa0ae57a8b741f90600090a25050565b611f5783612ae6565b6000838152601e60205260409020611f70828483614c70565b50827f3e8740446213c8a77d40e08f79136ce3f347d13ed270a6ebdf57159e0faf4850838360405161094a929190614dd4565b600082815260046020908152604091829020825161012081018452815460ff8082161515835261010080830490911615159483019490945263ffffffff6201000082048116958301959095526601000000000000810485166060830181905273ffffffffffffffffffffffffffffffffffffffff6a01000000000000000000009092048216608084015260018401546fffffffffffffffffffffffffffffffff811660a08501526bffffffffffffffffffffffff70010000000000000000000000000000000082041660c08501527c01000000000000000000000000000000000000000000000000000000009004861660e084015260029093015416928101929092529091146120df576040517f9c0083a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b341561217b577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1681610100015173ffffffffffffffffffffffffffffffffffffffff161461216f576040517fc1ab6dc100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61217834613664565b91505b818160c0015161218b9190614e1c565b600084815260046020908152604080832060010180547fffffffff000000000000000000000000ffffffffffffffffffffffffffffffff167001000000000000000000000000000000006bffffffffffffffffffffffff9687160217905561010085015173ffffffffffffffffffffffffffffffffffffffff168352602190915290205461221b91841690614e41565b61010082015173ffffffffffffffffffffffffffffffffffffffff1660009081526021602052604081209190915534900361228b576101008101516122869073ffffffffffffffffffffffffffffffffffffffff1633306bffffffffffffffffffffffff8616613706565b61231b565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663d0e30db0836bffffffffffffffffffffffff166040518263ffffffff1660e01b81526004016000604051808303818588803b15801561230157600080fd5b505af1158015612315573d6000803e3d6000fd5b50505050505b6040516bffffffffffffffffffffffff83168152339084907fafd24114486da8ebfc32f3626dada8863652e187461aa74d4bfa7348915062039060200160405180910390a3505050565b6108fc8163ffffffff1610806123a2575060165463ffffffff78010000000000000000000000000000000000000000000000009091048116908216115b156123d9576040517f14c237fb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6123e282612ae6565b60008281526004602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffff166201000063ffffffff861690810291909117909155915191825283917fc24c07e655ce79fba8a589778987d3c015bc6af1632bb20cf9182e02a65d972c91015b60405180910390a25050565b61246c613607565b6000828152600460205260409020546601000000000000900463ffffffff908116146124c4576040517f9c0083a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082815260046020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790556023909152902081906125128282614e65565b905050817fd8a6d79d170a55968079d3a89b960d86b4442aef6aac1d01e644c32b9e38b340826040516124589190614eec565b600080612550612b9b565b601454760100000000000000000000000000000000000000000000900460ff16156125a7576040517f24522f3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600085815260046020908152604091829020825161012081018452815460ff8082161515835261010080830490911615158386015262010000820463ffffffff90811684880181905266010000000000008404821660608601526a010000000000000000000090930473ffffffffffffffffffffffffffffffffffffffff9081166080860181905260018701546fffffffffffffffffffffffffffffffff811660a088015270010000000000000000000000000000000081046bffffffffffffffffffffffff1660c08801527c0100000000000000000000000000000000000000000000000000000000900490921660e0860152600290950154909416908301528451601f890185900485028101850190955287855290936126e393899089908190840183828082843760009201919091525061376a92505050565b9097909650945050505050565b600081815260046020908152604091829020825161012081018452815460ff8082161515835261010080830490911615159483019490945263ffffffff6201000082048116958301959095526601000000000000810485166060830181905273ffffffffffffffffffffffffffffffffffffffff6a01000000000000000000009092048216608084015260018401546fffffffffffffffffffffffffffffffff811660a08501526bffffffffffffffffffffffff70010000000000000000000000000000000082041660c08501527c01000000000000000000000000000000000000000000000000000000009004861660e0840152600290930154169281019290925290911461282c576040517f9c0083a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526006602052604090205473ffffffffffffffffffffffffffffffffffffffff163314612889576040517f6352a85300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526005602090815260408083208054337fffffffffffffffffffffffff0000000000000000000000000000000000000000808316821790935560069094528285208054909216909155905173ffffffffffffffffffffffffffffffffffffffff90911692839186917f5cff4db96bef051785e999f44bfcd21c18823e034fb92dd376e3db4ce0feeb2c91a4505050565b61292683612ae6565b6017547c0100000000000000000000000000000000000000000000000000000000900463ffffffff16811115612988576040517fae7235df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526007602052604090206129a1828483614c70565b50827fcba2d5723b2ee59e53a8e8a82a4a7caf4fdfe70e9f7c582950bf7e7a5c24e83d838360405161094a929190614dd4565b600060606000806000634b56a42e60e01b8888886040516024016129fa93929190614f23565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091529050612a838982610617565b929c919b50995090975095505050505050565b612a9e613985565b612aa781613a06565b50565b600060606000806000806000612acf886040518060200160405280600081525061102a565b959e949d50929b5090995097509550909350915050565b60008181526005602052604090205473ffffffffffffffffffffffffffffffffffffffff163314612b43576040517fa47c170600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000818152600460205260409020546601000000000000900463ffffffff90811614612aa7576040517f9c0083a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614612c0a576040517fb60ac5db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b6000612c188383613afb565b90505b92915050565b60185473ffffffffffffffffffffffffffffffffffffffff163314612c0a576040517fb6dfb7a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166000818152602160205260408082205490517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152919290916370a0823190602401602060405180830381865afa158015612d0e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d329190614e03565b612d3c9190614fb9565b905090565b60405173ffffffffffffffffffffffffffffffffffffffff8316602482015260448101829052612e159084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152613b4a565b505050565b6000818160045b600f811015612ea7577fff000000000000000000000000000000000000000000000000000000000000008216838260208110612e5f57612e5f614fd9565b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614612e9557506000949350505050565b80612e9f81615008565b915050612e21565b5081600f1a6001811115612ebd57612ebd6146b9565b949350505050565b600080600080846040015162ffffff1690506000808263ffffffff161190506000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015612f52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f76919061505a565b5094509092505050600081131580612f8d57508142105b80612fae5750828015612fae5750612fa58242614b23565b8463ffffffff16105b15612fbd576019549650612fc1565b8096505b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa15801561302c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613050919061505a565b509450909250505060008113158061306757508142105b806130885750828015613088575061307f8242614b23565b8463ffffffff16105b1561309757601a54955061309b565b8095505b86866130a68a613c56565b965096509650505050509193909250565b60008080808960018111156130ce576130ce6146b9565b036130dd575062017f98613132565b60018960018111156130f1576130f16146b9565b0361310057506201de84613132565b6040517ff2b2d41200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008a60800151600161314591906150aa565b6131539060ff1660406150c3565b601854613181906103a49074010000000000000000000000000000000000000000900463ffffffff16614e41565b61318b9190614e41565b601554604080517fde9ee35e0000000000000000000000000000000000000000000000000000000081528151939450600093849373ffffffffffffffffffffffffffffffffffffffff169263de9ee35e92600480820193918290030181865afa1580156131fc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061322091906150da565b90925090508183613232836018614e41565b61323c91906150c3565b60808f015161324c9060016150aa565b61325b9060ff166115e06150c3565b6132659190614e41565b61326f9190614e41565b6132799085614e41565b6101008e01516040517f125441400000000000000000000000000000000000000000000000000000000081526004810186905291955073ffffffffffffffffffffffffffffffffffffffff1690631254414090602401602060405180830381865afa1580156132ec573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133109190614e03565b8d6060015161ffff1661332391906150c3565b945050505060006133348b86613d47565b60008d815260046020526040902054909150610100900460ff16156133995760008c81526023602090815260409182902082518084018452905463ffffffff811680835264010000000090910462ffffff908116928401928352928501525116908201525b60006134038c6040518061012001604052808d63ffffffff1681526020018681526020018781526020018c81526020018b81526020018a81526020018973ffffffffffffffffffffffffffffffffffffffff16815260200185815260200160001515815250613ec3565b6020810151815191925061341691614e1c565b9d9c50505050505050505050505050565b6060600083600181111561343d5761343d6146b9565b03613506576000848152600760205260409081902090517f6e04ff0d000000000000000000000000000000000000000000000000000000009161348291602401615199565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091529050613600565b600183600181111561351a5761351a6146b9565b03613100576000828060200190518101906135359190615212565b6000868152600760205260409081902090519192507f40691db4000000000000000000000000000000000000000000000000000000009161357a918491602401615322565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915291506136009050565b9392505050565b60175473ffffffffffffffffffffffffffffffffffffffff163314612c0a576040517f77c3599200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000612c18838361420a565b60006bffffffffffffffffffffffff821115613702576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201527f36206269747300000000000000000000000000000000000000000000000000006064820152608401610bab565b5090565b60405173ffffffffffffffffffffffffffffffffffffffff808516602483015283166044820152606481018290526137649085907f23b872dd0000000000000000000000000000000000000000000000000000000090608401612d93565b50505050565b601454600090819077010000000000000000000000000000000000000000000000900460ff16156137c7576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601480547fffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffff16770100000000000000000000000000000000000000000000001790556040517f4585e33b000000000000000000000000000000000000000000000000000000009061383c9085906024016153ed565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290517f79188d1600000000000000000000000000000000000000000000000000000000815290935073ffffffffffffffffffffffffffffffffffffffff8616906379188d169061390f9087908790600401615400565b60408051808303816000875af115801561392d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139519190615419565b601480547fffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffff16905590969095509350505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314612c0a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610bab565b3373ffffffffffffffffffffffffffffffffffffffff821603613a85576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610bab565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000818152600183016020526040812054613b4257508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155612c1b565b506000612c1b565b6000613bac826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166142fd9092919063ffffffff16565b805190915015612e155780806020019051810190613bca9190614de8565b612e15576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610bab565b60008060007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015613cc6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613cea919061505a565b50935050925050600082131580613d0057508042105b80613d3057506000846040015162ffffff16118015613d305750613d248142614b23565b846040015162ffffff16105b15613d40575050601b5492915050565b5092915050565b60408051608081018252600080825260208083018281528385018381526060850184905273ffffffffffffffffffffffffffffffffffffffff878116855260229093528584208054640100000000810462ffffff1690925263ffffffff82169092527b01000000000000000000000000000000000000000000000000000000810460ff16855285517ffeaf968c00000000000000000000000000000000000000000000000000000000815295519495919484936701000000000000009092049091169163feaf968c9160048083019260a09291908290030181865afa158015613e34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e58919061505a565b50935050925050600082131580613e6e57508042105b80613e9e57506000866040015162ffffff16118015613e9e5750613e928142614b23565b866040015162ffffff16105b15613eb25760018301546060850152613eba565b606084018290525b50505092915050565b6040805161010081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081019190915260008260e001516000015160ff1690506000846060015161ffff168460600151613f2e91906150c3565b90508361010001518015613f415750803a105b15613f4957503a5b600060128311613f5a576001613f70565b613f65601284614b23565b613f7090600a615565565b9050600060128410613f83576001613f99565b613f8e846012614b23565b613f9990600a615565565b905060008660a00151876040015188602001518960000151613fbb9190614e41565b613fc590876150c3565b613fcf9190614e41565b613fd991906150c3565b9050614035828860e0015160600151613ff291906150c3565b6001848a60e001516060015161400891906150c3565b6140129190614b23565b61401c86856150c3565b6140269190614e41565b6140309190615571565b613664565b6bffffffffffffffffffffffff1686526080870151614058906140309083615571565b6bffffffffffffffffffffffff1660408088019190915260e088015101516000906140919062ffffff16683635c9adc5dea000006150c3565b9050600081633b9aca008a60a001518b60e001516020015163ffffffff168c604001518d600001518b6140c491906150c3565b6140ce9190614e41565b6140d891906150c3565b6140e291906150c3565b6140ec9190615571565b6140f69190614e41565b9050614139848a60e001516060015161410f91906150c3565b6001868c60e001516060015161412591906150c3565b61412f9190614b23565b61401c88856150c3565b6bffffffffffffffffffffffff166020890152608089015161415f906140309083615571565b6bffffffffffffffffffffffff16606089015260c089015173ffffffffffffffffffffffffffffffffffffffff166080808a01919091528901516141a290613664565b6bffffffffffffffffffffffff1660a0808a01919091528901516141c590613664565b6bffffffffffffffffffffffff1660c089015260e0890151606001516141ea90613664565b6bffffffffffffffffffffffff1660e08901525050505050505092915050565b600081815260018301602052604081205480156142f357600061422e600183614b23565b855490915060009061424290600190614b23565b90508181146142a757600086600001828154811061426257614262614fd9565b906000526020600020015490508087600001848154811061428557614285614fd9565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806142b8576142b86155ac565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050612c1b565b6000915050612c1b565b6060612ebd8484600085856000808673ffffffffffffffffffffffffffffffffffffffff1685876040516143319190614ad8565b60006040518083038185875af1925050503d806000811461436e576040519150601f19603f3d011682016040523d82523d6000602084013e614373565b606091505b50915091506143848783838761438f565b979650505050505050565b6060831561442557825160000361441e5773ffffffffffffffffffffffffffffffffffffffff85163b61441e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610bab565b5081612ebd565b612ebd838381511561443a5781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bab91906153ed565b73ffffffffffffffffffffffffffffffffffffffff81168114612aa757600080fd5b600080604083850312156144a357600080fd5b8235915060208301356144b58161446e565b809150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610100810167ffffffffffffffff81118282101715614513576145136144c0565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715614560576145606144c0565b604052919050565b600067ffffffffffffffff821115614582576145826144c0565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600082601f8301126145bf57600080fd5b81356145d26145cd82614568565b614519565b8181528460208386010111156145e757600080fd5b816020850160208301376000918101602001919091529392505050565b6000806040838503121561461757600080fd5b82359150602083013567ffffffffffffffff81111561463557600080fd5b614641858286016145ae565b9150509250929050565b60005b8381101561466657818101518382015260200161464e565b50506000910152565b6000815180845261468781602086016020860161464b565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600a811061471f577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b841515815260806020820152600061473e608083018661466f565b905061474d60408301856146e8565b82606083015295945050505050565b60008083601f84011261476e57600080fd5b50813567ffffffffffffffff81111561478657600080fd5b60208301915083602082850101111561479e57600080fd5b9250929050565b6000806000604084860312156147ba57600080fd5b83359250602084013567ffffffffffffffff8111156147d857600080fd5b6147e48682870161475c565b9497909650939450505050565b60006020828403121561480357600080fd5b5035919050565b6000806040838503121561481d57600080fd5b82356148288161446e565b946020939093013593505050565b60008060006060848603121561484b57600080fd5b83356148568161446e565b925060208401356148668161446e565b929592945050506040919091013590565b871515815260e06020820152600061489260e083018961466f565b90506148a160408301886146e8565b8560608301528460808301528360a08301528260c083015298975050505050505050565b600080604083850312156148d857600080fd5b8235915060208301356bffffffffffffffffffffffff811681146144b557600080fd5b63ffffffff81168114612aa757600080fd5b6000806040838503121561492057600080fd5b8235915060208301356144b5816148fb565b600080828403606081121561494657600080fd5b8335925060407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08201121561497a57600080fd5b506020830190509250929050565b600067ffffffffffffffff8211156149a2576149a26144c0565b5060051b60200190565b600080600080606085870312156149c257600080fd5b8435935060208086013567ffffffffffffffff808211156149e257600080fd5b818801915088601f8301126149f657600080fd5b8135614a046145cd82614988565b81815260059190911b8301840190848101908b831115614a2357600080fd5b8585015b83811015614a5b57803585811115614a3f5760008081fd5b614a4d8e89838a01016145ae565b845250918601918601614a27565b50975050506040880135925080831115614a7457600080fd5b5050614a828782880161475c565b95989497509550505050565b600060208284031215614aa057600080fd5b81356136008161446e565b8051614ab68161446e565b919050565b600060208284031215614acd57600080fd5b81516136008161446e565b60008251614aea81846020870161464b565b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115612c1b57612c1b614af4565b80518015158114614ab657600080fd5b600082601f830112614b5757600080fd5b8151614b656145cd82614568565b818152846020838601011115614b7a57600080fd5b612ebd82602083016020870161464b565b60008060408385031215614b9e57600080fd5b614ba783614b36565b9150602083015167ffffffffffffffff811115614bc357600080fd5b61464185828601614b46565b600181811c90821680614be357607f821691505b602082108103614c1c577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f821115612e1557600081815260208120601f850160051c81016020861015614c495750805b601f850160051c820191505b81811015614c6857828155600101614c55565b505050505050565b67ffffffffffffffff831115614c8857614c886144c0565b614c9c83614c968354614bcf565b83614c22565b6000601f841160018114614cee5760008515614cb85750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355614d84565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b82811015614d3d5786850135825560209485019460019092019101614d1d565b5086821015614d78577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b602081526000612ebd602083018486614d8b565b600060208284031215614dfa57600080fd5b612c1882614b36565b600060208284031215614e1557600080fd5b5051919050565b6bffffffffffffffffffffffff818116838216019080821115613d4057613d40614af4565b80820180821115612c1b57612c1b614af4565b62ffffff81168114612aa757600080fd5b8135614e70816148fb565b63ffffffff811690508154817fffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000082161783556020840135614eb081614e54565b66ffffff000000008160201b16837fffffffffffffffffffffffffffffffffffffffffffffffffff000000000000008416171784555050505050565b604081018235614efb816148fb565b63ffffffff1682526020830135614f1181614e54565b62ffffff811660208401525092915050565b6000604082016040835280865180835260608501915060608160051b8601019250602080890160005b83811015614f98577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0888703018552614f8686835161466f565b95509382019390820190600101614f4c565b505085840381870152505050614faf818587614d8b565b9695505050505050565b8181036000831280158383131683831282161715613d4057613d40614af4565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361503957615039614af4565b5060010190565b805169ffffffffffffffffffff81168114614ab657600080fd5b600080600080600060a0868803121561507257600080fd5b61507b86615040565b945060208601519350604086015192506060860151915061509e60808701615040565b90509295509295909350565b60ff8181168382160190811115612c1b57612c1b614af4565b8082028115828204841417612c1b57612c1b614af4565b600080604083850312156150ed57600080fd5b505080516020909101519092909150565b6000815461510b81614bcf565b80855260206001838116801561512857600181146151605761518e565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008516838901528284151560051b890101955061518e565b866000528260002060005b858110156151865781548a820186015290830190840161516b565b890184019650505b505050505092915050565b602081526000612c1860208301846150fe565b600082601f8301126151bd57600080fd5b815160206151cd6145cd83614988565b82815260059290921b840181019181810190868411156151ec57600080fd5b8286015b8481101561520757805183529183019183016151f0565b509695505050505050565b60006020828403121561522457600080fd5b815167ffffffffffffffff8082111561523c57600080fd5b90830190610100828603121561525157600080fd5b6152596144ef565b825181526020830151602082015260408301516040820152606083015160608201526080830151608082015261529160a08401614aab565b60a082015260c0830151828111156152a857600080fd5b6152b4878286016151ac565b60c08301525060e0830151828111156152cc57600080fd5b6152d887828601614b46565b60e08301525095945050505050565b600081518084526020808501945080840160005b83811015615317578151875295820195908201906001016152fb565b509495945050505050565b60408152825160408201526020830151606082015260408301516080820152606083015160a0820152608083015160c082015273ffffffffffffffffffffffffffffffffffffffff60a08401511660e0820152600060c08401516101008081850152506153936101408401826152e7565b905060e08501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0848303016101208501526153cf828261466f565b91505082810360208401526153e481856150fe565b95945050505050565b602081526000612c18602083018461466f565b828152604060208201526000612ebd604083018461466f565b6000806040838503121561542c57600080fd5b61543583614b36565b9150602083015190509250929050565b600181815b8085111561549e57817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111561548457615484614af4565b8085161561549157918102915b93841c939080029061544a565b509250929050565b6000826154b557506001612c1b565b816154c257506000612c1b565b81600181146154d857600281146154e2576154fe565b6001915050612c1b565b60ff8411156154f3576154f3614af4565b50506001821b612c1b565b5060208310610133831016604e8410600b8410161715615521575081810a612c1b565b61552b8383615445565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111561555d5761555d614af4565b029392505050565b6000612c1883836154a6565b6000826155a7577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfea164736f6c6343000813000a", + Bin: "0x6101806040523480156200001257600080fd5b5060405162005d2238038062005d2283398101604081905262000035916200062f565b80816001600160a01b031663ca30e6036040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000075573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200009b91906200062f565b826001600160a01b031663226cf83c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620000da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200010091906200062f565b836001600160a01b031663614486af6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200013f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200016591906200062f565b846001600160a01b0316636709d0e56040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001a4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001ca91906200062f565b856001600160a01b0316635425d8ac6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000209573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200022f91906200062f565b866001600160a01b031663a08714c06040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200026e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200029491906200062f565b876001600160a01b031663c5b964e06040518163ffffffff1660e01b8152600401602060405180830381865afa158015620002d3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002f9919062000656565b886001600160a01b031663ac4dc59a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000338573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200035e91906200062f565b3380600081620003b55760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620003e857620003e8816200056b565b5050506001600160a01b0380891660805287811660a05286811660c05285811660e052848116610100528316610120526025805483919060ff19166001838181111562000439576200043962000679565b0217905550806001600160a01b0316610140816001600160a01b03168152505060c0516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200049a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620004c091906200068f565b60ff1660a0516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000504573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200052a91906200068f565b60ff16146200054c576040516301f86e1760e41b815260040160405180910390fd5b5050506001600160a01b039095166101605250620006b4945050505050565b336001600160a01b03821603620005c55760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401620003ac565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6001600160a01b03811681146200062c57600080fd5b50565b6000602082840312156200064257600080fd5b81516200064f8162000616565b9392505050565b6000602082840312156200066957600080fd5b8151600281106200064f57600080fd5b634e487b7160e01b600052602160045260246000fd5b600060208284031215620006a257600080fd5b815160ff811681146200064f57600080fd5b60805160a05160c05160e051610100516101205161014051610160516155e86200073a60003960008181610182015261022f0152600081816120e7015261228d01526000612bb30152600050506000612ee901526000613c5d01526000612fc3015260008181610c4701528181610d0801528181610dd30152612c7401526155e86000f3fe6080604052600436106101805760003560e01c80638765ecbe116100d6578063aed2e9291161007f578063ce7dc5b411610059578063ce7dc5b4146104b1578063f2fde38b146104d1578063f7d334ba146104f157610180565b8063aed2e9291461043a578063b148ab6b14610471578063cd7f71b51461049157610180565b8063948108f7116100b0578063948108f7146103e7578063a72aa27e146103fa578063a86e17811461041a57610180565b80638765ecbe1461037c5780638da5cb5b1461039c5780638dcf0fe7146103c757610180565b806354b7faae1161013857806371fae17f1161011257806371fae17f14610327578063744bfe611461034757806379ba50971461036757610180565b806354b7faae146102b457806368d369d8146102d457806371791aa0146102f457610180565b8063349e8cca11610169578063349e8cca146102205780634ee88d35146102745780635165f2f51461029457610180565b80631a2af011146101c757806329c5efad146101e7575b7f00000000000000000000000000000000000000000000000000000000000000003660008037600080366000845af43d6000803e8080156101c0573d6000f35b3d6000fd5b005b3480156101d357600080fd5b506101c56101e2366004614490565b610511565b3480156101f357600080fd5b50610207610202366004614604565b610617565b6040516102179493929190614723565b60405180910390f35b34801561022c57600080fd5b507f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610217565b34801561028057600080fd5b506101c561028f3660046147a5565b6108f5565b3480156102a057600080fd5b506101c56102af3660046147f1565b610957565b3480156102c057600080fd5b506101c56102cf36600461480a565b610b09565b3480156102e057600080fd5b506101c56102ef366004614836565b610d7c565b34801561030057600080fd5b5061031461030f366004614604565b61102a565b6040516102179796959493929190614877565b34801561033357600080fd5b506101c56103423660046147f1565b61178e565b34801561035357600080fd5b506101c5610362366004614490565b611824565b34801561037357600080fd5b506101c5611c9c565b34801561038857600080fd5b506101c56103973660046147f1565b611d99565b3480156103a857600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff1661024f565b3480156103d357600080fd5b506101c56103e23660046147a5565b611f4e565b6101c56103f53660046148c5565b611fa3565b34801561040657600080fd5b506101c561041536600461490d565b612365565b34801561042657600080fd5b506101c5610435366004614932565b612464565b34801561044657600080fd5b5061045a6104553660046147a5565b612545565b604080519215158352602083019190915201610217565b34801561047d57600080fd5b506101c561048c3660046147f1565b6126f0565b34801561049d57600080fd5b506101c56104ac3660046147a5565b61291d565b3480156104bd57600080fd5b506102076104cc3660046149ac565b6129d4565b3480156104dd57600080fd5b506101c56104ec366004614a8e565b612a96565b3480156104fd57600080fd5b5061031461050c3660046147f1565b612aaa565b61051a82612ae6565b3373ffffffffffffffffffffffffffffffffffffffff821603610569576040517f8c8728c700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526006602052604090205473ffffffffffffffffffffffffffffffffffffffff8281169116146106135760008281526006602052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff851690811790915590519091339185917fb1cbb2c4b8480034c27e06da5f096b8233a8fd4497028593a41ff6df79726b3591a45b5050565b60006060600080610626612b9b565b600086815260046020908152604091829020825161012081018452815460ff8082161515835261010080830490911615159483019490945263ffffffff620100008204811695830195909552660100000000000081048516606083015273ffffffffffffffffffffffffffffffffffffffff6a01000000000000000000009091048116608083015260018301546fffffffffffffffffffffffffffffffff811660a08401526bffffffffffffffffffffffff70010000000000000000000000000000000082041660c08401527c0100000000000000000000000000000000000000000000000000000000900490941660e0820152600290910154909216908201525a9150600080826080015173ffffffffffffffffffffffffffffffffffffffff1663f00e6a2a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561077c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107a09190614abb565b73ffffffffffffffffffffffffffffffffffffffff16601660000160149054906101000a900463ffffffff1663ffffffff16896040516107e09190614ad8565b60006040518083038160008787f1925050503d806000811461081e576040519150601f19603f3d011682016040523d82523d6000602084013e610823565b606091505b50915091505a6108339085614b23565b93508161085c5760006040518060200160405280600081525060079650965096505050506108ec565b808060200190518101906108709190614b8b565b90975095508661089c5760006040518060200160405280600081525060049650965096505050506108ec565b60185486517401000000000000000000000000000000000000000090910463ffffffff1610156108e85760006040518060200160405280600081525060059650965096505050506108ec565b5050505b92959194509250565b6108fe83612ae6565b6000838152601d60205260409020610917828483614c70565b50827f2b72ac786c97e68dbab71023ed6f2bdbfc80ad9bb7808941929229d71b7d5664838360405161094a929190614dd4565b60405180910390a2505050565b61096081612ae6565b600081815260046020908152604091829020825161012081018452815460ff808216151580845261010080840490921615159584019590955262010000820463ffffffff9081169684019690965266010000000000008204861660608401526a010000000000000000000090910473ffffffffffffffffffffffffffffffffffffffff908116608084015260018401546fffffffffffffffffffffffffffffffff811660a085015270010000000000000000000000000000000081046bffffffffffffffffffffffff1660c08501527c0100000000000000000000000000000000000000000000000000000000900490951660e083015260029092015490931690830152610a9a576040517f1b88a78400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082815260046020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055610ad9600283612c0c565b5060405182907f7bada562044eb163f6b4003c4553e4e62825344c0418eea087bed5ee05a4745690600090a25050565b610b11612c21565b73ffffffffffffffffffffffffffffffffffffffff8216610b5e576040517f9c8d2cd200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610b68612c72565b90506000811215610bb4576040517fcf47918100000000000000000000000000000000000000000000000000000000815260006004820152602481018390526044015b60405180910390fd5b80821115610bf8576040517fcf4791810000000000000000000000000000000000000000000000000000000081526004810182905260248101839052604401610bab565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8481166004830152602482018490526000917f00000000000000000000000000000000000000000000000000000000000000009091169063a9059cbb906044016020604051808303816000875af1158015610c92573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cb69190614de8565b905080610cef576040517f90b8ec1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8373ffffffffffffffffffffffffffffffffffffffff167f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff167f5e110f8bc8a20b65dcc87f224bdf1cc039346e267118bae2739847f07321ffa885604051610d6e91815260200190565b60405180910390a350505050565b610d84612c21565b73ffffffffffffffffffffffffffffffffffffffff8216610dd1576040517f9c8d2cd200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610e56576040517fc1ab6dc100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610e60612c72565b128015610e835750600060255460ff166001811115610e8157610e816146b9565b145b15610eba576040517f981bb6a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff83166000818152602160205260408082205490517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152919290916370a0823190602401602060405180830381865afa158015610f36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f5a9190614e03565b610f649190614b23565b905080821115610faa576040517fcf4791810000000000000000000000000000000000000000000000000000000081526004810182905260248101839052604401610bab565b610fcb73ffffffffffffffffffffffffffffffffffffffff85168484612d41565b8273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f5e110f8bc8a20b65dcc87f224bdf1cc039346e267118bae2739847f07321ffa884604051610d6e91815260200190565b60006060600080600080600061103e612b9b565b60006110498a612e1a565b905060006014604051806101200160405290816000820160009054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff166bffffffffffffffffffffffff16815260200160008201600c9054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160109054906101000a900462ffffff1662ffffff1662ffffff1681526020016000820160139054906101000a900461ffff1661ffff1661ffff1681526020016000820160159054906101000a900460ff1660ff1660ff1681526020016000820160169054906101000a900460ff161515151581526020016000820160179054906101000a900460ff161515151581526020016000820160189054906101000a900460ff161515151581526020016001820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152505090506000600460008d8152602001908152602001600020604051806101200160405290816000820160009054906101000a900460ff161515151581526020016000820160019054906101000a900460ff161515151581526020016000820160029054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160069054906101000a900463ffffffff1663ffffffff1663ffffffff16815260200160008201600a9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020016001820160009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff1681526020016001820160109054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff166bffffffffffffffffffffffff16815260200160018201601c9054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016002820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152505090506000808360a001511561140c5750506040805160208101825260008082529290910151919a5098506009975089965063ffffffff169450859350839250611782915050565b606083015163ffffffff908116146114565750506040805160208101825260008082529290910151919a5098506001975089965063ffffffff169450859350839250611782915050565b8251156114955750506040805160208101825260008082529290910151919a5098506002975089965063ffffffff169450859350839250611782915050565b61149e84612ec5565b8094508198508299505050506114c38e858786604001518b8b888a61010001516130b7565b9050806bffffffffffffffffffffffff168360c001516bffffffffffffffffffffffff1610156115255750506040805160208101825260008082529290910151919a5098506006975089965063ffffffff169450859350839250611782915050565b505060006115348d858e613427565b90505a9750600080836080015173ffffffffffffffffffffffffffffffffffffffff1663f00e6a2a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561158b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115af9190614abb565b73ffffffffffffffffffffffffffffffffffffffff16601660000160149054906101000a900463ffffffff1663ffffffff16846040516115ef9190614ad8565b60006040518083038160008787f1925050503d806000811461162d576040519150601f19603f3d011682016040523d82523d6000602084013e611632565b606091505b50915091505a611642908b614b23565b9950816116c8576018548151780100000000000000000000000000000000000000000000000090910463ffffffff1610156116a75750506040805160208101825260008082529390910151929b509950600898505063ffffffff169450611782915050565b604090930151929a50600399505063ffffffff909116955061178292505050565b808060200190518101906116dc9190614b8b565b909d509b508c6117165750506040805160208101825260008082529390910151929b509950600498505063ffffffff169450611782915050565b6018548c517401000000000000000000000000000000000000000090910463ffffffff1610156117705750506040805160208101825260008082529390910151929b509950600598505063ffffffff169450611782915050565b5050506040015163ffffffff16945050505b92959891949750929550565b611796613607565b600081815260046020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055602390915280822080547fffffffffffffffffffffffffffffffffffffffffffffffffff000000000000001690555182917f97d0ef3f46a56168af653f547bdb6f77ec2b1d7d9bc6ba0193c2b340ec68064a91a250565b60145477010000000000000000000000000000000000000000000000900460ff161561187c576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601480547fffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffff167701000000000000000000000000000000000000000000000017905573ffffffffffffffffffffffffffffffffffffffff811661190b576040517f9c8d2cd200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000828152600460209081526040808320815161012081018352815460ff8082161515835261010080830490911615158387015263ffffffff620100008304811684870152660100000000000083048116606085015273ffffffffffffffffffffffffffffffffffffffff6a01000000000000000000009093048316608085015260018501546fffffffffffffffffffffffffffffffff811660a08601526bffffffffffffffffffffffff70010000000000000000000000000000000082041660c08601527c010000000000000000000000000000000000000000000000000000000090041660e084015260029093015481169282019290925286855260059093529220549091163314611a4b576040517fa47c170600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601554604080517f57e871e7000000000000000000000000000000000000000000000000000000008152905173ffffffffffffffffffffffffffffffffffffffff909216916357e871e7916004808201926020929091908290030181865afa158015611abb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611adf9190614e03565b816060015163ffffffff161115611b22576040517fff84e5dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526004602090815260408083206001015461010085015173ffffffffffffffffffffffffffffffffffffffff1684526021909252909120547001000000000000000000000000000000009091046bffffffffffffffffffffffff1690611b8d908290614b23565b6101008301805173ffffffffffffffffffffffffffffffffffffffff908116600090815260216020908152604080832095909555888252600490529290922060010180547fffffffff000000000000000000000000ffffffffffffffffffffffffffffffff16905551611c109116846bffffffffffffffffffffffff8416612d41565b604080516bffffffffffffffffffffffff8316815273ffffffffffffffffffffffffffffffffffffffff8516602082015285917ff3b5906e5672f3e524854103bcafbbdba80dbdfeca2c35e116127b1060a68318910160405180910390a25050601480547fffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffff1690555050565b60015473ffffffffffffffffffffffffffffffffffffffff163314611d1d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610bab565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b611da281612ae6565b600081815260046020908152604091829020825161012081018452815460ff808216158015845261010080840490921615159584019590955262010000820463ffffffff9081169684019690965266010000000000008204861660608401526a010000000000000000000090910473ffffffffffffffffffffffffffffffffffffffff908116608084015260018401546fffffffffffffffffffffffffffffffff811660a085015270010000000000000000000000000000000081046bffffffffffffffffffffffff1660c08501527c0100000000000000000000000000000000000000000000000000000000900490951660e083015260029092015490931690830152611edc576040517f514b6c2400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082815260046020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055611f1e600283613658565b5060405182907f8ab10247ce168c27748e656ecf852b951fcaac790c18106b19aa0ae57a8b741f90600090a25050565b611f5783612ae6565b6000838152601e60205260409020611f70828483614c70565b50827f3e8740446213c8a77d40e08f79136ce3f347d13ed270a6ebdf57159e0faf4850838360405161094a929190614dd4565b600082815260046020908152604091829020825161012081018452815460ff8082161515835261010080830490911615159483019490945263ffffffff6201000082048116958301959095526601000000000000810485166060830181905273ffffffffffffffffffffffffffffffffffffffff6a01000000000000000000009092048216608084015260018401546fffffffffffffffffffffffffffffffff811660a08501526bffffffffffffffffffffffff70010000000000000000000000000000000082041660c08501527c01000000000000000000000000000000000000000000000000000000009004861660e084015260029093015416928101929092529091146120df576040517f9c0083a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b341561217b577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1681610100015173ffffffffffffffffffffffffffffffffffffffff161461216f576040517fc1ab6dc100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61217834613664565b91505b818160c0015161218b9190614e1c565b600084815260046020908152604080832060010180547fffffffff000000000000000000000000ffffffffffffffffffffffffffffffff167001000000000000000000000000000000006bffffffffffffffffffffffff9687160217905561010085015173ffffffffffffffffffffffffffffffffffffffff168352602190915290205461221b91841690614e41565b61010082015173ffffffffffffffffffffffffffffffffffffffff1660009081526021602052604081209190915534900361228b576101008101516122869073ffffffffffffffffffffffffffffffffffffffff1633306bffffffffffffffffffffffff8616613706565b61231b565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663d0e30db0836bffffffffffffffffffffffff166040518263ffffffff1660e01b81526004016000604051808303818588803b15801561230157600080fd5b505af1158015612315573d6000803e3d6000fd5b50505050505b6040516bffffffffffffffffffffffff83168152339084907fafd24114486da8ebfc32f3626dada8863652e187461aa74d4bfa7348915062039060200160405180910390a3505050565b6108fc8163ffffffff1610806123a2575060165463ffffffff78010000000000000000000000000000000000000000000000009091048116908216115b156123d9576040517f14c237fb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6123e282612ae6565b60008281526004602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffff166201000063ffffffff861690810291909117909155915191825283917fc24c07e655ce79fba8a589778987d3c015bc6af1632bb20cf9182e02a65d972c91015b60405180910390a25050565b61246c613607565b6000828152600460205260409020546601000000000000900463ffffffff908116146124c4576040517f9c0083a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082815260046020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790556023909152902081906125128282614e65565b905050817fd8a6d79d170a55968079d3a89b960d86b4442aef6aac1d01e644c32b9e38b340826040516124589190614eec565b600080612550612b9b565b601454760100000000000000000000000000000000000000000000900460ff16156125a7576040517f24522f3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600085815260046020908152604091829020825161012081018452815460ff8082161515835261010080830490911615158386015262010000820463ffffffff90811684880181905266010000000000008404821660608601526a010000000000000000000090930473ffffffffffffffffffffffffffffffffffffffff9081166080860181905260018701546fffffffffffffffffffffffffffffffff811660a088015270010000000000000000000000000000000081046bffffffffffffffffffffffff1660c08801527c0100000000000000000000000000000000000000000000000000000000900490921660e0860152600290950154909416908301528451601f890185900485028101850190955287855290936126e393899089908190840183828082843760009201919091525061376a92505050565b9097909650945050505050565b600081815260046020908152604091829020825161012081018452815460ff8082161515835261010080830490911615159483019490945263ffffffff6201000082048116958301959095526601000000000000810485166060830181905273ffffffffffffffffffffffffffffffffffffffff6a01000000000000000000009092048216608084015260018401546fffffffffffffffffffffffffffffffff811660a08501526bffffffffffffffffffffffff70010000000000000000000000000000000082041660c08501527c01000000000000000000000000000000000000000000000000000000009004861660e0840152600290930154169281019290925290911461282c576040517f9c0083a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526006602052604090205473ffffffffffffffffffffffffffffffffffffffff163314612889576040517f6352a85300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008281526005602090815260408083208054337fffffffffffffffffffffffff0000000000000000000000000000000000000000808316821790935560069094528285208054909216909155905173ffffffffffffffffffffffffffffffffffffffff90911692839186917f5cff4db96bef051785e999f44bfcd21c18823e034fb92dd376e3db4ce0feeb2c91a4505050565b61292683612ae6565b6017547c0100000000000000000000000000000000000000000000000000000000900463ffffffff16811115612988576040517fae7235df00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526007602052604090206129a1828483614c70565b50827fcba2d5723b2ee59e53a8e8a82a4a7caf4fdfe70e9f7c582950bf7e7a5c24e83d838360405161094a929190614dd4565b600060606000806000634b56a42e60e01b8888886040516024016129fa93929190614f23565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091529050612a838982610617565b929c919b50995090975095505050505050565b612a9e613985565b612aa781613a06565b50565b600060606000806000806000612acf886040518060200160405280600081525061102a565b959e949d50929b5090995097509550909350915050565b60008181526005602052604090205473ffffffffffffffffffffffffffffffffffffffff163314612b43576040517fa47c170600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000818152600460205260409020546601000000000000900463ffffffff90811614612aa7576040517f9c0083a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614612c0a576040517fb60ac5db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b6000612c188383613afb565b90505b92915050565b60185473ffffffffffffffffffffffffffffffffffffffff163314612c0a576040517fb6dfb7a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166000818152602160205260408082205490517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152919290916370a0823190602401602060405180830381865afa158015612d0e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d329190614e03565b612d3c9190614fb9565b905090565b60405173ffffffffffffffffffffffffffffffffffffffff8316602482015260448101829052612e159084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152613b4a565b505050565b6000818160045b600f811015612ea7577fff000000000000000000000000000000000000000000000000000000000000008216838260208110612e5f57612e5f614fd9565b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614612e9557506000949350505050565b80612e9f81615008565b915050612e21565b5081600f1a6001811115612ebd57612ebd6146b9565b949350505050565b600080600080846040015162ffffff1690506000808263ffffffff161190506000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015612f52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f76919061505a565b5094509092505050600081131580612f8d57508142105b80612fae5750828015612fae5750612fa58242614b23565b8463ffffffff16105b15612fbd576019549650612fc1565b8096505b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa15801561302c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613050919061505a565b509450909250505060008113158061306757508142105b806130885750828015613088575061307f8242614b23565b8463ffffffff16105b1561309757601a54955061309b565b8095505b86866130a68a613c56565b965096509650505050509193909250565b60008080808960018111156130ce576130ce6146b9565b036130dd575062017f98613132565b60018960018111156130f1576130f16146b9565b0361310057506201e26c613132565b6040517ff2b2d41200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008a60800151600161314591906150aa565b6131539060ff1660406150c3565b601854613181906103a49074010000000000000000000000000000000000000000900463ffffffff16614e41565b61318b9190614e41565b601554604080517fde9ee35e0000000000000000000000000000000000000000000000000000000081528151939450600093849373ffffffffffffffffffffffffffffffffffffffff169263de9ee35e92600480820193918290030181865afa1580156131fc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061322091906150da565b90925090508183613232836018614e41565b61323c91906150c3565b60808f015161324c9060016150aa565b61325b9060ff166115e06150c3565b6132659190614e41565b61326f9190614e41565b6132799085614e41565b6101008e01516040517f125441400000000000000000000000000000000000000000000000000000000081526004810186905291955073ffffffffffffffffffffffffffffffffffffffff1690631254414090602401602060405180830381865afa1580156132ec573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133109190614e03565b8d6060015161ffff1661332391906150c3565b945050505060006133348b86613d47565b60008d815260046020526040902054909150610100900460ff16156133995760008c81526023602090815260409182902082518084018452905463ffffffff811680835264010000000090910462ffffff908116928401928352928501525116908201525b60006134038c6040518061012001604052808d63ffffffff1681526020018681526020018781526020018c81526020018b81526020018a81526020018973ffffffffffffffffffffffffffffffffffffffff16815260200185815260200160001515815250613ec3565b6020810151815191925061341691614e1c565b9d9c50505050505050505050505050565b6060600083600181111561343d5761343d6146b9565b03613506576000848152600760205260409081902090517f6e04ff0d000000000000000000000000000000000000000000000000000000009161348291602401615199565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091529050613600565b600183600181111561351a5761351a6146b9565b03613100576000828060200190518101906135359190615212565b6000868152600760205260409081902090519192507f40691db4000000000000000000000000000000000000000000000000000000009161357a918491602401615322565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915291506136009050565b9392505050565b60175473ffffffffffffffffffffffffffffffffffffffff163314612c0a576040517f77c3599200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000612c18838361420a565b60006bffffffffffffffffffffffff821115613702576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201527f36206269747300000000000000000000000000000000000000000000000000006064820152608401610bab565b5090565b60405173ffffffffffffffffffffffffffffffffffffffff808516602483015283166044820152606481018290526137649085907f23b872dd0000000000000000000000000000000000000000000000000000000090608401612d93565b50505050565b601454600090819077010000000000000000000000000000000000000000000000900460ff16156137c7576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601480547fffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffff16770100000000000000000000000000000000000000000000001790556040517f4585e33b000000000000000000000000000000000000000000000000000000009061383c9085906024016153ed565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290517f79188d1600000000000000000000000000000000000000000000000000000000815290935073ffffffffffffffffffffffffffffffffffffffff8616906379188d169061390f9087908790600401615400565b60408051808303816000875af115801561392d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139519190615419565b601480547fffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffff16905590969095509350505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314612c0a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610bab565b3373ffffffffffffffffffffffffffffffffffffffff821603613a85576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610bab565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000818152600183016020526040812054613b4257508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155612c1b565b506000612c1b565b6000613bac826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166142fd9092919063ffffffff16565b805190915015612e155780806020019051810190613bca9190614de8565b612e15576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610bab565b60008060007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015613cc6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613cea919061505a565b50935050925050600082131580613d0057508042105b80613d3057506000846040015162ffffff16118015613d305750613d248142614b23565b846040015162ffffff16105b15613d40575050601b5492915050565b5092915050565b60408051608081018252600080825260208083018281528385018381526060850184905273ffffffffffffffffffffffffffffffffffffffff878116855260229093528584208054640100000000810462ffffff1690925263ffffffff82169092527b01000000000000000000000000000000000000000000000000000000810460ff16855285517ffeaf968c00000000000000000000000000000000000000000000000000000000815295519495919484936701000000000000009092049091169163feaf968c9160048083019260a09291908290030181865afa158015613e34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e58919061505a565b50935050925050600082131580613e6e57508042105b80613e9e57506000866040015162ffffff16118015613e9e5750613e928142614b23565b866040015162ffffff16105b15613eb25760018301546060850152613eba565b606084018290525b50505092915050565b6040805161010081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081019190915260008260e001516000015160ff1690506000846060015161ffff168460600151613f2e91906150c3565b90508361010001518015613f415750803a105b15613f4957503a5b600060128311613f5a576001613f70565b613f65601284614b23565b613f7090600a615565565b9050600060128410613f83576001613f99565b613f8e846012614b23565b613f9990600a615565565b905060008660a00151876040015188602001518960000151613fbb9190614e41565b613fc590876150c3565b613fcf9190614e41565b613fd991906150c3565b9050614035828860e0015160600151613ff291906150c3565b6001848a60e001516060015161400891906150c3565b6140129190614b23565b61401c86856150c3565b6140269190614e41565b6140309190615571565b613664565b6bffffffffffffffffffffffff1686526080870151614058906140309083615571565b6bffffffffffffffffffffffff1660408088019190915260e088015101516000906140919062ffffff16683635c9adc5dea000006150c3565b9050600081633b9aca008a60a001518b60e001516020015163ffffffff168c604001518d600001518b6140c491906150c3565b6140ce9190614e41565b6140d891906150c3565b6140e291906150c3565b6140ec9190615571565b6140f69190614e41565b9050614139848a60e001516060015161410f91906150c3565b6001868c60e001516060015161412591906150c3565b61412f9190614b23565b61401c88856150c3565b6bffffffffffffffffffffffff166020890152608089015161415f906140309083615571565b6bffffffffffffffffffffffff16606089015260c089015173ffffffffffffffffffffffffffffffffffffffff166080808a01919091528901516141a290613664565b6bffffffffffffffffffffffff1660a0808a01919091528901516141c590613664565b6bffffffffffffffffffffffff1660c089015260e0890151606001516141ea90613664565b6bffffffffffffffffffffffff1660e08901525050505050505092915050565b600081815260018301602052604081205480156142f357600061422e600183614b23565b855490915060009061424290600190614b23565b90508181146142a757600086600001828154811061426257614262614fd9565b906000526020600020015490508087600001848154811061428557614285614fd9565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806142b8576142b86155ac565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050612c1b565b6000915050612c1b565b6060612ebd8484600085856000808673ffffffffffffffffffffffffffffffffffffffff1685876040516143319190614ad8565b60006040518083038185875af1925050503d806000811461436e576040519150601f19603f3d011682016040523d82523d6000602084013e614373565b606091505b50915091506143848783838761438f565b979650505050505050565b6060831561442557825160000361441e5773ffffffffffffffffffffffffffffffffffffffff85163b61441e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610bab565b5081612ebd565b612ebd838381511561443a5781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bab91906153ed565b73ffffffffffffffffffffffffffffffffffffffff81168114612aa757600080fd5b600080604083850312156144a357600080fd5b8235915060208301356144b58161446e565b809150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610100810167ffffffffffffffff81118282101715614513576145136144c0565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715614560576145606144c0565b604052919050565b600067ffffffffffffffff821115614582576145826144c0565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600082601f8301126145bf57600080fd5b81356145d26145cd82614568565b614519565b8181528460208386010111156145e757600080fd5b816020850160208301376000918101602001919091529392505050565b6000806040838503121561461757600080fd5b82359150602083013567ffffffffffffffff81111561463557600080fd5b614641858286016145ae565b9150509250929050565b60005b8381101561466657818101518382015260200161464e565b50506000910152565b6000815180845261468781602086016020860161464b565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600a811061471f577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b841515815260806020820152600061473e608083018661466f565b905061474d60408301856146e8565b82606083015295945050505050565b60008083601f84011261476e57600080fd5b50813567ffffffffffffffff81111561478657600080fd5b60208301915083602082850101111561479e57600080fd5b9250929050565b6000806000604084860312156147ba57600080fd5b83359250602084013567ffffffffffffffff8111156147d857600080fd5b6147e48682870161475c565b9497909650939450505050565b60006020828403121561480357600080fd5b5035919050565b6000806040838503121561481d57600080fd5b82356148288161446e565b946020939093013593505050565b60008060006060848603121561484b57600080fd5b83356148568161446e565b925060208401356148668161446e565b929592945050506040919091013590565b871515815260e06020820152600061489260e083018961466f565b90506148a160408301886146e8565b8560608301528460808301528360a08301528260c083015298975050505050505050565b600080604083850312156148d857600080fd5b8235915060208301356bffffffffffffffffffffffff811681146144b557600080fd5b63ffffffff81168114612aa757600080fd5b6000806040838503121561492057600080fd5b8235915060208301356144b5816148fb565b600080828403606081121561494657600080fd5b8335925060407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08201121561497a57600080fd5b506020830190509250929050565b600067ffffffffffffffff8211156149a2576149a26144c0565b5060051b60200190565b600080600080606085870312156149c257600080fd5b8435935060208086013567ffffffffffffffff808211156149e257600080fd5b818801915088601f8301126149f657600080fd5b8135614a046145cd82614988565b81815260059190911b8301840190848101908b831115614a2357600080fd5b8585015b83811015614a5b57803585811115614a3f5760008081fd5b614a4d8e89838a01016145ae565b845250918601918601614a27565b50975050506040880135925080831115614a7457600080fd5b5050614a828782880161475c565b95989497509550505050565b600060208284031215614aa057600080fd5b81356136008161446e565b8051614ab68161446e565b919050565b600060208284031215614acd57600080fd5b81516136008161446e565b60008251614aea81846020870161464b565b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115612c1b57612c1b614af4565b80518015158114614ab657600080fd5b600082601f830112614b5757600080fd5b8151614b656145cd82614568565b818152846020838601011115614b7a57600080fd5b612ebd82602083016020870161464b565b60008060408385031215614b9e57600080fd5b614ba783614b36565b9150602083015167ffffffffffffffff811115614bc357600080fd5b61464185828601614b46565b600181811c90821680614be357607f821691505b602082108103614c1c577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f821115612e1557600081815260208120601f850160051c81016020861015614c495750805b601f850160051c820191505b81811015614c6857828155600101614c55565b505050505050565b67ffffffffffffffff831115614c8857614c886144c0565b614c9c83614c968354614bcf565b83614c22565b6000601f841160018114614cee5760008515614cb85750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355614d84565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b82811015614d3d5786850135825560209485019460019092019101614d1d565b5086821015614d78577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b602081526000612ebd602083018486614d8b565b600060208284031215614dfa57600080fd5b612c1882614b36565b600060208284031215614e1557600080fd5b5051919050565b6bffffffffffffffffffffffff818116838216019080821115613d4057613d40614af4565b80820180821115612c1b57612c1b614af4565b62ffffff81168114612aa757600080fd5b8135614e70816148fb565b63ffffffff811690508154817fffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000082161783556020840135614eb081614e54565b66ffffff000000008160201b16837fffffffffffffffffffffffffffffffffffffffffffffffffff000000000000008416171784555050505050565b604081018235614efb816148fb565b63ffffffff1682526020830135614f1181614e54565b62ffffff811660208401525092915050565b6000604082016040835280865180835260608501915060608160051b8601019250602080890160005b83811015614f98577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0888703018552614f8686835161466f565b95509382019390820190600101614f4c565b505085840381870152505050614faf818587614d8b565b9695505050505050565b8181036000831280158383131683831282161715613d4057613d40614af4565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361503957615039614af4565b5060010190565b805169ffffffffffffffffffff81168114614ab657600080fd5b600080600080600060a0868803121561507257600080fd5b61507b86615040565b945060208601519350604086015192506060860151915061509e60808701615040565b90509295509295909350565b60ff8181168382160190811115612c1b57612c1b614af4565b8082028115828204841417612c1b57612c1b614af4565b600080604083850312156150ed57600080fd5b505080516020909101519092909150565b6000815461510b81614bcf565b80855260206001838116801561512857600181146151605761518e565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008516838901528284151560051b890101955061518e565b866000528260002060005b858110156151865781548a820186015290830190840161516b565b890184019650505b505050505092915050565b602081526000612c1860208301846150fe565b600082601f8301126151bd57600080fd5b815160206151cd6145cd83614988565b82815260059290921b840181019181810190868411156151ec57600080fd5b8286015b8481101561520757805183529183019183016151f0565b509695505050505050565b60006020828403121561522457600080fd5b815167ffffffffffffffff8082111561523c57600080fd5b90830190610100828603121561525157600080fd5b6152596144ef565b825181526020830151602082015260408301516040820152606083015160608201526080830151608082015261529160a08401614aab565b60a082015260c0830151828111156152a857600080fd5b6152b4878286016151ac565b60c08301525060e0830151828111156152cc57600080fd5b6152d887828601614b46565b60e08301525095945050505050565b600081518084526020808501945080840160005b83811015615317578151875295820195908201906001016152fb565b509495945050505050565b60408152825160408201526020830151606082015260408301516080820152606083015160a0820152608083015160c082015273ffffffffffffffffffffffffffffffffffffffff60a08401511660e0820152600060c08401516101008081850152506153936101408401826152e7565b905060e08501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0848303016101208501526153cf828261466f565b91505082810360208401526153e481856150fe565b95945050505050565b602081526000612c18602083018461466f565b828152604060208201526000612ebd604083018461466f565b6000806040838503121561542c57600080fd5b61543583614b36565b9150602083015190509250929050565b600181815b8085111561549e57817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111561548457615484614af4565b8085161561549157918102915b93841c939080029061544a565b509250929050565b6000826154b557506001612c1b565b816154c257506000612c1b565b81600181146154d857600281146154e2576154fe565b6001915050612c1b565b60ff8411156154f3576154f3614af4565b50506001821b612c1b565b5060208310610133831016604e8410600b8410161715615521575081810a612c1b565b61552b8383615445565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111561555d5761555d614af4565b029392505050565b6000612c1883836154a6565b6000826155a7577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfea164736f6c6343000813000a", } var AutomationRegistryLogicBABI = AutomationRegistryLogicBMetaData.ABI diff --git a/core/gethwrappers/generated/automation_registry_logic_c_wrapper_2_3/automation_registry_logic_c_wrapper_2_3.go b/core/gethwrappers/generated/automation_registry_logic_c_wrapper_2_3/automation_registry_logic_c_wrapper_2_3.go index e32d6890dd..d5e6d9d64f 100644 --- a/core/gethwrappers/generated/automation_registry_logic_c_wrapper_2_3/automation_registry_logic_c_wrapper_2_3.go +++ b/core/gethwrappers/generated/automation_registry_logic_c_wrapper_2_3/automation_registry_logic_c_wrapper_2_3.go @@ -151,7 +151,7 @@ type IAutomationV21PlusCommonUpkeepInfoLegacy struct { var AutomationRegistryLogicCMetaData = &bind.MetaData{ ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"link\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"linkUSDFeed\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"nativeUSDFeed\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"fastGasFeed\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"automationForwarderLogic\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"allowedReadOnlyAddress\",\"type\":\"address\"},{\"internalType\":\"enumAutomationRegistryBase2_3.PayoutMode\",\"name\":\"payoutMode\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"wrappedNativeTokenAddress\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ArrayHasNoEntries\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotCancel\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CheckDataExceedsLimit\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ConfigDigestMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateEntry\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitOutsideRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfFaultyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IndexOutOfRange\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"available\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requested\",\"type\":\"uint256\"}],\"name\":\"InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientLinkLiquidity\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidDataLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidFeed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRecipient\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReport\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTransmitter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTrigger\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTriggerType\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MigrationNotPermitted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MustSettleOffchain\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MustSettleOnchain\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotAContract\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveTransmitters\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByLINKToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrRegistrar\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByUpkeepPrivilegeManager\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyFinanceAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyPausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlySimulatedBackend\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyUnpausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ParameterLengthError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReentrantCall\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RegistryPaused\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedTransmitter\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"reason\",\"type\":\"bytes\"}],\"name\":\"TargetCheckReverted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TranscoderNotSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TransferFailed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepAlreadyExists\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepCancelled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotCanceled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotNeeded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ValueNotChanged\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"AdminPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"gasFeePPB\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"flatFeeMilliCents\",\"type\":\"uint24\"}],\"indexed\":false,\"internalType\":\"structAutomationRegistryBase2_3.BillingOverrides\",\"name\":\"overrides\",\"type\":\"tuple\"}],\"name\":\"BillingConfigOverridden\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"BillingConfigOverrideRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"contractIERC20Metadata\",\"name\":\"token\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"gasFeePPB\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"flatFeeMilliCents\",\"type\":\"uint24\"},{\"internalType\":\"contractAggregatorV3Interface\",\"name\":\"priceFeed\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"decimals\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"fallbackPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint96\",\"name\":\"minSpend\",\"type\":\"uint96\"}],\"indexed\":false,\"internalType\":\"structAutomationRegistryBase2_3.BillingConfig\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"BillingConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"CancelledUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newModule\",\"type\":\"address\"}],\"name\":\"ChainSpecificModuleUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"dedupKey\",\"type\":\"bytes32\"}],\"name\":\"DedupKeyAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"assetAddress\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"FeesWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"FundsAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"FundsWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"InsufficientFundsUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"payees\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"payments\",\"type\":\"uint256[]\"}],\"name\":\"NOPsSettledOffchain\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"payees\",\"type\":\"address[]\"}],\"name\":\"PayeesUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"payee\",\"type\":\"address\"}],\"name\":\"PaymentWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"ReorgedUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"StaleUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"atBlockHeight\",\"type\":\"uint64\"}],\"name\":\"UpkeepCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint96\",\"name\":\"gasChargeInBillingToken\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"premiumInBillingToken\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"gasReimbursementInJuels\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"premiumInJuels\",\"type\":\"uint96\"},{\"internalType\":\"contractIERC20Metadata\",\"name\":\"billingToken\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"linkUSD\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"nativeUSD\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"billingUSD\",\"type\":\"uint96\"}],\"indexed\":false,\"internalType\":\"structAutomationRegistryBase2_3.PaymentReceipt\",\"name\":\"receipt\",\"type\":\"tuple\"}],\"name\":\"UpkeepCharged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"newCheckData\",\"type\":\"bytes\"}],\"name\":\"UpkeepCheckDataSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"gasLimit\",\"type\":\"uint96\"}],\"name\":\"UpkeepGasLimitSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"remainingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"UpkeepMigrated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepOffchainConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepPaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"totalPayment\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasOverhead\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"UpkeepPerformed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"startingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"importedFrom\",\"type\":\"address\"}],\"name\":\"UpkeepReceived\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"performGas\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"}],\"name\":\"UpkeepRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepTriggerConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepUnpaused\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"}],\"name\":\"acceptPayeeship\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"disableOffchainPayments\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"startIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxCount\",\"type\":\"uint256\"}],\"name\":\"getActiveUpkeepIDs\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"}],\"name\":\"getAdminPrivilegeConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAllowedReadOnlyAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAutomationForwarderLogic\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contractIERC20Metadata\",\"name\":\"billingToken\",\"type\":\"address\"}],\"name\":\"getAvailableERC20ForPayment\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contractIERC20Metadata\",\"name\":\"billingToken\",\"type\":\"address\"}],\"name\":\"getBillingConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"gasFeePPB\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"flatFeeMilliCents\",\"type\":\"uint24\"},{\"internalType\":\"contractAggregatorV3Interface\",\"name\":\"priceFeed\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"decimals\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"fallbackPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint96\",\"name\":\"minSpend\",\"type\":\"uint96\"}],\"internalType\":\"structAutomationRegistryBase2_3.BillingConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepID\",\"type\":\"uint256\"}],\"name\":\"getBillingOverrides\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"gasFeePPB\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"flatFeeMilliCents\",\"type\":\"uint24\"}],\"internalType\":\"structAutomationRegistryBase2_3.BillingOverrides\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepID\",\"type\":\"uint256\"}],\"name\":\"getBillingOverridesEnabled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepID\",\"type\":\"uint256\"}],\"name\":\"getBillingToken\",\"outputs\":[{\"internalType\":\"contractIERC20Metadata\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contractIERC20Metadata\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"getBillingTokenConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"gasFeePPB\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"flatFeeMilliCents\",\"type\":\"uint24\"},{\"internalType\":\"contractAggregatorV3Interface\",\"name\":\"priceFeed\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"decimals\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"fallbackPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint96\",\"name\":\"minSpend\",\"type\":\"uint96\"}],\"internalType\":\"structAutomationRegistryBase2_3.BillingConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getBillingTokens\",\"outputs\":[{\"internalType\":\"contractIERC20Metadata[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCancellationDelay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getChainModule\",\"outputs\":[{\"internalType\":\"contractIChainModule\",\"name\":\"chainModule\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConditionalGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"checkGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxCheckDataSize\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"reorgProtectionEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint24\",\"name\":\"stalenessSeconds\",\"type\":\"uint24\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxRevertDataSize\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"upkeepPrivilegeManager\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"gasCeilingMultiplier\",\"type\":\"uint16\"},{\"internalType\":\"address\",\"name\":\"financeAdmin\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"fallbackGasPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fallbackLinkPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fallbackNativePrice\",\"type\":\"uint256\"},{\"internalType\":\"address[]\",\"name\":\"registrars\",\"type\":\"address[]\"},{\"internalType\":\"contractIChainModule\",\"name\":\"chainModule\",\"type\":\"address\"}],\"internalType\":\"structAutomationRegistryBase2_3.OnchainConfig\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getFallbackNativePrice\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getFastGasFeedAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepID\",\"type\":\"uint256\"}],\"name\":\"getForwarder\",\"outputs\":[{\"internalType\":\"contractIAutomationForwarder\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getHotVars\",\"outputs\":[{\"components\":[{\"internalType\":\"uint96\",\"name\":\"totalPremium\",\"type\":\"uint96\"},{\"internalType\":\"uint32\",\"name\":\"latestEpoch\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"stalenessSeconds\",\"type\":\"uint24\"},{\"internalType\":\"uint16\",\"name\":\"gasCeilingMultiplier\",\"type\":\"uint16\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"bool\",\"name\":\"paused\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"reentrancyGuard\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"reorgProtectionEnabled\",\"type\":\"bool\"},{\"internalType\":\"contractIChainModule\",\"name\":\"chainModule\",\"type\":\"address\"}],\"internalType\":\"structAutomationRegistryBase2_3.HotVars\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLinkAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLinkUSDFeedAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLogGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"enumAutomationRegistryBase2_3.Trigger\",\"name\":\"triggerType\",\"type\":\"uint8\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"},{\"internalType\":\"contractIERC20Metadata\",\"name\":\"billingToken\",\"type\":\"address\"}],\"name\":\"getMaxPaymentForGas\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"maxPayment\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getMinBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getMinBalanceForUpkeep\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"minBalance\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getNativeUSDFeedAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getNumUpkeeps\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getPayoutMode\",\"outputs\":[{\"internalType\":\"enumAutomationRegistryBase2_3.PayoutMode\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"peer\",\"type\":\"address\"}],\"name\":\"getPeerRegistryMigrationPermission\",\"outputs\":[{\"internalType\":\"enumAutomationRegistryBase2_3.MigrationPermission\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getPerPerformByteGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getPerSignerGasOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getReorgProtectionEnabled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"reorgProtectionEnabled\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contractIERC20Metadata\",\"name\":\"billingToken\",\"type\":\"address\"}],\"name\":\"getReserveAmount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"query\",\"type\":\"address\"}],\"name\":\"getSignerInfo\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"index\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getState\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nonce\",\"type\":\"uint32\"},{\"internalType\":\"uint96\",\"name\":\"ownerLinkBalance\",\"type\":\"uint96\"},{\"internalType\":\"uint256\",\"name\":\"expectedLinkBalance\",\"type\":\"uint256\"},{\"internalType\":\"uint96\",\"name\":\"totalPremium\",\"type\":\"uint96\"},{\"internalType\":\"uint256\",\"name\":\"numUpkeeps\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"latestConfigBlockNumber\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"latestConfigDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"latestEpoch\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"paused\",\"type\":\"bool\"}],\"internalType\":\"structIAutomationV21PlusCommon.StateLegacy\",\"name\":\"state\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"paymentPremiumPPB\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"flatFeeMicroLink\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"checkGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"stalenessSeconds\",\"type\":\"uint24\"},{\"internalType\":\"uint16\",\"name\":\"gasCeilingMultiplier\",\"type\":\"uint16\"},{\"internalType\":\"uint96\",\"name\":\"minUpkeepSpend\",\"type\":\"uint96\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxCheckDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxRevertDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"fallbackGasPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fallbackLinkPrice\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"registrars\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"upkeepPrivilegeManager\",\"type\":\"address\"}],\"internalType\":\"structIAutomationV21PlusCommon.OnchainConfigLegacy\",\"name\":\"config\",\"type\":\"tuple\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStorage\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"checkGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"nonce\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"upkeepPrivilegeManager\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"latestConfigBlockNumber\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxCheckDataSize\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"financeAdmin\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxRevertDataSize\",\"type\":\"uint32\"}],\"internalType\":\"structAutomationRegistryBase2_3.Storage\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getTransmitCalldataFixedBytesOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getTransmitCalldataPerSignerBytesOverhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"query\",\"type\":\"address\"}],\"name\":\"getTransmitterInfo\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"index\",\"type\":\"uint8\"},{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"lastCollected\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"payee\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getTransmittersWithPayees\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"transmitterAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"payeeAddress\",\"type\":\"address\"}],\"internalType\":\"structAutomationRegistryBase2_3.TransmitterPayeeInfo[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"}],\"name\":\"getTriggerType\",\"outputs\":[{\"internalType\":\"enumAutomationRegistryBase2_3.Trigger\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"getUpkeep\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"performGas\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"checkData\",\"type\":\"bytes\"},{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"maxValidBlocknumber\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"lastPerformedBlockNumber\",\"type\":\"uint32\"},{\"internalType\":\"uint96\",\"name\":\"amountSpent\",\"type\":\"uint96\"},{\"internalType\":\"bool\",\"name\":\"paused\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"internalType\":\"structIAutomationV21PlusCommon.UpkeepInfoLegacy\",\"name\":\"upkeepInfo\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"}],\"name\":\"getUpkeepPrivilegeConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"}],\"name\":\"getUpkeepTriggerConfig\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getWrappedNativeTokenAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"dedupKey\",\"type\":\"bytes32\"}],\"name\":\"hasDedupKey\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"linkAvailableForPayment\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"newPrivilegeConfig\",\"type\":\"bytes\"}],\"name\":\"setAdminPrivilegeConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"payees\",\"type\":\"address[]\"}],\"name\":\"setPayees\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"peer\",\"type\":\"address\"},{\"internalType\":\"enumAutomationRegistryBase2_3.MigrationPermission\",\"name\":\"permission\",\"type\":\"uint8\"}],\"name\":\"setPeerRegistryMigrationPermission\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"upkeepId\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"newPrivilegeConfig\",\"type\":\"bytes\"}],\"name\":\"setUpkeepPrivilegeConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"settleNOPsOffchain\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contractIERC20Metadata\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"supportsBillingToken\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"proposed\",\"type\":\"address\"}],\"name\":\"transferPayeeship\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"upkeepVersion\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"withdrawPayment\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x6101606040523480156200001257600080fd5b5060405162005c8738038062005c87833981016040819052620000359162000309565b87878787878787873380600081620000945760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000c757620000c78162000241565b5050506001600160a01b0380891660805287811660a05286811660c05285811660e052848116610100528316610120526025805483919060ff191660018381811115620001185762000118620003b6565b0217905550806001600160a01b0316610140816001600160a01b03168152505060c0516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000179573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200019f9190620003cc565b60ff1660a0516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001e3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002099190620003cc565b60ff16146200022b576040516301f86e1760e41b815260040160405180910390fd5b50505050505050505050505050505050620003f8565b336001600160a01b038216036200029b5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016200008b565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b80516001600160a01b03811681146200030457600080fd5b919050565b600080600080600080600080610100898b0312156200032757600080fd5b6200033289620002ec565b97506200034260208a01620002ec565b96506200035260408a01620002ec565b95506200036260608a01620002ec565b94506200037260808a01620002ec565b93506200038260a08a01620002ec565b925060c0890151600281106200039757600080fd5b9150620003a760e08a01620002ec565b90509295985092959890939650565b634e487b7160e01b600052602160045260246000fd5b600060208284031215620003df57600080fd5b815160ff81168114620003f157600080fd5b9392505050565b60805160a05160c05160e051610100516101205161014051615803620004846000396000610b7001526000610ad2015260006107be0152600081816109a001526135b60152600081816109540152613e3201526000818161051d0152613690015260008181610c1801528181611df00152818161256b015281816125c80152613af601526158036000f3fe608060405234801561001057600080fd5b50600436106103d05760003560e01c80638ed02bab116101ff578063c3f909d41161011a578063eb5dcd6c116100ad578063f5a418461161007c578063f5a4184614610eb4578063f777ff0614610f35578063faa3e99614610f3c578063ffd242bd14610f8257600080fd5b8063eb5dcd6c14610de5578063ec4de4ba14610df8578063ed56b3e114610e2e578063f2fde38b14610ea157600080fd5b8063d09dc339116100e9578063d09dc33914610c3c578063d763264814610c44578063d85aa07c14610c57578063e80a3d4414610c5f57600080fd5b8063c3f909d414610bd6578063c5b964e014610beb578063c7c3a19a14610bf6578063ca30e60314610c1657600080fd5b8063aab9edd611610192578063b3596c2311610161578063b3596c23146107e2578063b6511a2a14610ba7578063b657bc9c14610bae578063ba87666814610bc157600080fd5b8063aab9edd614610b57578063abc76ae014610b66578063ac4dc59a14610b6e578063b121e14714610b9457600080fd5b8063a08714c0116101ce578063a08714c014610ad0578063a538b2eb14610af6578063a710b22114610b3c578063a87f45fe14610b4f57600080fd5b80638ed02bab14610a5c5780639089daa414610a7a57806393f6ebcf14610a8f5780639e0a99ed14610ac857600080fd5b806343cc055c116102ef5780636209e1e91161028257806379ba50971161025157806379ba5097146109ea57806379ea9943146109f25780638456cb5914610a365780638da5cb5b14610a3e57600080fd5b80636209e1e91461098b5780636709d0e51461099e578063671d36ed146109c45780636eec02a2146109d757600080fd5b80635425d8ac116102be5780635425d8ac146107bc57806357359584146107e2578063614486af146109525780636181d82d1461097857600080fd5b806343cc055c1461074957806344cb70b8146107705780634ca16c52146107935780635147cd591461079c57600080fd5b8063207b6516116103675780633b9cce59116103365780633b9cce59146106c05780633f4ba83a146106d3578063421d183b146106db57806343b46e5f1461074157600080fd5b8063207b651614610508578063226cf83c1461051b578063232c1cc5146105625780633408f73a1461056957600080fd5b8063187256e8116103a3578063187256e81461043b57806319d97a941461044e5780631e0104391461046e5780631efcf646146104d057600080fd5b8063050ee65d146103d557806306e3b632146103ed5780630b7d33e61461040d5780631865c57d14610422575b600080fd5b6201de845b6040519081526020015b60405180910390f35b6104006103fb36600461447a565b610f8a565b6040516103e491906144d7565b61042061041b366004614533565b6110a7565b005b61042a611108565b6040516103e495949392919061472b565b61042061044936600461486c565b611508565b61046161045c3660046148a9565b611579565b6040516103e49190614926565b6104b361047c3660046148a9565b60009081526004602052604090206001015470010000000000000000000000000000000090046bffffffffffffffffffffffff1690565b6040516bffffffffffffffffffffffff90911681526020016103e4565b6104f86104de3660046148a9565b600090815260046020526040902054610100900460ff1690565b60405190151581526020016103e4565b6104616105163660046148a9565b61161b565b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016103e4565b60186103da565b6106b36040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081018290526101008101829052610120810182905261014081019190915250604080516101608101825260165473ffffffffffffffffffffffffffffffffffffffff808216835263ffffffff740100000000000000000000000000000000000000008084048216602086015278010000000000000000000000000000000000000000000000008085048316968601969096527c010000000000000000000000000000000000000000000000000000000093849004821660608601526017548084166080870152818104831660a0870152868104831660c087015293909304811660e085015260185491821661010085015291810482166101208401529290920490911661014082015290565b6040516103e49190614939565b6104206106ce366004614a63565b611638565b61042061188e565b6106ee6106e9366004614ad8565b6118f4565b60408051951515865260ff90941660208601526bffffffffffffffffffffffff9283169385019390935216606083015273ffffffffffffffffffffffffffffffffffffffff16608082015260a0016103e4565b610420611a13565b6014547801000000000000000000000000000000000000000000000000900460ff166104f8565b6104f861077e3660046148a9565b60009081526008602052604090205460ff1690565b62017f986103da565b6107af6107aa3660046148a9565b611eaa565b6040516103e49190614b34565b7f000000000000000000000000000000000000000000000000000000000000000061053d565b6108d46107f0366004614ad8565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a08101919091525073ffffffffffffffffffffffffffffffffffffffff908116600090815260226020908152604091829020825160c081018452815463ffffffff81168252640100000000810462ffffff16938201939093526701000000000000008304909416928401929092527b01000000000000000000000000000000000000000000000000000000900460ff16606083015260018101546080830152600201546bffffffffffffffffffffffff1660a082015290565b6040516103e49190600060c08201905063ffffffff835116825262ffffff602084015116602083015273ffffffffffffffffffffffffffffffffffffffff604084015116604083015260ff6060840151166060830152608083015160808301526bffffffffffffffffffffffff60a08401511660a083015292915050565b7f000000000000000000000000000000000000000000000000000000000000000061053d565b6104b3610986366004614b47565b611eb5565b610461610999366004614ad8565b612010565b7f000000000000000000000000000000000000000000000000000000000000000061053d565b6104206109d2366004614ba7565b612043565b6103da6109e5366004614ad8565b6120c3565b61042061216d565b61053d610a003660046148a9565b6000908152600460205260409020546a0100000000000000000000900473ffffffffffffffffffffffffffffffffffffffff1690565b61042061226f565b60005473ffffffffffffffffffffffffffffffffffffffff1661053d565b60155473ffffffffffffffffffffffffffffffffffffffff1661053d565b610a826122e8565b6040516103e49190614be3565b61053d610a9d3660046148a9565b60009081526004602052604090206002015473ffffffffffffffffffffffffffffffffffffffff1690565b6103a46103da565b7f000000000000000000000000000000000000000000000000000000000000000061053d565b6104f8610b04366004614ad8565b73ffffffffffffffffffffffffffffffffffffffff908116600090815260226020526040902054670100000000000000900416151590565b610420610b4a366004614c4c565b6123fa565b610420612728565b604051600481526020016103e4565b6115e06103da565b7f000000000000000000000000000000000000000000000000000000000000000061053d565b610420610ba2366004614ad8565b61275a565b60326103da565b6104b3610bbc3660046148a9565b612852565b610bc9612979565b6040516103e49190614c7a565b610bde6129e8565b6040516103e49190614cd4565b60255460ff166107af565b610c09610c043660046148a9565b612bd0565b6040516103e49190614e5b565b7f000000000000000000000000000000000000000000000000000000000000000061053d565b6103da612fe0565b6104b3610c523660046148a9565b612fef565b601b546103da565b610dd86040805161012081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081018290526101008101919091525060408051610120810182526014546bffffffffffffffffffffffff8116825263ffffffff6c01000000000000000000000000820416602083015262ffffff7001000000000000000000000000000000008204169282019290925261ffff730100000000000000000000000000000000000000830416606082015260ff750100000000000000000000000000000000000000000083048116608083015276010000000000000000000000000000000000000000000083048116151560a08301527701000000000000000000000000000000000000000000000083048116151560c08301527801000000000000000000000000000000000000000000000000909204909116151560e082015260155473ffffffffffffffffffffffffffffffffffffffff1661010082015290565b6040516103e49190614f92565b610420610df3366004614c4c565b612ffa565b6103da610e06366004614ad8565b73ffffffffffffffffffffffffffffffffffffffff1660009081526021602052604090205490565b610e88610e3c366004614ad8565b73ffffffffffffffffffffffffffffffffffffffff166000908152600c602090815260409182902082518084019093525460ff8082161515808552610100909204169290910182905291565b60408051921515835260ff9091166020830152016103e4565b610420610eaf366004614ad8565b613159565b610f0f610ec23660046148a9565b60408051808201909152600080825260208201525060009081526023602090815260409182902082518084019093525463ffffffff81168352640100000000900462ffffff169082015290565b60408051825163ffffffff16815260209283015162ffffff1692810192909252016103e4565b60406103da565b610f75610f4a366004614ad8565b73ffffffffffffffffffffffffffffffffffffffff166000908152601c602052604090205460ff1690565b6040516103e49190615062565b6103da61316d565b60606000610f986002613175565b9050808410610fd3576040517f1390f2a100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610fdf84866150a5565b905081811180610fed575083155b610ff75780610ff9565b815b9050600061100786836150b8565b67ffffffffffffffff81111561101f5761101f6150cb565b604051908082528060200260200182016040528015611048578160200160208202803683370190505b50905060005b815181101561109b5761106c61106488836150a5565b60029061317f565b82828151811061107e5761107e6150fa565b60209081029190910101528061109381615129565b91505061104e565b50925050505b92915050565b6110af61318b565b6000838152601f602052604090206110c8828483615203565b50827f2fd8d70753a007014349d4591843cc031c2dd7a260d7dd82eca8253686ae776983836040516110fb92919061531e565b60405180910390a2505050565b6040805161014081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081018290526101008101829052610120810191909152604080516101e08101825260008082526020820181905291810182905260608082018390526080820183905260a0820183905260c0820183905260e08201839052610100820183905261012082018390526101408201839052610160820183905261018082018390526101a08201526101c081019190915260408051610140810182526016547c0100000000000000000000000000000000000000000000000000000000900463ffffffff1681526000602082018190529181018290526014546bffffffffffffffffffffffff16606080830191909152918291608081016112416002613175565b81526017547401000000000000000000000000000000000000000080820463ffffffff908116602080860191909152780100000000000000000000000000000000000000000000000080850483166040808801919091526013546060808901919091526014546c01000000000000000000000000810486166080808b0191909152760100000000000000000000000000000000000000000000820460ff16151560a09a8b015283516101e0810185526000808252968101879052601654898104891695820195909552700100000000000000000000000000000000830462ffffff169381019390935273010000000000000000000000000000000000000090910461ffff169082015296870192909252808204831660c08701527c0100000000000000000000000000000000000000000000000000000000909404821660e086015260185492830482166101008601529290910416610120830152601954610140830152601a5461016083015273ffffffffffffffffffffffffffffffffffffffff166101808201529095506101a081016113dc60096131de565b815260175473ffffffffffffffffffffffffffffffffffffffff16602091820152601454600d80546040805182860281018601909152818152949850899489949293600e93750100000000000000000000000000000000000000000090910460ff1692859183018282801561148757602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff16815260019091019060200180831161145c575b50505050509250818054806020026020016040519081016040528092919081815260200182805480156114f057602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116114c5575b50505050509150945094509450945094509091929394565b6115106131eb565b73ffffffffffffffffffffffffffffffffffffffff82166000908152601c6020526040902080548291907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600183600381111561157057611570614af5565b02179055505050565b6000818152601f6020526040902080546060919061159690615161565b80601f01602080910402602001604051908101604052809291908181526020018280546115c290615161565b801561160f5780601f106115e45761010080835404028352916020019161160f565b820191906000526020600020905b8154815290600101906020018083116115f257829003601f168201915b50505050509050919050565b6000818152601d6020526040902080546060919061159690615161565b6116406131eb565b600e54811461167b576040517fcf54c06a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b600e5481101561184d576000600e828154811061169d5761169d6150fa565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff9081168084526011909252604083205491935016908585858181106116e7576116e76150fa565b90506020020160208101906116fc9190614ad8565b905073ffffffffffffffffffffffffffffffffffffffff8116158061178f575073ffffffffffffffffffffffffffffffffffffffff82161580159061176d57508073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b801561178f575073ffffffffffffffffffffffffffffffffffffffff81811614155b156117c6576040517fb387a23800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff818116146118375773ffffffffffffffffffffffffffffffffffffffff838116600090815260116020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169183169190911790555b505050808061184590615129565b91505061167e565b507fa46de38886467c59be07a0675f14781206a5477d871628af46c2443822fcb725600e83836040516118829392919061536b565b60405180910390a15050565b6118966131eb565b601480547fffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffff1690556040513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa906020015b60405180910390a1565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600b602090815260408083208151608081018352905460ff80821615801584526101008304909116948301949094526bffffffffffffffffffffffff6201000082048116938301939093526e0100000000000000000000000000009004909116606082015282918291829182919082906119ba5760608201516014546000916119a6916bffffffffffffffffffffffff1661541d565b600e549091506119b69082615471565b9150505b8151602083015160408401516119d190849061549c565b6060949094015173ffffffffffffffffffffffffffffffffffffffff9a8b16600090815260116020526040902054929b919a9499509750921694509092505050565b611a1b61326c565b600060255460ff166001811115611a3457611a34614af5565b03611a6b576040517fe0262d7400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601454600e546bffffffffffffffffffffffff909116906000611a8e600f613175565b90506000611a9c82846150a5565b905060008167ffffffffffffffff811115611ab957611ab96150cb565b604051908082528060200260200182016040528015611ae2578160200160208202803683370190505b50905060008267ffffffffffffffff811115611b0057611b006150cb565b604051908082528060200260200182016040528015611b29578160200160208202803683370190505b50905060005b85811015611c5b576000600e8281548110611b4c57611b4c6150fa565b600091825260208220015473ffffffffffffffffffffffffffffffffffffffff169150611b7a828a8a6132bd565b9050806bffffffffffffffffffffffff16858481518110611b9d57611b9d6150fa565b60209081029190910181019190915273ffffffffffffffffffffffffffffffffffffffff808416600090815260119092526040909120548551911690859085908110611beb57611beb6150fa565b73ffffffffffffffffffffffffffffffffffffffff92831660209182029290920181019190915292166000908152600b909252506040902080547fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff16905580611c5381615129565b915050611b2f565b5060005b84811015611dd8576000611c74600f8361317f565b73ffffffffffffffffffffffffffffffffffffffff8082166000818152600b602090815260408083208151608081018352905460ff80821615158352610100820416828501526bffffffffffffffffffffffff6201000082048116838501526e0100000000000000000000000000009091041660608201529383526011909152902054929350911684611d078a866150a5565b81518110611d1757611d176150fa565b73ffffffffffffffffffffffffffffffffffffffff9092166020928302919091019091015260408101516bffffffffffffffffffffffff1685611d5a8a866150a5565b81518110611d6a57611d6a6150fa565b60209081029190910181019190915273ffffffffffffffffffffffffffffffffffffffff9092166000908152600b909252506040902080547fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff16905580611dd081615129565b915050611c5f565b5073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166000908152602160205260408120819055611e2b600f613175565b90505b8015611e6857611e55611e4d611e456001846150b8565b600f9061317f565b600f906134c5565b5080611e60816154c1565b915050611e2e565b507f5af23b715253628d12b660b27a4f3fc626562ea8a55040aa99ab3dc178989fad8183604051611e9a9291906154f6565b60405180910390a1505050505050565b60006110a1826134e7565b60408051610120810182526014546bffffffffffffffffffffffff8116825263ffffffff6c01000000000000000000000000820416602083015262ffffff7001000000000000000000000000000000008204169282019290925261ffff730100000000000000000000000000000000000000830416606082015260ff750100000000000000000000000000000000000000000083048116608083015276010000000000000000000000000000000000000000000083048116151560a08301527701000000000000000000000000000000000000000000000083048116151560c08301527801000000000000000000000000000000000000000000000000909204909116151560e082015260155473ffffffffffffffffffffffffffffffffffffffff16610100820152600090818080611fed84613592565b92509250925061200389858a8a8787878d613784565b9998505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff81166000908152602080526040902080546060919061159690615161565b61204b61318b565b73ffffffffffffffffffffffffffffffffffffffff83166000908152602080526040902061207a828483615203565b508273ffffffffffffffffffffffffffffffffffffffff167f7c44b4eb59ee7873514e7e43e7718c269d872965938b288aa143befca62f99d283836040516110fb92919061531e565b73ffffffffffffffffffffffffffffffffffffffff81166000818152602160205260408082205490517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152919290916370a0823190602401602060405180830381865afa15801561213f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121639190615524565b6110a191906150b8565b60015473ffffffffffffffffffffffffffffffffffffffff1633146121f3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b6122776131eb565b601480547fffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffff167601000000000000000000000000000000000000000000001790556040513381527f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258906020016118ea565b600e5460609060008167ffffffffffffffff811115612309576123096150cb565b60405190808252806020026020018201604052801561234e57816020015b60408051808201909152600080825260208201528152602001906001900390816123275790505b50905060005b828110156123f3576000600e8281548110612371576123716150fa565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff9081168084526011835260409384902054845180860190955281855290911691830182905285519093509091908590859081106123d3576123d36150fa565b6020026020010181905250505080806123eb90615129565b915050612354565b5092915050565b73ffffffffffffffffffffffffffffffffffffffff8116612447576040517f9c8d2cd200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600160255460ff16600181111561246057612460614af5565b03612497576040517f4a3578fb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8281166000908152601160205260409020541633146124f7576040517fcebf515b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601454600e5460009161251a9185916bffffffffffffffffffffffff16906132bd565b73ffffffffffffffffffffffffffffffffffffffff8085166000908152600b6020908152604080832080547fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff1690557f000000000000000000000000000000000000000000000000000000000000000090931682526021905220549091506125b1906bffffffffffffffffffffffff8316906150b8565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000081166000818152602160205260408082209490945592517fa9059cbb00000000000000000000000000000000000000000000000000000000815291851660048301526bffffffffffffffffffffffff841660248301529063a9059cbb906044016020604051808303816000875af1158015612666573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061268a919061553d565b9050806126c3576040517f90b8ec1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405133815273ffffffffffffffffffffffffffffffffffffffff808516916bffffffffffffffffffffffff8516918716907f9819093176a1851202c7bcfa46845809b4e47c261866550e94ed3775d2f406989060200160405180910390a450505050565b6127306131eb565b602580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055565b73ffffffffffffffffffffffffffffffffffffffff8181166000908152601260205260409020541633146127ba576040517f6752e7aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81811660008181526011602090815260408083208054337fffffffffffffffffffffffff000000000000000000000000000000000000000080831682179093556012909452828520805490921690915590519416939092849290917f78af32efdcad432315431e9b03d27e6cd98fb79c405fdc5af7c1714d9c0f75b39190a45050565b6000818152600460209081526040808320815161012081018352815460ff8082161515835261010080830490911615159583019590955263ffffffff620100008204811694830194909452660100000000000081048416606083015273ffffffffffffffffffffffffffffffffffffffff6a01000000000000000000009091048116608083015260018301546fffffffffffffffffffffffffffffffff811660a08401526bffffffffffffffffffffffff70010000000000000000000000000000000082041660c08401527c0100000000000000000000000000000000000000000000000000000000900490931660e08201526002909101549091169181019190915261297283612962816134e7565b8360400151846101000151611eb5565b9392505050565b606060248054806020026020016040519081016040528092919081815260200182805480156129de57602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116129b3575b5050505050905090565b604080516102008101825260008082526020820181905291810182905260608082018390526080820183905260a0820183905260c0820183905260e08201839052610100820183905261012082018390526101408201839052610160820183905261018082018390526101a082018390526101c08201526101e0810191909152604080516102008101825260165463ffffffff74010000000000000000000000000000000000000000808304821684527801000000000000000000000000000000000000000000000000808404831660208601526017547c0100000000000000000000000000000000000000000000000000000000810484169686019690965273ffffffffffffffffffffffffffffffffffffffff938416606086015260145460ff828204161515608087015262ffffff70010000000000000000000000000000000082041660a0870152601854928304841660c087015290820490921660e085015293821661010084015261ffff7301000000000000000000000000000000000000009091041661012083015291909116610140820152601954610160820152601a54610180820152601b546101a08201526101c08101612baa60096131de565b815260155473ffffffffffffffffffffffffffffffffffffffff16602090910152919050565b604080516101408101825260008082526020820181905260609282018390528282018190526080820181905260a0820181905260c0820181905260e082018190526101008201526101208101919091526000828152600460209081526040808320815161012081018352815460ff8082161515835261010080830490911615159583019590955263ffffffff620100008204811694830194909452660100000000000081048416606083015273ffffffffffffffffffffffffffffffffffffffff6a010000000000000000000090910481166080830181905260018401546fffffffffffffffffffffffffffffffff811660a08501526bffffffffffffffffffffffff70010000000000000000000000000000000082041660c08501527c0100000000000000000000000000000000000000000000000000000000900490941660e08301526002909201549091169281019290925290919015612da557816080015173ffffffffffffffffffffffffffffffffffffffff1663f00e6a2a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612d7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612da0919061555f565b612da8565b60005b90506040518061014001604052808273ffffffffffffffffffffffffffffffffffffffff168152602001836040015163ffffffff168152602001600760008781526020019081526020016000208054612e0090615161565b80601f0160208091040260200160405190810160405280929190818152602001828054612e2c90615161565b8015612e795780601f10612e4e57610100808354040283529160200191612e79565b820191906000526020600020905b815481529060010190602001808311612e5c57829003601f168201915b505050505081526020018360c001516bffffffffffffffffffffffff1681526020016005600087815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001836060015163ffffffff1667ffffffffffffffff1681526020018360e0015163ffffffff1681526020018360a001516bffffffffffffffffffffffff168152602001836000015115158152602001601e60008781526020019081526020016000208054612f5690615161565b80601f0160208091040260200160405190810160405280929190818152602001828054612f8290615161565b8015612fcf5780601f10612fa457610100808354040283529160200191612fcf565b820191906000526020600020905b815481529060010190602001808311612fb257829003601f168201915b505050505081525092505050919050565b6000612fea613af4565b905090565b60006110a182612852565b73ffffffffffffffffffffffffffffffffffffffff82811660009081526011602052604090205416331461305a576040517fcebf515b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff8216036130a9576040517f8c8728c700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8281166000908152601260205260409020548116908216146131555773ffffffffffffffffffffffffffffffffffffffff82811660008181526012602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055513392917f84f7c7c80bb8ed2279b4aab5f61cd05e6374073d38f46d7f32de8c30e9e3836791a45b5050565b6131616131eb565b61316a81613bbe565b50565b6000612fea60025b60006110a1825490565b60006129728383613cb3565b60175473ffffffffffffffffffffffffffffffffffffffff1633146131dc576040517f77c3599200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b6060600061297283613cdd565b60005473ffffffffffffffffffffffffffffffffffffffff1633146131dc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016121ea565b60185473ffffffffffffffffffffffffffffffffffffffff1633146131dc576040517fb6dfb7a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff83166000908152600b602090815260408083208151608081018352905460ff80821615801584526101008304909116948301949094526bffffffffffffffffffffffff6201000082048116938301939093526e01000000000000000000000000000090049091166060820152906134b9576000816060015185613355919061541d565b905060006133638583615471565b90508083604001818151613377919061549c565b6bffffffffffffffffffffffff16905250613392858261557c565b836060018181516133a3919061549c565b6bffffffffffffffffffffffff90811690915273ffffffffffffffffffffffffffffffffffffffff89166000908152600b602090815260409182902087518154928901519389015160608a015186166e010000000000000000000000000000027fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff919096166201000002167fffffffffffff000000000000000000000000000000000000000000000000ffff60ff95909516610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909416939093171792909216179190911790555050505b60400151949350505050565b60006129728373ffffffffffffffffffffffffffffffffffffffff8416613d38565b6000818160045b600f811015613574577fff00000000000000000000000000000000000000000000000000000000000000821683826020811061352c5761352c6150fa565b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161461356257506000949350505050565b8061356c81615129565b9150506134ee565b5081600f1a600181111561358a5761358a614af5565b949350505050565b600080600080846040015162ffffff1690506000808263ffffffff161190506000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa15801561361f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061364391906155c3565b509450909250505060008113158061365a57508142105b8061367b575082801561367b575061367282426150b8565b8463ffffffff16105b1561368a57601954965061368e565b8096505b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa1580156136f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061371d91906155c3565b509450909250505060008113158061373457508142105b806137555750828015613755575061374c82426150b8565b8463ffffffff16105b1561376457601a549550613768565b8095505b86866137738a613e2b565b965096509650505050509193909250565b600080808089600181111561379b5761379b614af5565b036137aa575062017f986137ff565b60018960018111156137be576137be614af5565b036137cd57506201de846137ff565b6040517ff2b2d41200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008a6080015160016138129190615613565b6138209060ff16604061562c565b60185461384e906103a49074010000000000000000000000000000000000000000900463ffffffff166150a5565b61385891906150a5565b601554604080517fde9ee35e0000000000000000000000000000000000000000000000000000000081528151939450600093849373ffffffffffffffffffffffffffffffffffffffff169263de9ee35e92600480820193918290030181865afa1580156138c9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138ed9190615643565b909250905081836138ff8360186150a5565b613909919061562c565b60808f0151613919906001615613565b6139289060ff166115e061562c565b61393291906150a5565b61393c91906150a5565b61394690856150a5565b6101008e01516040517f125441400000000000000000000000000000000000000000000000000000000081526004810186905291955073ffffffffffffffffffffffffffffffffffffffff1690631254414090602401602060405180830381865afa1580156139b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139dd9190615524565b8d6060015161ffff166139f0919061562c565b94505050506000613a018b86613f15565b60008d815260046020526040902054909150610100900460ff1615613a665760008c81526023602090815260409182902082518084018452905463ffffffff811680835264010000000090910462ffffff908116928401928352928501525116908201525b6000613ad08c6040518061012001604052808d63ffffffff1681526020018681526020018781526020018c81526020018b81526020018a81526020018973ffffffffffffffffffffffffffffffffffffffff16815260200185815260200160001515815250614091565b60208101518151919250613ae39161549c565b9d9c50505050505050505050505050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166000818152602160205260408082205490517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152919290916370a0823190602401602060405180830381865afa158015613b90573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bb49190615524565b612fea9190615667565b3373ffffffffffffffffffffffffffffffffffffffff821603613c3d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016121ea565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000826000018281548110613cca57613cca6150fa565b9060005260206000200154905092915050565b60608160000180548060200260200160405190810160405280929190818152602001828054801561160f57602002820191906000526020600020905b815481526020019060010190808311613d195750505050509050919050565b60008181526001830160205260408120548015613e21576000613d5c6001836150b8565b8554909150600090613d70906001906150b8565b9050818114613dd5576000866000018281548110613d9057613d906150fa565b9060005260206000200154905080876000018481548110613db357613db36150fa565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080613de657613de6615687565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506110a1565b60009150506110a1565b60008060007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015613e9b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ebf91906155c3565b50935050925050600082131580613ed557508042105b80613f0557506000846040015162ffffff16118015613f055750613ef981426150b8565b846040015162ffffff16105b156123f3575050601b5492915050565b60408051608081018252600080825260208083018281528385018381526060850184905273ffffffffffffffffffffffffffffffffffffffff878116855260229093528584208054640100000000810462ffffff1690925263ffffffff82169092527b01000000000000000000000000000000000000000000000000000000810460ff16855285517ffeaf968c00000000000000000000000000000000000000000000000000000000815295519495919484936701000000000000009092049091169163feaf968c9160048083019260a09291908290030181865afa158015614002573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061402691906155c3565b5093505092505060008213158061403c57508042105b8061406c57506000866040015162ffffff1611801561406c575061406081426150b8565b866040015162ffffff16105b156140805760018301546060850152614088565b606084018290525b50505092915050565b6040805161010081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081019190915260008260e001516000015160ff1690506000846060015161ffff1684606001516140fc919061562c565b9050836101000151801561410f5750803a105b1561411757503a5b60006012831161412857600161413e565b6141336012846150b8565b61413e90600a6157d6565b9050600060128410614151576001614167565b61415c8460126150b8565b61416790600a6157d6565b905060008660a0015187604001518860200151896000015161418991906150a5565b614193908761562c565b61419d91906150a5565b6141a7919061562c565b9050614203828860e00151606001516141c0919061562c565b6001848a60e00151606001516141d6919061562c565b6141e091906150b8565b6141ea868561562c565b6141f491906150a5565b6141fe91906157e2565b6143d8565b6bffffffffffffffffffffffff1686526080870151614226906141fe90836157e2565b6bffffffffffffffffffffffff1660408088019190915260e0880151015160009061425f9062ffffff16683635c9adc5dea0000061562c565b9050600081633b9aca008a60a001518b60e001516020015163ffffffff168c604001518d600001518b614292919061562c565b61429c91906150a5565b6142a6919061562c565b6142b0919061562c565b6142ba91906157e2565b6142c491906150a5565b9050614307848a60e00151606001516142dd919061562c565b6001868c60e00151606001516142f3919061562c565b6142fd91906150b8565b6141ea888561562c565b6bffffffffffffffffffffffff166020890152608089015161432d906141fe90836157e2565b6bffffffffffffffffffffffff16606089015260c089015173ffffffffffffffffffffffffffffffffffffffff166080808a0191909152890151614370906143d8565b6bffffffffffffffffffffffff1660a0808a0191909152890151614393906143d8565b6bffffffffffffffffffffffff1660c089015260e0890151606001516143b8906143d8565b6bffffffffffffffffffffffff1660e08901525050505050505092915050565b60006bffffffffffffffffffffffff821115614476576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201527f362062697473000000000000000000000000000000000000000000000000000060648201526084016121ea565b5090565b6000806040838503121561448d57600080fd5b50508035926020909101359150565b600081518084526020808501945080840160005b838110156144cc578151875295820195908201906001016144b0565b509495945050505050565b602081526000612972602083018461449c565b60008083601f8401126144fc57600080fd5b50813567ffffffffffffffff81111561451457600080fd5b60208301915083602082850101111561452c57600080fd5b9250929050565b60008060006040848603121561454857600080fd5b83359250602084013567ffffffffffffffff81111561456657600080fd5b614572868287016144ea565b9497909650939450505050565b600081518084526020808501945080840160005b838110156144cc57815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101614593565b805163ffffffff16825260006101e060208301516145eb602086018263ffffffff169052565b506040830151614603604086018263ffffffff169052565b50606083015161461a606086018262ffffff169052565b506080830151614630608086018261ffff169052565b5060a083015161465060a08601826bffffffffffffffffffffffff169052565b5060c083015161466860c086018263ffffffff169052565b5060e083015161468060e086018263ffffffff169052565b506101008381015163ffffffff908116918601919091526101208085015190911690850152610140808401519085015261016080840151908501526101808084015173ffffffffffffffffffffffffffffffffffffffff16908501526101a0808401518186018390526146f58387018261457f565b925050506101c0808401516147218287018273ffffffffffffffffffffffffffffffffffffffff169052565b5090949350505050565b855163ffffffff16815260006101c0602088015161475960208501826bffffffffffffffffffffffff169052565b5060408801516040840152606088015161478360608501826bffffffffffffffffffffffff169052565b506080880151608084015260a08801516147a560a085018263ffffffff169052565b5060c08801516147bd60c085018263ffffffff169052565b5060e088015160e0840152610100808901516147e08286018263ffffffff169052565b5050610120888101511515908401526101408301819052614803818401886145c5565b9050828103610160840152614818818761457f565b905082810361018084015261482d818661457f565b9150506148406101a083018460ff169052565b9695505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461316a57600080fd5b6000806040838503121561487f57600080fd5b823561488a8161484a565b915060208301356004811061489e57600080fd5b809150509250929050565b6000602082840312156148bb57600080fd5b5035919050565b6000815180845260005b818110156148e8576020818501810151868301820152016148cc565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b60208152600061297260208301846148c2565b815173ffffffffffffffffffffffffffffffffffffffff1681526101608101602083015161496f602084018263ffffffff169052565b506040830151614987604084018263ffffffff169052565b50606083015161499f606084018263ffffffff169052565b5060808301516149c7608084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060a08301516149df60a084018263ffffffff169052565b5060c08301516149f760c084018263ffffffff169052565b5060e0830151614a0f60e084018263ffffffff169052565b506101008381015173ffffffffffffffffffffffffffffffffffffffff81168483015250506101208381015163ffffffff81168483015250506101408381015163ffffffff8116848301525b505092915050565b60008060208385031215614a7657600080fd5b823567ffffffffffffffff80821115614a8e57600080fd5b818501915085601f830112614aa257600080fd5b813581811115614ab157600080fd5b8660208260051b8501011115614ac657600080fd5b60209290920196919550909350505050565b600060208284031215614aea57600080fd5b81356129728161484a565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6002811061316a5761316a614af5565b60208101614b4183614b24565b91905290565b60008060008060808587031215614b5d57600080fd5b84359350602085013560028110614b7357600080fd5b9250604085013563ffffffff81168114614b8c57600080fd5b91506060850135614b9c8161484a565b939692955090935050565b600080600060408486031215614bbc57600080fd5b8335614bc78161484a565b9250602084013567ffffffffffffffff81111561456657600080fd5b602080825282518282018190526000919060409081850190868401855b82811015614c3f578151805173ffffffffffffffffffffffffffffffffffffffff90811686529087015116868501529284019290850190600101614c00565b5091979650505050505050565b60008060408385031215614c5f57600080fd5b8235614c6a8161484a565b9150602083013561489e8161484a565b6020808252825182820181905260009190848201906040850190845b81811015614cc857835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101614c96565b50909695505050505050565b60208152614ceb60208201835163ffffffff169052565b60006020830151614d04604084018263ffffffff169052565b50604083015163ffffffff8116606084015250606083015173ffffffffffffffffffffffffffffffffffffffff8116608084015250608083015180151560a08401525060a083015162ffffff811660c08401525060c083015163ffffffff811660e08401525060e0830151610100614d838185018363ffffffff169052565b8401519050610120614dac8482018373ffffffffffffffffffffffffffffffffffffffff169052565b8401519050610140614dc38482018361ffff169052565b8401519050610160614dec8482018373ffffffffffffffffffffffffffffffffffffffff169052565b840151610180848101919091528401516101a0808501919091528401516101c0808501919091528401516102006101e080860182905291925090614e3461022086018461457f565b9086015173ffffffffffffffffffffffffffffffffffffffff811683870152909250614721565b60208152614e8260208201835173ffffffffffffffffffffffffffffffffffffffff169052565b60006020830151614e9b604084018263ffffffff169052565b506040830151610140806060850152614eb86101608501836148c2565b91506060850151614ed960808601826bffffffffffffffffffffffff169052565b50608085015173ffffffffffffffffffffffffffffffffffffffff811660a08601525060a085015167ffffffffffffffff811660c08601525060c085015163ffffffff811660e08601525060e0850151610100614f45818701836bffffffffffffffffffffffff169052565b8601519050610120614f5a8682018315159052565b8601518584037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00183870152905061484083826148c2565b6000610120820190506bffffffffffffffffffffffff835116825263ffffffff60208401511660208301526040830151614fd3604084018262ffffff169052565b506060830151614fe9606084018261ffff169052565b506080830151614ffe608084018260ff169052565b5060a083015161501260a084018215159052565b5060c083015161502660c084018215159052565b5060e083015161503a60e084018215159052565b506101008381015173ffffffffffffffffffffffffffffffffffffffff811684830152614a5b565b6020810160048310614b4157614b41614af5565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808201808211156110a1576110a1615076565b818103818111156110a1576110a1615076565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361515a5761515a615076565b5060010190565b600181811c9082168061517557607f821691505b6020821081036151ae577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f8211156151fe57600081815260208120601f850160051c810160208610156151db5750805b601f850160051c820191505b818110156151fa578281556001016151e7565b5050505b505050565b67ffffffffffffffff83111561521b5761521b6150cb565b61522f836152298354615161565b836151b4565b6000601f841160018114615281576000851561524b5750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355615317565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156152d057868501358255602094850194600190920191016152b0565b508682101561530b577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b60208152816020820152818360408301376000818301604090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160101919050565b6000604082016040835280865480835260608501915087600052602092508260002060005b828110156153c257815473ffffffffffffffffffffffffffffffffffffffff1684529284019260019182019101615390565b505050838103828501528481528590820160005b868110156154115782356153e98161484a565b73ffffffffffffffffffffffffffffffffffffffff16825291830191908301906001016153d6565b50979650505050505050565b6bffffffffffffffffffffffff8281168282160390808211156123f3576123f3615076565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006bffffffffffffffffffffffff8084168061549057615490615442565b92169190910492915050565b6bffffffffffffffffffffffff8181168382160190808211156123f3576123f3615076565b6000816154d0576154d0615076565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b604081526000615509604083018561457f565b828103602084015261551b818561449c565b95945050505050565b60006020828403121561553657600080fd5b5051919050565b60006020828403121561554f57600080fd5b8151801515811461297257600080fd5b60006020828403121561557157600080fd5b81516129728161484a565b6bffffffffffffffffffffffff818116838216028082169190828114614a5b57614a5b615076565b805169ffffffffffffffffffff811681146155be57600080fd5b919050565b600080600080600060a086880312156155db57600080fd5b6155e4866155a4565b9450602086015193506040860151925060608601519150615607608087016155a4565b90509295509295909350565b60ff81811683821601908111156110a1576110a1615076565b80820281158282048414176110a1576110a1615076565b6000806040838503121561565657600080fd5b505080516020909101519092909150565b81810360008312801583831316838312821617156123f3576123f3615076565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b600181815b8085111561570f57817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156156f5576156f5615076565b8085161561570257918102915b93841c93908002906156bb565b509250929050565b600082615726575060016110a1565b81615733575060006110a1565b816001811461574957600281146157535761576f565b60019150506110a1565b60ff84111561576457615764615076565b50506001821b6110a1565b5060208310610133831016604e8410600b8410161715615792575081810a6110a1565b61579c83836156b6565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156157ce576157ce615076565b029392505050565b60006129728383615717565b6000826157f1576157f1615442565b50049056fea164736f6c6343000813000a", + Bin: "0x6101606040523480156200001257600080fd5b5060405162005c8738038062005c87833981016040819052620000359162000309565b87878787878787873380600081620000945760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000c757620000c78162000241565b5050506001600160a01b0380891660805287811660a05286811660c05285811660e052848116610100528316610120526025805483919060ff191660018381811115620001185762000118620003b6565b0217905550806001600160a01b0316610140816001600160a01b03168152505060c0516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000179573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200019f9190620003cc565b60ff1660a0516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001e3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002099190620003cc565b60ff16146200022b576040516301f86e1760e41b815260040160405180910390fd5b50505050505050505050505050505050620003f8565b336001600160a01b038216036200029b5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016200008b565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b80516001600160a01b03811681146200030457600080fd5b919050565b600080600080600080600080610100898b0312156200032757600080fd5b6200033289620002ec565b97506200034260208a01620002ec565b96506200035260408a01620002ec565b95506200036260608a01620002ec565b94506200037260808a01620002ec565b93506200038260a08a01620002ec565b925060c0890151600281106200039757600080fd5b9150620003a760e08a01620002ec565b90509295985092959890939650565b634e487b7160e01b600052602160045260246000fd5b600060208284031215620003df57600080fd5b815160ff81168114620003f157600080fd5b9392505050565b60805160a05160c05160e051610100516101205161014051615803620004846000396000610b7001526000610ad2015260006107be0152600081816109a001526135b60152600081816109540152613e3201526000818161051d0152613690015260008181610c1801528181611df00152818161256b015281816125c80152613af601526158036000f3fe608060405234801561001057600080fd5b50600436106103d05760003560e01c80638ed02bab116101ff578063c3f909d41161011a578063eb5dcd6c116100ad578063f5a418461161007c578063f5a4184614610eb4578063f777ff0614610f35578063faa3e99614610f3c578063ffd242bd14610f8257600080fd5b8063eb5dcd6c14610de5578063ec4de4ba14610df8578063ed56b3e114610e2e578063f2fde38b14610ea157600080fd5b8063d09dc339116100e9578063d09dc33914610c3c578063d763264814610c44578063d85aa07c14610c57578063e80a3d4414610c5f57600080fd5b8063c3f909d414610bd6578063c5b964e014610beb578063c7c3a19a14610bf6578063ca30e60314610c1657600080fd5b8063aab9edd611610192578063b3596c2311610161578063b3596c23146107e2578063b6511a2a14610ba7578063b657bc9c14610bae578063ba87666814610bc157600080fd5b8063aab9edd614610b57578063abc76ae014610b66578063ac4dc59a14610b6e578063b121e14714610b9457600080fd5b8063a08714c0116101ce578063a08714c014610ad0578063a538b2eb14610af6578063a710b22114610b3c578063a87f45fe14610b4f57600080fd5b80638ed02bab14610a5c5780639089daa414610a7a57806393f6ebcf14610a8f5780639e0a99ed14610ac857600080fd5b806343cc055c116102ef5780636209e1e91161028257806379ba50971161025157806379ba5097146109ea57806379ea9943146109f25780638456cb5914610a365780638da5cb5b14610a3e57600080fd5b80636209e1e91461098b5780636709d0e51461099e578063671d36ed146109c45780636eec02a2146109d757600080fd5b80635425d8ac116102be5780635425d8ac146107bc57806357359584146107e2578063614486af146109525780636181d82d1461097857600080fd5b806343cc055c1461074957806344cb70b8146107705780634ca16c52146107935780635147cd591461079c57600080fd5b8063207b6516116103675780633b9cce59116103365780633b9cce59146106c05780633f4ba83a146106d3578063421d183b146106db57806343b46e5f1461074157600080fd5b8063207b651614610508578063226cf83c1461051b578063232c1cc5146105625780633408f73a1461056957600080fd5b8063187256e8116103a3578063187256e81461043b57806319d97a941461044e5780631e0104391461046e5780631efcf646146104d057600080fd5b8063050ee65d146103d557806306e3b632146103ed5780630b7d33e61461040d5780631865c57d14610422575b600080fd5b6201e26c5b6040519081526020015b60405180910390f35b6104006103fb36600461447a565b610f8a565b6040516103e491906144d7565b61042061041b366004614533565b6110a7565b005b61042a611108565b6040516103e495949392919061472b565b61042061044936600461486c565b611508565b61046161045c3660046148a9565b611579565b6040516103e49190614926565b6104b361047c3660046148a9565b60009081526004602052604090206001015470010000000000000000000000000000000090046bffffffffffffffffffffffff1690565b6040516bffffffffffffffffffffffff90911681526020016103e4565b6104f86104de3660046148a9565b600090815260046020526040902054610100900460ff1690565b60405190151581526020016103e4565b6104616105163660046148a9565b61161b565b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016103e4565b60186103da565b6106b36040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081018290526101008101829052610120810182905261014081019190915250604080516101608101825260165473ffffffffffffffffffffffffffffffffffffffff808216835263ffffffff740100000000000000000000000000000000000000008084048216602086015278010000000000000000000000000000000000000000000000008085048316968601969096527c010000000000000000000000000000000000000000000000000000000093849004821660608601526017548084166080870152818104831660a0870152868104831660c087015293909304811660e085015260185491821661010085015291810482166101208401529290920490911661014082015290565b6040516103e49190614939565b6104206106ce366004614a63565b611638565b61042061188e565b6106ee6106e9366004614ad8565b6118f4565b60408051951515865260ff90941660208601526bffffffffffffffffffffffff9283169385019390935216606083015273ffffffffffffffffffffffffffffffffffffffff16608082015260a0016103e4565b610420611a13565b6014547801000000000000000000000000000000000000000000000000900460ff166104f8565b6104f861077e3660046148a9565b60009081526008602052604090205460ff1690565b62017f986103da565b6107af6107aa3660046148a9565b611eaa565b6040516103e49190614b34565b7f000000000000000000000000000000000000000000000000000000000000000061053d565b6108d46107f0366004614ad8565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a08101919091525073ffffffffffffffffffffffffffffffffffffffff908116600090815260226020908152604091829020825160c081018452815463ffffffff81168252640100000000810462ffffff16938201939093526701000000000000008304909416928401929092527b01000000000000000000000000000000000000000000000000000000900460ff16606083015260018101546080830152600201546bffffffffffffffffffffffff1660a082015290565b6040516103e49190600060c08201905063ffffffff835116825262ffffff602084015116602083015273ffffffffffffffffffffffffffffffffffffffff604084015116604083015260ff6060840151166060830152608083015160808301526bffffffffffffffffffffffff60a08401511660a083015292915050565b7f000000000000000000000000000000000000000000000000000000000000000061053d565b6104b3610986366004614b47565b611eb5565b610461610999366004614ad8565b612010565b7f000000000000000000000000000000000000000000000000000000000000000061053d565b6104206109d2366004614ba7565b612043565b6103da6109e5366004614ad8565b6120c3565b61042061216d565b61053d610a003660046148a9565b6000908152600460205260409020546a0100000000000000000000900473ffffffffffffffffffffffffffffffffffffffff1690565b61042061226f565b60005473ffffffffffffffffffffffffffffffffffffffff1661053d565b60155473ffffffffffffffffffffffffffffffffffffffff1661053d565b610a826122e8565b6040516103e49190614be3565b61053d610a9d3660046148a9565b60009081526004602052604090206002015473ffffffffffffffffffffffffffffffffffffffff1690565b6103a46103da565b7f000000000000000000000000000000000000000000000000000000000000000061053d565b6104f8610b04366004614ad8565b73ffffffffffffffffffffffffffffffffffffffff908116600090815260226020526040902054670100000000000000900416151590565b610420610b4a366004614c4c565b6123fa565b610420612728565b604051600481526020016103e4565b6115e06103da565b7f000000000000000000000000000000000000000000000000000000000000000061053d565b610420610ba2366004614ad8565b61275a565b60326103da565b6104b3610bbc3660046148a9565b612852565b610bc9612979565b6040516103e49190614c7a565b610bde6129e8565b6040516103e49190614cd4565b60255460ff166107af565b610c09610c043660046148a9565b612bd0565b6040516103e49190614e5b565b7f000000000000000000000000000000000000000000000000000000000000000061053d565b6103da612fe0565b6104b3610c523660046148a9565b612fef565b601b546103da565b610dd86040805161012081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081018290526101008101919091525060408051610120810182526014546bffffffffffffffffffffffff8116825263ffffffff6c01000000000000000000000000820416602083015262ffffff7001000000000000000000000000000000008204169282019290925261ffff730100000000000000000000000000000000000000830416606082015260ff750100000000000000000000000000000000000000000083048116608083015276010000000000000000000000000000000000000000000083048116151560a08301527701000000000000000000000000000000000000000000000083048116151560c08301527801000000000000000000000000000000000000000000000000909204909116151560e082015260155473ffffffffffffffffffffffffffffffffffffffff1661010082015290565b6040516103e49190614f92565b610420610df3366004614c4c565b612ffa565b6103da610e06366004614ad8565b73ffffffffffffffffffffffffffffffffffffffff1660009081526021602052604090205490565b610e88610e3c366004614ad8565b73ffffffffffffffffffffffffffffffffffffffff166000908152600c602090815260409182902082518084019093525460ff8082161515808552610100909204169290910182905291565b60408051921515835260ff9091166020830152016103e4565b610420610eaf366004614ad8565b613159565b610f0f610ec23660046148a9565b60408051808201909152600080825260208201525060009081526023602090815260409182902082518084019093525463ffffffff81168352640100000000900462ffffff169082015290565b60408051825163ffffffff16815260209283015162ffffff1692810192909252016103e4565b60406103da565b610f75610f4a366004614ad8565b73ffffffffffffffffffffffffffffffffffffffff166000908152601c602052604090205460ff1690565b6040516103e49190615062565b6103da61316d565b60606000610f986002613175565b9050808410610fd3576040517f1390f2a100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610fdf84866150a5565b905081811180610fed575083155b610ff75780610ff9565b815b9050600061100786836150b8565b67ffffffffffffffff81111561101f5761101f6150cb565b604051908082528060200260200182016040528015611048578160200160208202803683370190505b50905060005b815181101561109b5761106c61106488836150a5565b60029061317f565b82828151811061107e5761107e6150fa565b60209081029190910101528061109381615129565b91505061104e565b50925050505b92915050565b6110af61318b565b6000838152601f602052604090206110c8828483615203565b50827f2fd8d70753a007014349d4591843cc031c2dd7a260d7dd82eca8253686ae776983836040516110fb92919061531e565b60405180910390a2505050565b6040805161014081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081018290526101008101829052610120810191909152604080516101e08101825260008082526020820181905291810182905260608082018390526080820183905260a0820183905260c0820183905260e08201839052610100820183905261012082018390526101408201839052610160820183905261018082018390526101a08201526101c081019190915260408051610140810182526016547c0100000000000000000000000000000000000000000000000000000000900463ffffffff1681526000602082018190529181018290526014546bffffffffffffffffffffffff16606080830191909152918291608081016112416002613175565b81526017547401000000000000000000000000000000000000000080820463ffffffff908116602080860191909152780100000000000000000000000000000000000000000000000080850483166040808801919091526013546060808901919091526014546c01000000000000000000000000810486166080808b0191909152760100000000000000000000000000000000000000000000820460ff16151560a09a8b015283516101e0810185526000808252968101879052601654898104891695820195909552700100000000000000000000000000000000830462ffffff169381019390935273010000000000000000000000000000000000000090910461ffff169082015296870192909252808204831660c08701527c0100000000000000000000000000000000000000000000000000000000909404821660e086015260185492830482166101008601529290910416610120830152601954610140830152601a5461016083015273ffffffffffffffffffffffffffffffffffffffff166101808201529095506101a081016113dc60096131de565b815260175473ffffffffffffffffffffffffffffffffffffffff16602091820152601454600d80546040805182860281018601909152818152949850899489949293600e93750100000000000000000000000000000000000000000090910460ff1692859183018282801561148757602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff16815260019091019060200180831161145c575b50505050509250818054806020026020016040519081016040528092919081815260200182805480156114f057602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116114c5575b50505050509150945094509450945094509091929394565b6115106131eb565b73ffffffffffffffffffffffffffffffffffffffff82166000908152601c6020526040902080548291907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600183600381111561157057611570614af5565b02179055505050565b6000818152601f6020526040902080546060919061159690615161565b80601f01602080910402602001604051908101604052809291908181526020018280546115c290615161565b801561160f5780601f106115e45761010080835404028352916020019161160f565b820191906000526020600020905b8154815290600101906020018083116115f257829003601f168201915b50505050509050919050565b6000818152601d6020526040902080546060919061159690615161565b6116406131eb565b600e54811461167b576040517fcf54c06a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b600e5481101561184d576000600e828154811061169d5761169d6150fa565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff9081168084526011909252604083205491935016908585858181106116e7576116e76150fa565b90506020020160208101906116fc9190614ad8565b905073ffffffffffffffffffffffffffffffffffffffff8116158061178f575073ffffffffffffffffffffffffffffffffffffffff82161580159061176d57508073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b801561178f575073ffffffffffffffffffffffffffffffffffffffff81811614155b156117c6576040517fb387a23800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff818116146118375773ffffffffffffffffffffffffffffffffffffffff838116600090815260116020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169183169190911790555b505050808061184590615129565b91505061167e565b507fa46de38886467c59be07a0675f14781206a5477d871628af46c2443822fcb725600e83836040516118829392919061536b565b60405180910390a15050565b6118966131eb565b601480547fffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffff1690556040513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa906020015b60405180910390a1565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600b602090815260408083208151608081018352905460ff80821615801584526101008304909116948301949094526bffffffffffffffffffffffff6201000082048116938301939093526e0100000000000000000000000000009004909116606082015282918291829182919082906119ba5760608201516014546000916119a6916bffffffffffffffffffffffff1661541d565b600e549091506119b69082615471565b9150505b8151602083015160408401516119d190849061549c565b6060949094015173ffffffffffffffffffffffffffffffffffffffff9a8b16600090815260116020526040902054929b919a9499509750921694509092505050565b611a1b61326c565b600060255460ff166001811115611a3457611a34614af5565b03611a6b576040517fe0262d7400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601454600e546bffffffffffffffffffffffff909116906000611a8e600f613175565b90506000611a9c82846150a5565b905060008167ffffffffffffffff811115611ab957611ab96150cb565b604051908082528060200260200182016040528015611ae2578160200160208202803683370190505b50905060008267ffffffffffffffff811115611b0057611b006150cb565b604051908082528060200260200182016040528015611b29578160200160208202803683370190505b50905060005b85811015611c5b576000600e8281548110611b4c57611b4c6150fa565b600091825260208220015473ffffffffffffffffffffffffffffffffffffffff169150611b7a828a8a6132bd565b9050806bffffffffffffffffffffffff16858481518110611b9d57611b9d6150fa565b60209081029190910181019190915273ffffffffffffffffffffffffffffffffffffffff808416600090815260119092526040909120548551911690859085908110611beb57611beb6150fa565b73ffffffffffffffffffffffffffffffffffffffff92831660209182029290920181019190915292166000908152600b909252506040902080547fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff16905580611c5381615129565b915050611b2f565b5060005b84811015611dd8576000611c74600f8361317f565b73ffffffffffffffffffffffffffffffffffffffff8082166000818152600b602090815260408083208151608081018352905460ff80821615158352610100820416828501526bffffffffffffffffffffffff6201000082048116838501526e0100000000000000000000000000009091041660608201529383526011909152902054929350911684611d078a866150a5565b81518110611d1757611d176150fa565b73ffffffffffffffffffffffffffffffffffffffff9092166020928302919091019091015260408101516bffffffffffffffffffffffff1685611d5a8a866150a5565b81518110611d6a57611d6a6150fa565b60209081029190910181019190915273ffffffffffffffffffffffffffffffffffffffff9092166000908152600b909252506040902080547fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff16905580611dd081615129565b915050611c5f565b5073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166000908152602160205260408120819055611e2b600f613175565b90505b8015611e6857611e55611e4d611e456001846150b8565b600f9061317f565b600f906134c5565b5080611e60816154c1565b915050611e2e565b507f5af23b715253628d12b660b27a4f3fc626562ea8a55040aa99ab3dc178989fad8183604051611e9a9291906154f6565b60405180910390a1505050505050565b60006110a1826134e7565b60408051610120810182526014546bffffffffffffffffffffffff8116825263ffffffff6c01000000000000000000000000820416602083015262ffffff7001000000000000000000000000000000008204169282019290925261ffff730100000000000000000000000000000000000000830416606082015260ff750100000000000000000000000000000000000000000083048116608083015276010000000000000000000000000000000000000000000083048116151560a08301527701000000000000000000000000000000000000000000000083048116151560c08301527801000000000000000000000000000000000000000000000000909204909116151560e082015260155473ffffffffffffffffffffffffffffffffffffffff16610100820152600090818080611fed84613592565b92509250925061200389858a8a8787878d613784565b9998505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff81166000908152602080526040902080546060919061159690615161565b61204b61318b565b73ffffffffffffffffffffffffffffffffffffffff83166000908152602080526040902061207a828483615203565b508273ffffffffffffffffffffffffffffffffffffffff167f7c44b4eb59ee7873514e7e43e7718c269d872965938b288aa143befca62f99d283836040516110fb92919061531e565b73ffffffffffffffffffffffffffffffffffffffff81166000818152602160205260408082205490517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152919290916370a0823190602401602060405180830381865afa15801561213f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121639190615524565b6110a191906150b8565b60015473ffffffffffffffffffffffffffffffffffffffff1633146121f3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b6122776131eb565b601480547fffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffff167601000000000000000000000000000000000000000000001790556040513381527f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258906020016118ea565b600e5460609060008167ffffffffffffffff811115612309576123096150cb565b60405190808252806020026020018201604052801561234e57816020015b60408051808201909152600080825260208201528152602001906001900390816123275790505b50905060005b828110156123f3576000600e8281548110612371576123716150fa565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff9081168084526011835260409384902054845180860190955281855290911691830182905285519093509091908590859081106123d3576123d36150fa565b6020026020010181905250505080806123eb90615129565b915050612354565b5092915050565b73ffffffffffffffffffffffffffffffffffffffff8116612447576040517f9c8d2cd200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600160255460ff16600181111561246057612460614af5565b03612497576040517f4a3578fb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8281166000908152601160205260409020541633146124f7576040517fcebf515b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601454600e5460009161251a9185916bffffffffffffffffffffffff16906132bd565b73ffffffffffffffffffffffffffffffffffffffff8085166000908152600b6020908152604080832080547fffffffffffffffffffffffffffffffffffff000000000000000000000000ffff1690557f000000000000000000000000000000000000000000000000000000000000000090931682526021905220549091506125b1906bffffffffffffffffffffffff8316906150b8565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000081166000818152602160205260408082209490945592517fa9059cbb00000000000000000000000000000000000000000000000000000000815291851660048301526bffffffffffffffffffffffff841660248301529063a9059cbb906044016020604051808303816000875af1158015612666573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061268a919061553d565b9050806126c3576040517f90b8ec1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405133815273ffffffffffffffffffffffffffffffffffffffff808516916bffffffffffffffffffffffff8516918716907f9819093176a1851202c7bcfa46845809b4e47c261866550e94ed3775d2f406989060200160405180910390a450505050565b6127306131eb565b602580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055565b73ffffffffffffffffffffffffffffffffffffffff8181166000908152601260205260409020541633146127ba576040517f6752e7aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81811660008181526011602090815260408083208054337fffffffffffffffffffffffff000000000000000000000000000000000000000080831682179093556012909452828520805490921690915590519416939092849290917f78af32efdcad432315431e9b03d27e6cd98fb79c405fdc5af7c1714d9c0f75b39190a45050565b6000818152600460209081526040808320815161012081018352815460ff8082161515835261010080830490911615159583019590955263ffffffff620100008204811694830194909452660100000000000081048416606083015273ffffffffffffffffffffffffffffffffffffffff6a01000000000000000000009091048116608083015260018301546fffffffffffffffffffffffffffffffff811660a08401526bffffffffffffffffffffffff70010000000000000000000000000000000082041660c08401527c0100000000000000000000000000000000000000000000000000000000900490931660e08201526002909101549091169181019190915261297283612962816134e7565b8360400151846101000151611eb5565b9392505050565b606060248054806020026020016040519081016040528092919081815260200182805480156129de57602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116129b3575b5050505050905090565b604080516102008101825260008082526020820181905291810182905260608082018390526080820183905260a0820183905260c0820183905260e08201839052610100820183905261012082018390526101408201839052610160820183905261018082018390526101a082018390526101c08201526101e0810191909152604080516102008101825260165463ffffffff74010000000000000000000000000000000000000000808304821684527801000000000000000000000000000000000000000000000000808404831660208601526017547c0100000000000000000000000000000000000000000000000000000000810484169686019690965273ffffffffffffffffffffffffffffffffffffffff938416606086015260145460ff828204161515608087015262ffffff70010000000000000000000000000000000082041660a0870152601854928304841660c087015290820490921660e085015293821661010084015261ffff7301000000000000000000000000000000000000009091041661012083015291909116610140820152601954610160820152601a54610180820152601b546101a08201526101c08101612baa60096131de565b815260155473ffffffffffffffffffffffffffffffffffffffff16602090910152919050565b604080516101408101825260008082526020820181905260609282018390528282018190526080820181905260a0820181905260c0820181905260e082018190526101008201526101208101919091526000828152600460209081526040808320815161012081018352815460ff8082161515835261010080830490911615159583019590955263ffffffff620100008204811694830194909452660100000000000081048416606083015273ffffffffffffffffffffffffffffffffffffffff6a010000000000000000000090910481166080830181905260018401546fffffffffffffffffffffffffffffffff811660a08501526bffffffffffffffffffffffff70010000000000000000000000000000000082041660c08501527c0100000000000000000000000000000000000000000000000000000000900490941660e08301526002909201549091169281019290925290919015612da557816080015173ffffffffffffffffffffffffffffffffffffffff1663f00e6a2a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612d7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612da0919061555f565b612da8565b60005b90506040518061014001604052808273ffffffffffffffffffffffffffffffffffffffff168152602001836040015163ffffffff168152602001600760008781526020019081526020016000208054612e0090615161565b80601f0160208091040260200160405190810160405280929190818152602001828054612e2c90615161565b8015612e795780601f10612e4e57610100808354040283529160200191612e79565b820191906000526020600020905b815481529060010190602001808311612e5c57829003601f168201915b505050505081526020018360c001516bffffffffffffffffffffffff1681526020016005600087815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001836060015163ffffffff1667ffffffffffffffff1681526020018360e0015163ffffffff1681526020018360a001516bffffffffffffffffffffffff168152602001836000015115158152602001601e60008781526020019081526020016000208054612f5690615161565b80601f0160208091040260200160405190810160405280929190818152602001828054612f8290615161565b8015612fcf5780601f10612fa457610100808354040283529160200191612fcf565b820191906000526020600020905b815481529060010190602001808311612fb257829003601f168201915b505050505081525092505050919050565b6000612fea613af4565b905090565b60006110a182612852565b73ffffffffffffffffffffffffffffffffffffffff82811660009081526011602052604090205416331461305a576040517fcebf515b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff8216036130a9576040517f8c8728c700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8281166000908152601260205260409020548116908216146131555773ffffffffffffffffffffffffffffffffffffffff82811660008181526012602052604080822080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169486169485179055513392917f84f7c7c80bb8ed2279b4aab5f61cd05e6374073d38f46d7f32de8c30e9e3836791a45b5050565b6131616131eb565b61316a81613bbe565b50565b6000612fea60025b60006110a1825490565b60006129728383613cb3565b60175473ffffffffffffffffffffffffffffffffffffffff1633146131dc576040517f77c3599200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b6060600061297283613cdd565b60005473ffffffffffffffffffffffffffffffffffffffff1633146131dc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016121ea565b60185473ffffffffffffffffffffffffffffffffffffffff1633146131dc576040517fb6dfb7a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff83166000908152600b602090815260408083208151608081018352905460ff80821615801584526101008304909116948301949094526bffffffffffffffffffffffff6201000082048116938301939093526e01000000000000000000000000000090049091166060820152906134b9576000816060015185613355919061541d565b905060006133638583615471565b90508083604001818151613377919061549c565b6bffffffffffffffffffffffff16905250613392858261557c565b836060018181516133a3919061549c565b6bffffffffffffffffffffffff90811690915273ffffffffffffffffffffffffffffffffffffffff89166000908152600b602090815260409182902087518154928901519389015160608a015186166e010000000000000000000000000000027fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff919096166201000002167fffffffffffff000000000000000000000000000000000000000000000000ffff60ff95909516610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909416939093171792909216179190911790555050505b60400151949350505050565b60006129728373ffffffffffffffffffffffffffffffffffffffff8416613d38565b6000818160045b600f811015613574577fff00000000000000000000000000000000000000000000000000000000000000821683826020811061352c5761352c6150fa565b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161461356257506000949350505050565b8061356c81615129565b9150506134ee565b5081600f1a600181111561358a5761358a614af5565b949350505050565b600080600080846040015162ffffff1690506000808263ffffffff161190506000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa15801561361f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061364391906155c3565b509450909250505060008113158061365a57508142105b8061367b575082801561367b575061367282426150b8565b8463ffffffff16105b1561368a57601954965061368e565b8096505b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa1580156136f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061371d91906155c3565b509450909250505060008113158061373457508142105b806137555750828015613755575061374c82426150b8565b8463ffffffff16105b1561376457601a549550613768565b8095505b86866137738a613e2b565b965096509650505050509193909250565b600080808089600181111561379b5761379b614af5565b036137aa575062017f986137ff565b60018960018111156137be576137be614af5565b036137cd57506201e26c6137ff565b6040517ff2b2d41200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008a6080015160016138129190615613565b6138209060ff16604061562c565b60185461384e906103a49074010000000000000000000000000000000000000000900463ffffffff166150a5565b61385891906150a5565b601554604080517fde9ee35e0000000000000000000000000000000000000000000000000000000081528151939450600093849373ffffffffffffffffffffffffffffffffffffffff169263de9ee35e92600480820193918290030181865afa1580156138c9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138ed9190615643565b909250905081836138ff8360186150a5565b613909919061562c565b60808f0151613919906001615613565b6139289060ff166115e061562c565b61393291906150a5565b61393c91906150a5565b61394690856150a5565b6101008e01516040517f125441400000000000000000000000000000000000000000000000000000000081526004810186905291955073ffffffffffffffffffffffffffffffffffffffff1690631254414090602401602060405180830381865afa1580156139b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139dd9190615524565b8d6060015161ffff166139f0919061562c565b94505050506000613a018b86613f15565b60008d815260046020526040902054909150610100900460ff1615613a665760008c81526023602090815260409182902082518084018452905463ffffffff811680835264010000000090910462ffffff908116928401928352928501525116908201525b6000613ad08c6040518061012001604052808d63ffffffff1681526020018681526020018781526020018c81526020018b81526020018a81526020018973ffffffffffffffffffffffffffffffffffffffff16815260200185815260200160001515815250614091565b60208101518151919250613ae39161549c565b9d9c50505050505050505050505050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166000818152602160205260408082205490517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152919290916370a0823190602401602060405180830381865afa158015613b90573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bb49190615524565b612fea9190615667565b3373ffffffffffffffffffffffffffffffffffffffff821603613c3d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016121ea565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000826000018281548110613cca57613cca6150fa565b9060005260206000200154905092915050565b60608160000180548060200260200160405190810160405280929190818152602001828054801561160f57602002820191906000526020600020905b815481526020019060010190808311613d195750505050509050919050565b60008181526001830160205260408120548015613e21576000613d5c6001836150b8565b8554909150600090613d70906001906150b8565b9050818114613dd5576000866000018281548110613d9057613d906150fa565b9060005260206000200154905080876000018481548110613db357613db36150fa565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080613de657613de6615687565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506110a1565b60009150506110a1565b60008060007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015613e9b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ebf91906155c3565b50935050925050600082131580613ed557508042105b80613f0557506000846040015162ffffff16118015613f055750613ef981426150b8565b846040015162ffffff16105b156123f3575050601b5492915050565b60408051608081018252600080825260208083018281528385018381526060850184905273ffffffffffffffffffffffffffffffffffffffff878116855260229093528584208054640100000000810462ffffff1690925263ffffffff82169092527b01000000000000000000000000000000000000000000000000000000810460ff16855285517ffeaf968c00000000000000000000000000000000000000000000000000000000815295519495919484936701000000000000009092049091169163feaf968c9160048083019260a09291908290030181865afa158015614002573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061402691906155c3565b5093505092505060008213158061403c57508042105b8061406c57506000866040015162ffffff1611801561406c575061406081426150b8565b866040015162ffffff16105b156140805760018301546060850152614088565b606084018290525b50505092915050565b6040805161010081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081019190915260008260e001516000015160ff1690506000846060015161ffff1684606001516140fc919061562c565b9050836101000151801561410f5750803a105b1561411757503a5b60006012831161412857600161413e565b6141336012846150b8565b61413e90600a6157d6565b9050600060128410614151576001614167565b61415c8460126150b8565b61416790600a6157d6565b905060008660a0015187604001518860200151896000015161418991906150a5565b614193908761562c565b61419d91906150a5565b6141a7919061562c565b9050614203828860e00151606001516141c0919061562c565b6001848a60e00151606001516141d6919061562c565b6141e091906150b8565b6141ea868561562c565b6141f491906150a5565b6141fe91906157e2565b6143d8565b6bffffffffffffffffffffffff1686526080870151614226906141fe90836157e2565b6bffffffffffffffffffffffff1660408088019190915260e0880151015160009061425f9062ffffff16683635c9adc5dea0000061562c565b9050600081633b9aca008a60a001518b60e001516020015163ffffffff168c604001518d600001518b614292919061562c565b61429c91906150a5565b6142a6919061562c565b6142b0919061562c565b6142ba91906157e2565b6142c491906150a5565b9050614307848a60e00151606001516142dd919061562c565b6001868c60e00151606001516142f3919061562c565b6142fd91906150b8565b6141ea888561562c565b6bffffffffffffffffffffffff166020890152608089015161432d906141fe90836157e2565b6bffffffffffffffffffffffff16606089015260c089015173ffffffffffffffffffffffffffffffffffffffff166080808a0191909152890151614370906143d8565b6bffffffffffffffffffffffff1660a0808a0191909152890151614393906143d8565b6bffffffffffffffffffffffff1660c089015260e0890151606001516143b8906143d8565b6bffffffffffffffffffffffff1660e08901525050505050505092915050565b60006bffffffffffffffffffffffff821115614476576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201527f362062697473000000000000000000000000000000000000000000000000000060648201526084016121ea565b5090565b6000806040838503121561448d57600080fd5b50508035926020909101359150565b600081518084526020808501945080840160005b838110156144cc578151875295820195908201906001016144b0565b509495945050505050565b602081526000612972602083018461449c565b60008083601f8401126144fc57600080fd5b50813567ffffffffffffffff81111561451457600080fd5b60208301915083602082850101111561452c57600080fd5b9250929050565b60008060006040848603121561454857600080fd5b83359250602084013567ffffffffffffffff81111561456657600080fd5b614572868287016144ea565b9497909650939450505050565b600081518084526020808501945080840160005b838110156144cc57815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101614593565b805163ffffffff16825260006101e060208301516145eb602086018263ffffffff169052565b506040830151614603604086018263ffffffff169052565b50606083015161461a606086018262ffffff169052565b506080830151614630608086018261ffff169052565b5060a083015161465060a08601826bffffffffffffffffffffffff169052565b5060c083015161466860c086018263ffffffff169052565b5060e083015161468060e086018263ffffffff169052565b506101008381015163ffffffff908116918601919091526101208085015190911690850152610140808401519085015261016080840151908501526101808084015173ffffffffffffffffffffffffffffffffffffffff16908501526101a0808401518186018390526146f58387018261457f565b925050506101c0808401516147218287018273ffffffffffffffffffffffffffffffffffffffff169052565b5090949350505050565b855163ffffffff16815260006101c0602088015161475960208501826bffffffffffffffffffffffff169052565b5060408801516040840152606088015161478360608501826bffffffffffffffffffffffff169052565b506080880151608084015260a08801516147a560a085018263ffffffff169052565b5060c08801516147bd60c085018263ffffffff169052565b5060e088015160e0840152610100808901516147e08286018263ffffffff169052565b5050610120888101511515908401526101408301819052614803818401886145c5565b9050828103610160840152614818818761457f565b905082810361018084015261482d818661457f565b9150506148406101a083018460ff169052565b9695505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461316a57600080fd5b6000806040838503121561487f57600080fd5b823561488a8161484a565b915060208301356004811061489e57600080fd5b809150509250929050565b6000602082840312156148bb57600080fd5b5035919050565b6000815180845260005b818110156148e8576020818501810151868301820152016148cc565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b60208152600061297260208301846148c2565b815173ffffffffffffffffffffffffffffffffffffffff1681526101608101602083015161496f602084018263ffffffff169052565b506040830151614987604084018263ffffffff169052565b50606083015161499f606084018263ffffffff169052565b5060808301516149c7608084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060a08301516149df60a084018263ffffffff169052565b5060c08301516149f760c084018263ffffffff169052565b5060e0830151614a0f60e084018263ffffffff169052565b506101008381015173ffffffffffffffffffffffffffffffffffffffff81168483015250506101208381015163ffffffff81168483015250506101408381015163ffffffff8116848301525b505092915050565b60008060208385031215614a7657600080fd5b823567ffffffffffffffff80821115614a8e57600080fd5b818501915085601f830112614aa257600080fd5b813581811115614ab157600080fd5b8660208260051b8501011115614ac657600080fd5b60209290920196919550909350505050565b600060208284031215614aea57600080fd5b81356129728161484a565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6002811061316a5761316a614af5565b60208101614b4183614b24565b91905290565b60008060008060808587031215614b5d57600080fd5b84359350602085013560028110614b7357600080fd5b9250604085013563ffffffff81168114614b8c57600080fd5b91506060850135614b9c8161484a565b939692955090935050565b600080600060408486031215614bbc57600080fd5b8335614bc78161484a565b9250602084013567ffffffffffffffff81111561456657600080fd5b602080825282518282018190526000919060409081850190868401855b82811015614c3f578151805173ffffffffffffffffffffffffffffffffffffffff90811686529087015116868501529284019290850190600101614c00565b5091979650505050505050565b60008060408385031215614c5f57600080fd5b8235614c6a8161484a565b9150602083013561489e8161484a565b6020808252825182820181905260009190848201906040850190845b81811015614cc857835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101614c96565b50909695505050505050565b60208152614ceb60208201835163ffffffff169052565b60006020830151614d04604084018263ffffffff169052565b50604083015163ffffffff8116606084015250606083015173ffffffffffffffffffffffffffffffffffffffff8116608084015250608083015180151560a08401525060a083015162ffffff811660c08401525060c083015163ffffffff811660e08401525060e0830151610100614d838185018363ffffffff169052565b8401519050610120614dac8482018373ffffffffffffffffffffffffffffffffffffffff169052565b8401519050610140614dc38482018361ffff169052565b8401519050610160614dec8482018373ffffffffffffffffffffffffffffffffffffffff169052565b840151610180848101919091528401516101a0808501919091528401516101c0808501919091528401516102006101e080860182905291925090614e3461022086018461457f565b9086015173ffffffffffffffffffffffffffffffffffffffff811683870152909250614721565b60208152614e8260208201835173ffffffffffffffffffffffffffffffffffffffff169052565b60006020830151614e9b604084018263ffffffff169052565b506040830151610140806060850152614eb86101608501836148c2565b91506060850151614ed960808601826bffffffffffffffffffffffff169052565b50608085015173ffffffffffffffffffffffffffffffffffffffff811660a08601525060a085015167ffffffffffffffff811660c08601525060c085015163ffffffff811660e08601525060e0850151610100614f45818701836bffffffffffffffffffffffff169052565b8601519050610120614f5a8682018315159052565b8601518584037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00183870152905061484083826148c2565b6000610120820190506bffffffffffffffffffffffff835116825263ffffffff60208401511660208301526040830151614fd3604084018262ffffff169052565b506060830151614fe9606084018261ffff169052565b506080830151614ffe608084018260ff169052565b5060a083015161501260a084018215159052565b5060c083015161502660c084018215159052565b5060e083015161503a60e084018215159052565b506101008381015173ffffffffffffffffffffffffffffffffffffffff811684830152614a5b565b6020810160048310614b4157614b41614af5565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808201808211156110a1576110a1615076565b818103818111156110a1576110a1615076565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361515a5761515a615076565b5060010190565b600181811c9082168061517557607f821691505b6020821081036151ae577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f8211156151fe57600081815260208120601f850160051c810160208610156151db5750805b601f850160051c820191505b818110156151fa578281556001016151e7565b5050505b505050565b67ffffffffffffffff83111561521b5761521b6150cb565b61522f836152298354615161565b836151b4565b6000601f841160018114615281576000851561524b5750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355615317565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156152d057868501358255602094850194600190920191016152b0565b508682101561530b577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b60208152816020820152818360408301376000818301604090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160101919050565b6000604082016040835280865480835260608501915087600052602092508260002060005b828110156153c257815473ffffffffffffffffffffffffffffffffffffffff1684529284019260019182019101615390565b505050838103828501528481528590820160005b868110156154115782356153e98161484a565b73ffffffffffffffffffffffffffffffffffffffff16825291830191908301906001016153d6565b50979650505050505050565b6bffffffffffffffffffffffff8281168282160390808211156123f3576123f3615076565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006bffffffffffffffffffffffff8084168061549057615490615442565b92169190910492915050565b6bffffffffffffffffffffffff8181168382160190808211156123f3576123f3615076565b6000816154d0576154d0615076565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b604081526000615509604083018561457f565b828103602084015261551b818561449c565b95945050505050565b60006020828403121561553657600080fd5b5051919050565b60006020828403121561554f57600080fd5b8151801515811461297257600080fd5b60006020828403121561557157600080fd5b81516129728161484a565b6bffffffffffffffffffffffff818116838216028082169190828114614a5b57614a5b615076565b805169ffffffffffffffffffff811681146155be57600080fd5b919050565b600080600080600060a086880312156155db57600080fd5b6155e4866155a4565b9450602086015193506040860151925060608601519150615607608087016155a4565b90509295509295909350565b60ff81811683821601908111156110a1576110a1615076565b80820281158282048414176110a1576110a1615076565b6000806040838503121561565657600080fd5b505080516020909101519092909150565b81810360008312801583831316838312821617156123f3576123f3615076565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b600181815b8085111561570f57817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156156f5576156f5615076565b8085161561570257918102915b93841c93908002906156bb565b509250929050565b600082615726575060016110a1565b81615733575060006110a1565b816001811461574957600281146157535761576f565b60019150506110a1565b60ff84111561576457615764615076565b50506001821b6110a1565b5060208310610133831016604e8410600b8410161715615792575081810a6110a1565b61579c83836156b6565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156157ce576157ce615076565b029392505050565b60006129728383615717565b6000826157f1576157f1615442565b50049056fea164736f6c6343000813000a", } var AutomationRegistryLogicCABI = AutomationRegistryLogicCMetaData.ABI diff --git a/core/gethwrappers/generated/automation_registry_wrapper_2_2/automation_registry_wrapper_2_2.go b/core/gethwrappers/generated/automation_registry_wrapper_2_2/automation_registry_wrapper_2_2.go index eb385cf7b0..a7fcf430b3 100644 --- a/core/gethwrappers/generated/automation_registry_wrapper_2_2/automation_registry_wrapper_2_2.go +++ b/core/gethwrappers/generated/automation_registry_wrapper_2_2/automation_registry_wrapper_2_2.go @@ -52,7 +52,7 @@ type AutomationRegistryBase22OnchainConfig struct { var AutomationRegistryMetaData = &bind.MetaData{ ABI: "[{\"inputs\":[{\"internalType\":\"contractAutomationRegistryLogicB2_2\",\"name\":\"logicA\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ArrayHasNoEntries\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotCancel\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CheckDataExceedsLimit\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ConfigDigestMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateEntry\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitOutsideRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfFaultyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IndexOutOfRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidDataLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRecipient\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReport\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTransmitter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTrigger\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTriggerType\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxCheckDataSizeCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MaxPerformDataSizeCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MigrationNotPermitted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotAContract\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveTransmitters\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByLINKToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrRegistrar\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByUpkeepPrivilegeManager\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyPausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlySimulatedBackend\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyUnpausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ParameterLengthError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PaymentGreaterThanAllLINK\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReentrantCall\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RegistryPaused\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedTransmitter\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"reason\",\"type\":\"bytes\"}],\"name\":\"TargetCheckReverted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TranscoderNotSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepAlreadyExists\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepCancelled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotCanceled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotNeeded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ValueNotChanged\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"AdminPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"CancelledUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newModule\",\"type\":\"address\"}],\"name\":\"ChainSpecificModuleUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"previousConfigBlockNumber\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"configCount\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"onchainConfig\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"offchainConfigVersion\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"dedupKey\",\"type\":\"bytes32\"}],\"name\":\"DedupKeyAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"FundsAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"FundsWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"InsufficientFundsUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"OwnerFundsWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"payees\",\"type\":\"address[]\"}],\"name\":\"PayeesUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"payee\",\"type\":\"address\"}],\"name\":\"PaymentWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"ReorgedUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"StaleUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"epoch\",\"type\":\"uint32\"}],\"name\":\"Transmitted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"atBlockHeight\",\"type\":\"uint64\"}],\"name\":\"UpkeepCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"newCheckData\",\"type\":\"bytes\"}],\"name\":\"UpkeepCheckDataSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"gasLimit\",\"type\":\"uint96\"}],\"name\":\"UpkeepGasLimitSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"remainingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"UpkeepMigrated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepOffchainConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepPaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"totalPayment\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasOverhead\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"UpkeepPerformed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"startingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"importedFrom\",\"type\":\"address\"}],\"name\":\"UpkeepReceived\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"performGas\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"}],\"name\":\"UpkeepRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepTriggerConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepUnpaused\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"fallbackTo\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestConfigDetails\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"blockNumber\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestConfigDigestAndEpoch\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"scanLogs\",\"type\":\"bool\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"epoch\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"onTokenTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"onchainConfigBytes\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"offchainConfigVersion\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"paymentPremiumPPB\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"flatFeeMicroLink\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"checkGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"stalenessSeconds\",\"type\":\"uint24\"},{\"internalType\":\"uint16\",\"name\":\"gasCeilingMultiplier\",\"type\":\"uint16\"},{\"internalType\":\"uint96\",\"name\":\"minUpkeepSpend\",\"type\":\"uint96\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxCheckDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxRevertDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"fallbackGasPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fallbackLinkPrice\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"registrars\",\"type\":\"address[]\"},{\"internalType\":\"address\",\"name\":\"upkeepPrivilegeManager\",\"type\":\"address\"},{\"internalType\":\"contractIChainModule\",\"name\":\"chainModule\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"reorgProtectionEnabled\",\"type\":\"bool\"}],\"internalType\":\"structAutomationRegistryBase2_2.OnchainConfig\",\"name\":\"onchainConfig\",\"type\":\"tuple\"},{\"internalType\":\"uint64\",\"name\":\"offchainConfigVersion\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"setConfigTypeSafe\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"}],\"name\":\"simulatePerformUpkeep\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"rawReport\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"rs\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"ss\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"rawVs\",\"type\":\"bytes32\"}],\"name\":\"transmit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", - Bin: "0x6101406040523480156200001257600080fd5b506040516200524a3803806200524a8339810160408190526200003591620003b1565b80816001600160a01b031663ca30e6036040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000075573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200009b9190620003b1565b826001600160a01b031663b10b673c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620000da573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001009190620003b1565b836001600160a01b0316636709d0e56040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200013f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001659190620003b1565b846001600160a01b0316635425d8ac6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001a4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001ca9190620003b1565b856001600160a01b031663a08714c06040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000209573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200022f9190620003b1565b3380600081620002865760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620002b957620002b981620002ed565b5050506001600160a01b0394851660805292841660a05290831660c052821660e052811661010052166101205250620003d8565b336001600160a01b03821603620003475760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016200027d565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6001600160a01b0381168114620003ae57600080fd5b50565b600060208284031215620003c457600080fd5b8151620003d18162000398565b9392505050565b60805160a05160c05160e0516101005161012051614e25620004256000396000818160c9015261017c01526000611bb8015260005050600050506000505060006104c20152614e256000f3fe6080604052600436106100c75760003560e01c8063aed2e92911610074578063e3d0e7121161004e578063e3d0e71214610348578063f2fde38b14610368578063f75f6b1114610388576100c7565b8063aed2e929146102a3578063afcb95d7146102da578063b1dc65a414610328576100c7565b806381ff7048116100a557806381ff7048146101d65780638da5cb5b14610258578063a4c0ed3614610283576100c7565b8063181f5a771461010e578063349e8cca1461016d57806379ba5097146101c1575b7f00000000000000000000000000000000000000000000000000000000000000003660008037600080366000845af43d6000803e808015610107573d6000f35b3d6000fd5b005b34801561011a57600080fd5b506101576040518060400160405280601881526020017f4175746f6d6174696f6e526567697374727920322e322e30000000000000000081525081565b6040516101649190613b21565b60405180910390f35b34801561017957600080fd5b507f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610164565b3480156101cd57600080fd5b5061010c6103a8565b3480156101e257600080fd5b5061023560155460115463ffffffff780100000000000000000000000000000000000000000000000083048116937c01000000000000000000000000000000000000000000000000000000009093041691565b6040805163ffffffff948516815293909216602084015290820152606001610164565b34801561026457600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff1661019c565b34801561028f57600080fd5b5061010c61029e366004613baf565b6104aa565b3480156102af57600080fd5b506102c36102be366004613c0b565b6106c6565b604080519215158352602083019190915201610164565b3480156102e657600080fd5b50601154601254604080516000815260208101939093527401000000000000000000000000000000000000000090910463ffffffff1690820152606001610164565b34801561033457600080fd5b5061010c610343366004613c9c565b61083c565b34801561035457600080fd5b5061010c610363366004613f6d565b610b77565b34801561037457600080fd5b5061010c61038336600461403a565b610ba0565b34801561039457600080fd5b5061010c6103a336600461423e565b610bb4565b60015473ffffffffffffffffffffffffffffffffffffffff16331461042e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610519576040517fc8bad78d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208114610553576040517fdfe9309000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610561828401846142cd565b60008181526004602052604090205490915065010000000000900463ffffffff908116146105bb576040517f9c0083a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000818152600460205260409020600101546105f69085906c0100000000000000000000000090046bffffffffffffffffffffffff16614315565b600082815260046020526040902060010180546bffffffffffffffffffffffff929092166c01000000000000000000000000027fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff90921691909117905560195461066190859061433a565b6019556040516bffffffffffffffffffffffff8516815273ffffffffffffffffffffffffffffffffffffffff86169082907fafd24114486da8ebfc32f3626dada8863652e187461aa74d4bfa7348915062039060200160405180910390a35050505050565b6000806106d1611ba0565b6012547e01000000000000000000000000000000000000000000000000000000000000900460ff1615610730576040517f24522f3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600085815260046020908152604091829020825160e081018452815460ff811615158252610100810463ffffffff908116838601819052650100000000008304821684880152690100000000000000000090920473ffffffffffffffffffffffffffffffffffffffff16606084018190526001909401546bffffffffffffffffffffffff80821660808601526c0100000000000000000000000082041660a0850152780100000000000000000000000000000000000000000000000090041660c08301528451601f8901859004850281018501909552878552909361082f938990899081908401838280828437600092019190915250611c1192505050565b9097909650945050505050565b60005a60408051610160810182526012546bffffffffffffffffffffffff8116825263ffffffff6c010000000000000000000000008204811660208401527001000000000000000000000000000000008204811693830193909352740100000000000000000000000000000000000000008104909216606082015262ffffff7801000000000000000000000000000000000000000000000000830416608082015261ffff7b0100000000000000000000000000000000000000000000000000000083041660a082015260ff7d0100000000000000000000000000000000000000000000000000000000008304811660c08301527e010000000000000000000000000000000000000000000000000000000000008304811615801560e08401527f01000000000000000000000000000000000000000000000000000000000000009093048116151561010080840191909152601354918216151561012084015273ffffffffffffffffffffffffffffffffffffffff9104166101408201529192506109f2576040517f24522f3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152600b602052604090205460ff16610a3b576040517f1099ed7500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6011548a3514610a77576040517fdfdcf8e700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60c0810151610a8790600161437c565b60ff1686141580610a985750858414155b15610acf576040517f0244f71a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610adf8a8a8a8a8a8a8a8a611e3a565b6000610aeb8a8a6120a3565b905060208b0135600881901c63ffffffff16610b0884848761215e565b836060015163ffffffff168163ffffffff161115610b6857601280547fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000063ffffffff8416021790555b50505050505050505050505050565b610b9886868686806020019051810190610b91919061443b565b8686610bb4565b505050505050565b610ba8612add565b610bb181612b5e565b50565b610bbc612add565b601f86511115610bf8576040517f25d0209c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8360ff16600003610c35576040517fe77dba5600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84518651141580610c545750610c4c8460036145bd565b60ff16865111155b15610c8b576040517f1d2d1c5800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601254600e546bffffffffffffffffffffffff9091169060005b816bffffffffffffffffffffffff16811015610d0d57610cfa600e8281548110610cd157610cd161434d565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff168484612c53565b5080610d05816145d9565b915050610ca5565b5060008060005b836bffffffffffffffffffffffff16811015610e1657600d8181548110610d3d57610d3d61434d565b600091825260209091200154600e805473ffffffffffffffffffffffffffffffffffffffff90921694509082908110610d7857610d7861434d565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff8681168452600c8352604080852080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001690559116808452600b90925290912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055915080610e0e816145d9565b915050610d14565b50610e23600d6000613a00565b610e2f600e6000613a00565b604080516080810182526000808252602082018190529181018290526060810182905290805b8c5181101561129857600c60008e8381518110610e7457610e7461434d565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040016000205460ff1615610edf576040517f77cea0fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168d8281518110610f0957610f0961434d565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1603610f5e576040517f815e1d6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405180604001604052806001151581526020018260ff16815250600c60008f8481518110610f8f57610f8f61434d565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528181019290925260400160002082518154939092015160ff16610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909316929092171790558b518c90829081106110375761103761434d565b60200260200101519150600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036110a7576040517f58a70a0a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82166000908152600b60209081526040918290208251608081018452905460ff80821615801584526101008304909116938301939093526bffffffffffffffffffffffff6201000082048116948301949094526e01000000000000000000000000000090049092166060830152909350611162576040517f6a7281ad00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001835260ff80821660208086019182526bffffffffffffffffffffffff808b166060880190815273ffffffffffffffffffffffffffffffffffffffff87166000908152600b909352604092839020885181549551948a0151925184166e010000000000000000000000000000027fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff939094166201000002929092167fffffffffffff000000000000000000000000000000000000000000000000ffff94909616610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00009095169490941717919091169290921791909117905580611290816145d9565b915050610e55565b50508a516112ae9150600d9060208d0190613a1e565b5088516112c290600e9060208c0190613a1e565b50604051806101600160405280856bffffffffffffffffffffffff168152602001886000015163ffffffff168152602001886020015163ffffffff168152602001600063ffffffff168152602001886060015162ffffff168152602001886080015161ffff1681526020018960ff1681526020016012600001601e9054906101000a900460ff16151581526020016012600001601f9054906101000a900460ff161515815260200188610200015115158152602001886101e0015173ffffffffffffffffffffffffffffffffffffffff16815250601260008201518160000160006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550602082015181600001600c6101000a81548163ffffffff021916908363ffffffff16021790555060408201518160000160106101000a81548163ffffffff021916908363ffffffff16021790555060608201518160000160146101000a81548163ffffffff021916908363ffffffff16021790555060808201518160000160186101000a81548162ffffff021916908362ffffff16021790555060a082015181600001601b6101000a81548161ffff021916908361ffff16021790555060c082015181600001601d6101000a81548160ff021916908360ff16021790555060e082015181600001601e6101000a81548160ff02191690831515021790555061010082015181600001601f6101000a81548160ff0219169083151502179055506101208201518160010160006101000a81548160ff0219169083151502179055506101408201518160010160016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509050506040518061018001604052808860a001516bffffffffffffffffffffffff16815260200188610180015173ffffffffffffffffffffffffffffffffffffffff168152602001601460010160009054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff168152602001886040015163ffffffff1681526020018860c0015163ffffffff168152602001601460010160149054906101000a900463ffffffff1663ffffffff168152602001601460010160189054906101000a900463ffffffff1663ffffffff1681526020016014600101601c9054906101000a900463ffffffff1663ffffffff1681526020018860e0015163ffffffff16815260200188610100015163ffffffff16815260200188610120015163ffffffff168152602001886101c0015173ffffffffffffffffffffffffffffffffffffffff16815250601460008201518160000160006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550602082015181600001600c6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060408201518160010160006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550606082015181600101600c6101000a81548163ffffffff021916908363ffffffff16021790555060808201518160010160106101000a81548163ffffffff021916908363ffffffff16021790555060a08201518160010160146101000a81548163ffffffff021916908363ffffffff16021790555060c08201518160010160186101000a81548163ffffffff021916908363ffffffff16021790555060e082015181600101601c6101000a81548163ffffffff021916908363ffffffff1602179055506101008201518160020160006101000a81548163ffffffff021916908363ffffffff1602179055506101208201518160020160046101000a81548163ffffffff021916908363ffffffff1602179055506101408201518160020160086101000a81548163ffffffff021916908363ffffffff16021790555061016082015181600201600c6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555090505086610140015160178190555086610160015160188190555060006014600101601c9054906101000a900463ffffffff169050876101e0015173ffffffffffffffffffffffffffffffffffffffff166357e871e76040518163ffffffff1660e01b8152600401602060405180830381865afa158015611963573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119879190614611565b601580547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167c010000000000000000000000000000000000000000000000000000000063ffffffff9384160217808255600192601891611a02918591780100000000000000000000000000000000000000000000000090041661462a565b92506101000a81548163ffffffff021916908363ffffffff160217905550600088604051602001611a339190614698565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0018152919052601554909150611a9c90469030907801000000000000000000000000000000000000000000000000900463ffffffff168f8f8f878f8f612e5b565b60115560005b611aac6009612f05565b811015611adc57611ac9611ac1600983612f0f565b600990612f22565b5080611ad4816145d9565b915050611aa2565b5060005b896101a0015151811015611b3357611b208a6101a001518281518110611b0857611b0861434d565b60200260200101516009612f4490919063ffffffff16565b5080611b2b816145d9565b915050611ae0565b507f1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e0582601154601460010160189054906101000a900463ffffffff168f8f8f878f8f604051611b8a9998979695949392919061483c565b60405180910390a1505050505050505050505050565b3273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611c0f576040517fb60ac5db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b60125460009081907f0100000000000000000000000000000000000000000000000000000000000000900460ff1615611c76576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601280547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01000000000000000000000000000000000000000000000000000000000000001790556040517f4585e33b0000000000000000000000000000000000000000000000000000000090611cf2908590602401613b21565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290517f79188d1600000000000000000000000000000000000000000000000000000000815290935073ffffffffffffffffffffffffffffffffffffffff8616906379188d1690611dc590879087906004016148d2565b60408051808303816000875af1158015611de3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e0791906148eb565b601280547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16905590969095509350505050565b60008787604051611e4c929190614919565b604051908190038120611e63918b90602001614929565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201208383019092526000808452908301819052909250906000805b8881101561203a57600185878360208110611ecf57611ecf61434d565b611edc91901a601b61437c565b8c8c85818110611eee57611eee61434d565b905060200201358b8b86818110611f0757611f0761434d565b9050602002013560405160008152602001604052604051611f44949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa158015611f66573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081015173ffffffffffffffffffffffffffffffffffffffff81166000908152600c602090815290849020838501909452925460ff8082161515808552610100909204169383019390935290955093509050612014576040517f0f4c073700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b826020015160080260ff166001901b840193508080612032906145d9565b915050611eb2565b50827e01010101010101010101010101010101010101010101010101010101010101841614612095576040517fc103be2e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050505050505050565b6120dc6040518060c001604052806000815260200160008152602001606081526020016060815260200160608152602001606081525090565b60006120ea83850185614a1a565b604081015151606082015151919250908114158061210d57508082608001515114155b8061211d5750808260a001515114155b15612154576040517fb55ac75400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5090505b92915050565b600082604001515167ffffffffffffffff81111561217e5761217e613d53565b60405190808252806020026020018201604052801561223a57816020015b604080516101c081018252600060e08201818152610100830182905261012083018290526101408301829052610160830182905261018083018290526101a0830182905282526020808301829052928201819052606082018190526080820181905260a0820181905260c082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90920191018161219c5790505b50905060006040518060800160405280600061ffff1681526020016000815260200160006bffffffffffffffffffffffff16815260200160006bffffffffffffffffffffffff168152509050600085610140015173ffffffffffffffffffffffffffffffffffffffff166357e871e76040518163ffffffff1660e01b8152600401602060405180830381865afa1580156122d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122fc9190614611565b9050600086610140015173ffffffffffffffffffffffffffffffffffffffff166318b8f6136040518163ffffffff1660e01b8152600401602060405180830381865afa158015612350573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123749190614611565b905060005b8660400151518110156127c35760046000886040015183815181106123a0576123a061434d565b6020908102919091018101518252818101929092526040908101600020815160e081018352815460ff811615158252610100810463ffffffff90811695830195909552650100000000008104851693820193909352690100000000000000000090920473ffffffffffffffffffffffffffffffffffffffff166060830152600101546bffffffffffffffffffffffff80821660808401526c0100000000000000000000000082041660a08301527801000000000000000000000000000000000000000000000000900490911660c082015285518690839081106124855761248561434d565b6020026020010151600001819052506124ba876040015182815181106124ad576124ad61434d565b6020026020010151612f66565b8582815181106124cc576124cc61434d565b60200260200101516060019060018111156124e9576124e9614b07565b908160018111156124fc576124fc614b07565b81525050612560876040015182815181106125195761251961434d565b602002602001015184896080015184815181106125385761253861434d565b60200260200101518885815181106125525761255261434d565b60200260200101518c613011565b8683815181106125725761257261434d565b602002602001015160200187848151811061258f5761258f61434d565b602002602001015160c00182815250821515151581525050508481815181106125ba576125ba61434d565b602002602001015160200151156125ea576001846000018181516125de9190614b36565b61ffff169052506125ef565b6127b1565b6126558582815181106126045761260461434d565b602002602001015160000151606001518860600151838151811061262a5761262a61434d565b60200260200101518960a0015184815181106126485761264861434d565b6020026020010151611c11565b8683815181106126675761266761434d565b60200260200101516040018784815181106126845761268461434d565b602090810291909101015160800191909152901515905260c08801516126ab90600161437c565b6126b99060ff166040614b51565b6103a48860a0015183815181106126d2576126d261434d565b6020026020010151516126e5919061433a565b6126ef919061433a565b8582815181106127015761270161434d565b602002602001015160a00181815250508481815181106127235761272361434d565b602002602001015160a0015184602001818151612740919061433a565b90525084518590829081106127575761275761434d565b6020026020010151608001518661276e9190614b68565b95506127b1876040015182815181106127895761278961434d565b6020026020010151848784815181106127a4576127a461434d565b6020026020010151613130565b806127bb816145d9565b915050612379565b50825161ffff166000036127da5750505050505050565b6155f06127e8366010614b51565b5a6127f39088614b68565b6127fd919061433a565b612807919061433a565b8351909550611b589061281e9061ffff1687614baa565b612828919061433a565b945060008060005b886040015151811015612a0c5786818151811061284f5761284f61434d565b602002602001015160200151156129fa576128e88a8a60400151838151811061287a5761287a61434d565b60200260200101518984815181106128945761289461434d565b6020026020010151608001518c600001518d602001518d8c602001518e89815181106128c2576128c261434d565b602002602001015160a001518c6128d99190614b51565b6128e39190614baa565b613235565b6060880180519295509093508391612901908390614315565b6bffffffffffffffffffffffff16905250604086018051849190612926908390614315565b6bffffffffffffffffffffffff16905250865187908290811061294b5761294b61434d565b60200260200101516040015115158960400151828151811061296f5761296f61434d565b60200260200101517fad8cc9579b21dfe2c2f6ea35ba15b656e46b4f5b0cb424f52739b8ce5cac9c5b84866129a49190614315565b8a85815181106129b6576129b661434d565b6020026020010151608001518c8e6080015187815181106129d9576129d961434d565b60200260200101516040516129f19493929190614bbe565b60405180910390a35b80612a04816145d9565b915050612830565b505050604083810151336000908152600b602052919091208054600290612a489084906201000090046bffffffffffffffffffffffff16614315565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508260600151601260000160008282829054906101000a90046bffffffffffffffffffffffff16612aa69190614315565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555050505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314611c0f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610425565b3373ffffffffffffffffffffffffffffffffffffffff821603612bdd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610425565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b73ffffffffffffffffffffffffffffffffffffffff83166000908152600b602090815260408083208151608081018352905460ff80821615801584526101008304909116948301949094526bffffffffffffffffffffffff6201000082048116938301939093526e0100000000000000000000000000009004909116606082015290612e4f576000816060015185612ceb9190614bfb565b90506000612cf98583614c20565b90508083604001818151612d0d9190614315565b6bffffffffffffffffffffffff16905250612d288582614c4b565b83606001818151612d399190614315565b6bffffffffffffffffffffffff90811690915273ffffffffffffffffffffffffffffffffffffffff89166000908152600b602090815260409182902087518154928901519389015160608a015186166e010000000000000000000000000000027fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff919096166201000002167fffffffffffff000000000000000000000000000000000000000000000000ffff60ff95909516610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909416939093171792909216179190911790555050505b60400151949350505050565b6000808a8a8a8a8a8a8a8a8a604051602001612e7f99989796959493929190614c7b565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179b9a5050505050505050505050565b6000612158825490565b6000612f1b83836133b8565b9392505050565b6000612f1b8373ffffffffffffffffffffffffffffffffffffffff84166133e2565b6000612f1b8373ffffffffffffffffffffffffffffffffffffffff84166134dc565b6000818160045b600f811015612ff3577fff000000000000000000000000000000000000000000000000000000000000008216838260208110612fab57612fab61434d565b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614612fe157506000949350505050565b80612feb816145d9565b915050612f6d565b5081600f1a600181111561300957613009614b07565b949350505050565b60008080808560600151600181111561302c5761302c614b07565b036130525761303e888888888861352b565b61304d57600092509050613126565b6130ca565b60018560600151600181111561306a5761306a614b07565b0361309857600061307d898989886136b6565b92509050806130925750600092509050613126565b506130ca565b6040517ff2b2d41200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84516040015163ffffffff16871061311f57877fc3237c8807c467c1b39b8d0395eff077313e691bf0a7388106792564ebfd56368760405161310c9190613b21565b60405180910390a2600092509050613126565b6001925090505b9550959350505050565b60008160600151600181111561314857613148614b07565b036131ac57600083815260046020526040902060010180547fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff16780100000000000000000000000000000000000000000000000063ffffffff851602179055505050565b6001816060015160018111156131c4576131c4614b07565b036132305760c08101805160009081526008602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055915191517fa4a4e334c0e330143f9437484fe516c13bc560b86b5b0daf58e7084aaac228f29190a25b505050565b600080613248898886868a8a60016138c4565b60008a8152600460205260408120600101549294509092506c010000000000000000000000009091046bffffffffffffffffffffffff169061328a8385614315565b9050836bffffffffffffffffffffffff16826bffffffffffffffffffffffff1610156132be575091506000905081806132f1565b806bffffffffffffffffffffffff16826bffffffffffffffffffffffff1610156132f15750806132ee8482614bfb565b92505b60008a81526004602052604090206001018054829190600c906133339084906c0100000000000000000000000090046bffffffffffffffffffffffff16614bfb565b82546101009290920a6bffffffffffffffffffffffff81810219909316918316021790915560008c81526004602052604081206001018054859450909261337c91859116614315565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550505097509795505050505050565b60008260000182815481106133cf576133cf61434d565b9060005260206000200154905092915050565b600081815260018301602052604081205480156134cb576000613406600183614b68565b855490915060009061341a90600190614b68565b905081811461347f57600086600001828154811061343a5761343a61434d565b906000526020600020015490508087600001848154811061345d5761345d61434d565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061349057613490614d10565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050612158565b6000915050612158565b5092915050565b600081815260018301602052604081205461352357508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155612158565b506000612158565b600080848060200190518101906135429190614d3f565b845160c00151815191925063ffffffff9081169116101561359f57867f405288ea7be309e16cfdf481367f90a413e1d4634fcdaf8966546db9b93012e88660405161358d9190613b21565b60405180910390a260009150506136ad565b826101200151801561366057506020810151158015906136605750602081015161014084015182516040517f85df51fd00000000000000000000000000000000000000000000000000000000815263ffffffff909116600482015273ffffffffffffffffffffffffffffffffffffffff909116906385df51fd90602401602060405180830381865afa158015613639573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061365d9190614611565b14155b806136725750805163ffffffff168611155b156136a757867f6aa7f60c176da7af894b384daea2249497448137f5943c1237ada8bc92bdc3018660405161358d9190613b21565b60019150505b95945050505050565b6000806000848060200190518101906136cf9190614d97565b905060008782600001518360200151846040015160405160200161373194939291909384526020840192909252604083015260e01b7fffffffff0000000000000000000000000000000000000000000000000000000016606082015260640190565b604051602081830303815290604052805190602001209050846101200151801561380d575060808201511580159061380d5750608082015161014086015160608401516040517f85df51fd00000000000000000000000000000000000000000000000000000000815263ffffffff909116600482015273ffffffffffffffffffffffffffffffffffffffff909116906385df51fd90602401602060405180830381865afa1580156137e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061380a9190614611565b14155b80613822575086826060015163ffffffff1610155b1561386c57877f6aa7f60c176da7af894b384daea2249497448137f5943c1237ada8bc92bdc301876040516138579190613b21565b60405180910390a26000935091506138bb9050565b60008181526008602052604090205460ff16156138b357877f405288ea7be309e16cfdf481367f90a413e1d4634fcdaf8966546db9b93012e8876040516138579190613b21565b600193509150505b94509492505050565b60008060008960a0015161ffff16866138dd9190614b51565b90508380156138eb5750803a105b156138f357503a5b600085886139018b8d61433a565b61390b9085614b51565b613915919061433a565b61392790670de0b6b3a7640000614b51565b6139319190614baa565b905060008b6040015163ffffffff1664e8d4a510006139509190614b51565b60208d0151889063ffffffff168b6139688f88614b51565b613972919061433a565b61398090633b9aca00614b51565b61398a9190614b51565b6139949190614baa565b61399e919061433a565b90506b033b2e3c9fd0803ce80000006139b7828461433a565b11156139ef576040517f2ad7547a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b909b909a5098505050505050505050565b5080546000825590600052602060002090810190610bb19190613aa8565b828054828255906000526020600020908101928215613a98579160200282015b82811115613a9857825182547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909116178255602090920191600190910190613a3e565b50613aa4929150613aa8565b5090565b5b80821115613aa45760008155600101613aa9565b6000815180845260005b81811015613ae357602081850181015186830182015201613ac7565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081526000612f1b6020830184613abd565b73ffffffffffffffffffffffffffffffffffffffff81168114610bb157600080fd5b8035613b6181613b34565b919050565b60008083601f840112613b7857600080fd5b50813567ffffffffffffffff811115613b9057600080fd5b602083019150836020828501011115613ba857600080fd5b9250929050565b60008060008060608587031215613bc557600080fd5b8435613bd081613b34565b935060208501359250604085013567ffffffffffffffff811115613bf357600080fd5b613bff87828801613b66565b95989497509550505050565b600080600060408486031215613c2057600080fd5b83359250602084013567ffffffffffffffff811115613c3e57600080fd5b613c4a86828701613b66565b9497909650939450505050565b60008083601f840112613c6957600080fd5b50813567ffffffffffffffff811115613c8157600080fd5b6020830191508360208260051b8501011115613ba857600080fd5b60008060008060008060008060e0898b031215613cb857600080fd5b606089018a811115613cc957600080fd5b8998503567ffffffffffffffff80821115613ce357600080fd5b613cef8c838d01613b66565b909950975060808b0135915080821115613d0857600080fd5b613d148c838d01613c57565b909750955060a08b0135915080821115613d2d57600080fd5b50613d3a8b828c01613c57565b999c989b50969995989497949560c00135949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610220810167ffffffffffffffff81118282101715613da657613da6613d53565b60405290565b60405160c0810167ffffffffffffffff81118282101715613da657613da6613d53565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613e1657613e16613d53565b604052919050565b600067ffffffffffffffff821115613e3857613e38613d53565b5060051b60200190565b600082601f830112613e5357600080fd5b81356020613e68613e6383613e1e565b613dcf565b82815260059290921b84018101918181019086841115613e8757600080fd5b8286015b84811015613eab578035613e9e81613b34565b8352918301918301613e8b565b509695505050505050565b803560ff81168114613b6157600080fd5b600082601f830112613ed857600080fd5b813567ffffffffffffffff811115613ef257613ef2613d53565b613f2360207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613dcf565b818152846020838601011115613f3857600080fd5b816020850160208301376000918101602001919091529392505050565b803567ffffffffffffffff81168114613b6157600080fd5b60008060008060008060c08789031215613f8657600080fd5b863567ffffffffffffffff80821115613f9e57600080fd5b613faa8a838b01613e42565b97506020890135915080821115613fc057600080fd5b613fcc8a838b01613e42565b9650613fda60408a01613eb6565b95506060890135915080821115613ff057600080fd5b613ffc8a838b01613ec7565b945061400a60808a01613f55565b935060a089013591508082111561402057600080fd5b5061402d89828a01613ec7565b9150509295509295509295565b60006020828403121561404c57600080fd5b8135612f1b81613b34565b63ffffffff81168114610bb157600080fd5b8035613b6181614057565b62ffffff81168114610bb157600080fd5b8035613b6181614074565b61ffff81168114610bb157600080fd5b8035613b6181614090565b6bffffffffffffffffffffffff81168114610bb157600080fd5b8035613b61816140ab565b8015158114610bb157600080fd5b8035613b61816140d0565b600061022082840312156140fc57600080fd5b614104613d82565b905061410f82614069565b815261411d60208301614069565b602082015261412e60408301614069565b604082015261413f60608301614085565b6060820152614150608083016140a0565b608082015261416160a083016140c5565b60a082015261417260c08301614069565b60c082015261418360e08301614069565b60e0820152610100614196818401614069565b908201526101206141a8838201614069565b90820152610140828101359082015261016080830135908201526101806141d0818401613b56565b908201526101a08281013567ffffffffffffffff8111156141f057600080fd5b6141fc85828601613e42565b8284015250506101c0614210818401613b56565b908201526101e0614222838201613b56565b908201526102006142348382016140de565b9082015292915050565b60008060008060008060c0878903121561425757600080fd5b863567ffffffffffffffff8082111561426f57600080fd5b61427b8a838b01613e42565b9750602089013591508082111561429157600080fd5b61429d8a838b01613e42565b96506142ab60408a01613eb6565b955060608901359150808211156142c157600080fd5b613ffc8a838b016140e9565b6000602082840312156142df57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6bffffffffffffffffffffffff8181168382160190808211156134d5576134d56142e6565b80820180821115612158576121586142e6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60ff8181168382160190811115612158576121586142e6565b8051613b6181614057565b8051613b6181614074565b8051613b6181614090565b8051613b61816140ab565b8051613b6181613b34565b600082601f8301126143dd57600080fd5b815160206143ed613e6383613e1e565b82815260059290921b8401810191818101908684111561440c57600080fd5b8286015b84811015613eab57805161442381613b34565b8352918301918301614410565b8051613b61816140d0565b60006020828403121561444d57600080fd5b815167ffffffffffffffff8082111561446557600080fd5b90830190610220828603121561447a57600080fd5b614482613d82565b61448b83614395565b815261449960208401614395565b60208201526144aa60408401614395565b60408201526144bb606084016143a0565b60608201526144cc608084016143ab565b60808201526144dd60a084016143b6565b60a08201526144ee60c08401614395565b60c08201526144ff60e08401614395565b60e0820152610100614512818501614395565b90820152610120614524848201614395565b908201526101408381015190820152610160808401519082015261018061454c8185016143c1565b908201526101a0838101518381111561456457600080fd5b614570888287016143cc565b8284015250506101c091506145868284016143c1565b828201526101e0915061459a8284016143c1565b8282015261020091506145ae828401614430565b91810191909152949350505050565b60ff81811683821602908116908181146134d5576134d56142e6565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361460a5761460a6142e6565b5060010190565b60006020828403121561462357600080fd5b5051919050565b63ffffffff8181168382160190808211156134d5576134d56142e6565b600081518084526020808501945080840160005b8381101561468d57815173ffffffffffffffffffffffffffffffffffffffff168752958201959082019060010161465b565b509495945050505050565b602081526146af60208201835163ffffffff169052565b600060208301516146c8604084018263ffffffff169052565b50604083015163ffffffff8116606084015250606083015162ffffff8116608084015250608083015161ffff811660a08401525060a08301516bffffffffffffffffffffffff811660c08401525060c083015163ffffffff811660e08401525060e08301516101006147418185018363ffffffff169052565b840151905061012061475a8482018363ffffffff169052565b84015190506101406147738482018363ffffffff169052565b840151610160848101919091528401516101808085019190915284015190506101a06147b68185018373ffffffffffffffffffffffffffffffffffffffff169052565b808501519150506102206101c081818601526147d6610240860184614647565b908601519092506101e06148018682018373ffffffffffffffffffffffffffffffffffffffff169052565b860151905061020061482a8682018373ffffffffffffffffffffffffffffffffffffffff169052565b90950151151593019290925250919050565b600061012063ffffffff808d1684528b6020850152808b1660408501525080606084015261486c8184018a614647565b905082810360808401526148808189614647565b905060ff871660a084015282810360c084015261489d8187613abd565b905067ffffffffffffffff851660e08401528281036101008401526148c28185613abd565b9c9b505050505050505050505050565b8281526040602082015260006130096040830184613abd565b600080604083850312156148fe57600080fd5b8251614909816140d0565b6020939093015192949293505050565b8183823760009101908152919050565b8281526080810160608360208401379392505050565b600082601f83011261495057600080fd5b81356020614960613e6383613e1e565b82815260059290921b8401810191818101908684111561497f57600080fd5b8286015b84811015613eab5780358352918301918301614983565b600082601f8301126149ab57600080fd5b813560206149bb613e6383613e1e565b82815260059290921b840181019181810190868411156149da57600080fd5b8286015b84811015613eab57803567ffffffffffffffff8111156149fe5760008081fd5b614a0c8986838b0101613ec7565b8452509183019183016149de565b600060208284031215614a2c57600080fd5b813567ffffffffffffffff80821115614a4457600080fd5b9083019060c08286031215614a5857600080fd5b614a60613dac565b8235815260208301356020820152604083013582811115614a8057600080fd5b614a8c8782860161493f565b604083015250606083013582811115614aa457600080fd5b614ab08782860161493f565b606083015250608083013582811115614ac857600080fd5b614ad48782860161499a565b60808301525060a083013582811115614aec57600080fd5b614af88782860161499a565b60a08301525095945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b61ffff8181168382160190808211156134d5576134d56142e6565b8082028115828204841417612158576121586142e6565b81810381811115612158576121586142e6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082614bb957614bb9614b7b565b500490565b6bffffffffffffffffffffffff85168152836020820152826040820152608060608201526000614bf16080830184613abd565b9695505050505050565b6bffffffffffffffffffffffff8281168282160390808211156134d5576134d56142e6565b60006bffffffffffffffffffffffff80841680614c3f57614c3f614b7b565b92169190910492915050565b6bffffffffffffffffffffffff818116838216028082169190828114614c7357614c736142e6565b505092915050565b60006101208b835273ffffffffffffffffffffffffffffffffffffffff8b16602084015267ffffffffffffffff808b166040850152816060850152614cc28285018b614647565b91508382036080850152614cd6828a614647565b915060ff881660a085015283820360c0850152614cf38288613abd565b90861660e085015283810361010085015290506148c28185613abd565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b600060408284031215614d5157600080fd5b6040516040810181811067ffffffffffffffff82111715614d7457614d74613d53565b6040528251614d8281614057565b81526020928301519281019290925250919050565b600060a08284031215614da957600080fd5b60405160a0810181811067ffffffffffffffff82111715614dcc57614dcc613d53565b806040525082518152602083015160208201526040830151614ded81614057565b60408201526060830151614e0081614057565b6060820152608092830151928101929092525091905056fea164736f6c6343000813000a", + Bin: "0x6101406040523480156200001257600080fd5b506040516200526d3803806200526d8339810160408190526200003591620003b1565b80816001600160a01b031663ca30e6036040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000075573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200009b9190620003b1565b826001600160a01b031663b10b673c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620000da573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001009190620003b1565b836001600160a01b0316636709d0e56040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200013f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001659190620003b1565b846001600160a01b0316635425d8ac6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001a4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001ca9190620003b1565b856001600160a01b031663a08714c06040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000209573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200022f9190620003b1565b3380600081620002865760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620002b957620002b981620002ed565b5050506001600160a01b0394851660805292841660a05290831660c052821660e052811661010052166101205250620003d8565b336001600160a01b03821603620003475760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016200027d565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6001600160a01b0381168114620003ae57600080fd5b50565b600060208284031215620003c457600080fd5b8151620003d18162000398565b9392505050565b60805160a05160c05160e0516101005161012051614e48620004256000396000818160c9015261017c01526000611bb8015260005050600050506000505060006104c20152614e486000f3fe6080604052600436106100c75760003560e01c8063aed2e92911610074578063e3d0e7121161004e578063e3d0e71214610348578063f2fde38b14610368578063f75f6b1114610388576100c7565b8063aed2e929146102a3578063afcb95d7146102da578063b1dc65a414610328576100c7565b806381ff7048116100a557806381ff7048146101d65780638da5cb5b14610258578063a4c0ed3614610283576100c7565b8063181f5a771461010e578063349e8cca1461016d57806379ba5097146101c1575b7f00000000000000000000000000000000000000000000000000000000000000003660008037600080366000845af43d6000803e808015610107573d6000f35b3d6000fd5b005b34801561011a57600080fd5b506101576040518060400160405280601881526020017f4175746f6d6174696f6e526567697374727920322e322e30000000000000000081525081565b6040516101649190613b44565b60405180910390f35b34801561017957600080fd5b507f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610164565b3480156101cd57600080fd5b5061010c6103a8565b3480156101e257600080fd5b5061023560155460115463ffffffff780100000000000000000000000000000000000000000000000083048116937c01000000000000000000000000000000000000000000000000000000009093041691565b6040805163ffffffff948516815293909216602084015290820152606001610164565b34801561026457600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff1661019c565b34801561028f57600080fd5b5061010c61029e366004613bd2565b6104aa565b3480156102af57600080fd5b506102c36102be366004613c2e565b6106c6565b604080519215158352602083019190915201610164565b3480156102e657600080fd5b50601154601254604080516000815260208101939093527401000000000000000000000000000000000000000090910463ffffffff1690820152606001610164565b34801561033457600080fd5b5061010c610343366004613cbf565b61083c565b34801561035457600080fd5b5061010c610363366004613f90565b610b77565b34801561037457600080fd5b5061010c61038336600461405d565b610ba0565b34801561039457600080fd5b5061010c6103a3366004614261565b610bb4565b60015473ffffffffffffffffffffffffffffffffffffffff16331461042e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610519576040517fc8bad78d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208114610553576040517fdfe9309000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610561828401846142f0565b60008181526004602052604090205490915065010000000000900463ffffffff908116146105bb576040517f9c0083a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000818152600460205260409020600101546105f69085906c0100000000000000000000000090046bffffffffffffffffffffffff16614338565b600082815260046020526040902060010180546bffffffffffffffffffffffff929092166c01000000000000000000000000027fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff90921691909117905560195461066190859061435d565b6019556040516bffffffffffffffffffffffff8516815273ffffffffffffffffffffffffffffffffffffffff86169082907fafd24114486da8ebfc32f3626dada8863652e187461aa74d4bfa7348915062039060200160405180910390a35050505050565b6000806106d1611ba0565b6012547e01000000000000000000000000000000000000000000000000000000000000900460ff1615610730576040517f24522f3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600085815260046020908152604091829020825160e081018452815460ff811615158252610100810463ffffffff908116838601819052650100000000008304821684880152690100000000000000000090920473ffffffffffffffffffffffffffffffffffffffff16606084018190526001909401546bffffffffffffffffffffffff80821660808601526c0100000000000000000000000082041660a0850152780100000000000000000000000000000000000000000000000090041660c08301528451601f8901859004850281018501909552878552909361082f938990899081908401838280828437600092019190915250611c1192505050565b9097909650945050505050565b60005a60408051610160810182526012546bffffffffffffffffffffffff8116825263ffffffff6c010000000000000000000000008204811660208401527001000000000000000000000000000000008204811693830193909352740100000000000000000000000000000000000000008104909216606082015262ffffff7801000000000000000000000000000000000000000000000000830416608082015261ffff7b0100000000000000000000000000000000000000000000000000000083041660a082015260ff7d0100000000000000000000000000000000000000000000000000000000008304811660c08301527e010000000000000000000000000000000000000000000000000000000000008304811615801560e08401527f01000000000000000000000000000000000000000000000000000000000000009093048116151561010080840191909152601354918216151561012084015273ffffffffffffffffffffffffffffffffffffffff9104166101408201529192506109f2576040517f24522f3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152600b602052604090205460ff16610a3b576040517f1099ed7500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6011548a3514610a77576040517fdfdcf8e700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60c0810151610a8790600161439f565b60ff1686141580610a985750858414155b15610acf576040517f0244f71a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610adf8a8a8a8a8a8a8a8a611e3a565b6000610aeb8a8a6120a3565b905060208b0135600881901c63ffffffff16610b0884848761215e565b836060015163ffffffff168163ffffffff161115610b6857601280547fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000063ffffffff8416021790555b50505050505050505050505050565b610b9886868686806020019051810190610b91919061445e565b8686610bb4565b505050505050565b610ba8612b00565b610bb181612b81565b50565b610bbc612b00565b601f86511115610bf8576040517f25d0209c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8360ff16600003610c35576040517fe77dba5600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84518651141580610c545750610c4c8460036145e0565b60ff16865111155b15610c8b576040517f1d2d1c5800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601254600e546bffffffffffffffffffffffff9091169060005b816bffffffffffffffffffffffff16811015610d0d57610cfa600e8281548110610cd157610cd1614370565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff168484612c76565b5080610d05816145fc565b915050610ca5565b5060008060005b836bffffffffffffffffffffffff16811015610e1657600d8181548110610d3d57610d3d614370565b600091825260209091200154600e805473ffffffffffffffffffffffffffffffffffffffff90921694509082908110610d7857610d78614370565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff8681168452600c8352604080852080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001690559116808452600b90925290912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055915080610e0e816145fc565b915050610d14565b50610e23600d6000613a23565b610e2f600e6000613a23565b604080516080810182526000808252602082018190529181018290526060810182905290805b8c5181101561129857600c60008e8381518110610e7457610e74614370565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040016000205460ff1615610edf576040517f77cea0fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168d8281518110610f0957610f09614370565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1603610f5e576040517f815e1d6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405180604001604052806001151581526020018260ff16815250600c60008f8481518110610f8f57610f8f614370565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528181019290925260400160002082518154939092015160ff16610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909316929092171790558b518c908290811061103757611037614370565b60200260200101519150600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036110a7576040517f58a70a0a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82166000908152600b60209081526040918290208251608081018452905460ff80821615801584526101008304909116938301939093526bffffffffffffffffffffffff6201000082048116948301949094526e01000000000000000000000000000090049092166060830152909350611162576040517f6a7281ad00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001835260ff80821660208086019182526bffffffffffffffffffffffff808b166060880190815273ffffffffffffffffffffffffffffffffffffffff87166000908152600b909352604092839020885181549551948a0151925184166e010000000000000000000000000000027fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff939094166201000002929092167fffffffffffff000000000000000000000000000000000000000000000000ffff94909616610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00009095169490941717919091169290921791909117905580611290816145fc565b915050610e55565b50508a516112ae9150600d9060208d0190613a41565b5088516112c290600e9060208c0190613a41565b50604051806101600160405280856bffffffffffffffffffffffff168152602001886000015163ffffffff168152602001886020015163ffffffff168152602001600063ffffffff168152602001886060015162ffffff168152602001886080015161ffff1681526020018960ff1681526020016012600001601e9054906101000a900460ff16151581526020016012600001601f9054906101000a900460ff161515815260200188610200015115158152602001886101e0015173ffffffffffffffffffffffffffffffffffffffff16815250601260008201518160000160006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550602082015181600001600c6101000a81548163ffffffff021916908363ffffffff16021790555060408201518160000160106101000a81548163ffffffff021916908363ffffffff16021790555060608201518160000160146101000a81548163ffffffff021916908363ffffffff16021790555060808201518160000160186101000a81548162ffffff021916908362ffffff16021790555060a082015181600001601b6101000a81548161ffff021916908361ffff16021790555060c082015181600001601d6101000a81548160ff021916908360ff16021790555060e082015181600001601e6101000a81548160ff02191690831515021790555061010082015181600001601f6101000a81548160ff0219169083151502179055506101208201518160010160006101000a81548160ff0219169083151502179055506101408201518160010160016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509050506040518061018001604052808860a001516bffffffffffffffffffffffff16815260200188610180015173ffffffffffffffffffffffffffffffffffffffff168152602001601460010160009054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff168152602001886040015163ffffffff1681526020018860c0015163ffffffff168152602001601460010160149054906101000a900463ffffffff1663ffffffff168152602001601460010160189054906101000a900463ffffffff1663ffffffff1681526020016014600101601c9054906101000a900463ffffffff1663ffffffff1681526020018860e0015163ffffffff16815260200188610100015163ffffffff16815260200188610120015163ffffffff168152602001886101c0015173ffffffffffffffffffffffffffffffffffffffff16815250601460008201518160000160006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550602082015181600001600c6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060408201518160010160006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550606082015181600101600c6101000a81548163ffffffff021916908363ffffffff16021790555060808201518160010160106101000a81548163ffffffff021916908363ffffffff16021790555060a08201518160010160146101000a81548163ffffffff021916908363ffffffff16021790555060c08201518160010160186101000a81548163ffffffff021916908363ffffffff16021790555060e082015181600101601c6101000a81548163ffffffff021916908363ffffffff1602179055506101008201518160020160006101000a81548163ffffffff021916908363ffffffff1602179055506101208201518160020160046101000a81548163ffffffff021916908363ffffffff1602179055506101408201518160020160086101000a81548163ffffffff021916908363ffffffff16021790555061016082015181600201600c6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555090505086610140015160178190555086610160015160188190555060006014600101601c9054906101000a900463ffffffff169050876101e0015173ffffffffffffffffffffffffffffffffffffffff166357e871e76040518163ffffffff1660e01b8152600401602060405180830381865afa158015611963573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119879190614634565b601580547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167c010000000000000000000000000000000000000000000000000000000063ffffffff9384160217808255600192601891611a02918591780100000000000000000000000000000000000000000000000090041661464d565b92506101000a81548163ffffffff021916908363ffffffff160217905550600088604051602001611a3391906146bb565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0018152919052601554909150611a9c90469030907801000000000000000000000000000000000000000000000000900463ffffffff168f8f8f878f8f612e7e565b60115560005b611aac6009612f28565b811015611adc57611ac9611ac1600983612f32565b600990612f45565b5080611ad4816145fc565b915050611aa2565b5060005b896101a0015151811015611b3357611b208a6101a001518281518110611b0857611b08614370565b60200260200101516009612f6790919063ffffffff16565b5080611b2b816145fc565b915050611ae0565b507f1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e0582601154601460010160189054906101000a900463ffffffff168f8f8f878f8f604051611b8a9998979695949392919061485f565b60405180910390a1505050505050505050505050565b3273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614611c0f576040517fb60ac5db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b60125460009081907f0100000000000000000000000000000000000000000000000000000000000000900460ff1615611c76576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601280547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01000000000000000000000000000000000000000000000000000000000000001790556040517f4585e33b0000000000000000000000000000000000000000000000000000000090611cf2908590602401613b44565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290517f79188d1600000000000000000000000000000000000000000000000000000000815290935073ffffffffffffffffffffffffffffffffffffffff8616906379188d1690611dc590879087906004016148f5565b60408051808303816000875af1158015611de3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e07919061490e565b601280547effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16905590969095509350505050565b60008787604051611e4c92919061493c565b604051908190038120611e63918b9060200161494c565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201208383019092526000808452908301819052909250906000805b8881101561203a57600185878360208110611ecf57611ecf614370565b611edc91901a601b61439f565b8c8c85818110611eee57611eee614370565b905060200201358b8b86818110611f0757611f07614370565b9050602002013560405160008152602001604052604051611f44949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa158015611f66573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081015173ffffffffffffffffffffffffffffffffffffffff81166000908152600c602090815290849020838501909452925460ff8082161515808552610100909204169383019390935290955093509050612014576040517f0f4c073700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b826020015160080260ff166001901b840193508080612032906145fc565b915050611eb2565b50827e01010101010101010101010101010101010101010101010101010101010101841614612095576040517fc103be2e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050505050505050565b6120dc6040518060c001604052806000815260200160008152602001606081526020016060815260200160608152602001606081525090565b60006120ea83850185614a3d565b604081015151606082015151919250908114158061210d57508082608001515114155b8061211d5750808260a001515114155b15612154576040517fb55ac75400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5090505b92915050565b600082604001515167ffffffffffffffff81111561217e5761217e613d76565b60405190808252806020026020018201604052801561223a57816020015b604080516101c081018252600060e08201818152610100830182905261012083018290526101408301829052610160830182905261018083018290526101a0830182905282526020808301829052928201819052606082018190526080820181905260a0820181905260c082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90920191018161219c5790505b50905060006040518060800160405280600061ffff1681526020016000815260200160006bffffffffffffffffffffffff16815260200160006bffffffffffffffffffffffff168152509050600085610140015173ffffffffffffffffffffffffffffffffffffffff166357e871e76040518163ffffffff1660e01b8152600401602060405180830381865afa1580156122d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122fc9190614634565b6101408701516040517f7810d12a00000000000000000000000000000000000000000000000000000000815236600482015291925060009173ffffffffffffffffffffffffffffffffffffffff90911690637810d12a90602401602060405180830381865afa158015612373573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123979190614634565b905060005b8660400151518110156127e65760046000886040015183815181106123c3576123c3614370565b6020908102919091018101518252818101929092526040908101600020815160e081018352815460ff811615158252610100810463ffffffff90811695830195909552650100000000008104851693820193909352690100000000000000000090920473ffffffffffffffffffffffffffffffffffffffff166060830152600101546bffffffffffffffffffffffff80821660808401526c0100000000000000000000000082041660a08301527801000000000000000000000000000000000000000000000000900490911660c082015285518690839081106124a8576124a8614370565b6020026020010151600001819052506124dd876040015182815181106124d0576124d0614370565b6020026020010151612f89565b8582815181106124ef576124ef614370565b602002602001015160600190600181111561250c5761250c614b2a565b9081600181111561251f5761251f614b2a565b815250506125838760400151828151811061253c5761253c614370565b6020026020010151848960800151848151811061255b5761255b614370565b602002602001015188858151811061257557612575614370565b60200260200101518c613034565b86838151811061259557612595614370565b60200260200101516020018784815181106125b2576125b2614370565b602002602001015160c00182815250821515151581525050508481815181106125dd576125dd614370565b6020026020010151602001511561260d576001846000018181516126019190614b59565b61ffff16905250612612565b6127d4565b61267885828151811061262757612627614370565b602002602001015160000151606001518860600151838151811061264d5761264d614370565b60200260200101518960a00151848151811061266b5761266b614370565b6020026020010151611c11565b86838151811061268a5761268a614370565b60200260200101516040018784815181106126a7576126a7614370565b602090810291909101015160800191909152901515905260c08801516126ce90600161439f565b6126dc9060ff166040614b74565b6103a48860a0015183815181106126f5576126f5614370565b602002602001015151612708919061435d565b612712919061435d565b85828151811061272457612724614370565b602002602001015160a001818152505084818151811061274657612746614370565b602002602001015160a0015184602001818151612763919061435d565b905250845185908290811061277a5761277a614370565b602002602001015160800151866127919190614b8b565b95506127d4876040015182815181106127ac576127ac614370565b6020026020010151848784815181106127c7576127c7614370565b6020026020010151613153565b806127de816145fc565b91505061239c565b50825161ffff166000036127fd5750505050505050565b6155f061280b366010614b74565b5a6128169088614b8b565b612820919061435d565b61282a919061435d565b8351909550611b58906128419061ffff1687614bcd565b61284b919061435d565b945060008060005b886040015151811015612a2f5786818151811061287257612872614370565b60200260200101516020015115612a1d5761290b8a8a60400151838151811061289d5761289d614370565b60200260200101518984815181106128b7576128b7614370565b6020026020010151608001518c600001518d602001518d8c602001518e89815181106128e5576128e5614370565b602002602001015160a001518c6128fc9190614b74565b6129069190614bcd565b613258565b6060880180519295509093508391612924908390614338565b6bffffffffffffffffffffffff16905250604086018051849190612949908390614338565b6bffffffffffffffffffffffff16905250865187908290811061296e5761296e614370565b60200260200101516040015115158960400151828151811061299257612992614370565b60200260200101517fad8cc9579b21dfe2c2f6ea35ba15b656e46b4f5b0cb424f52739b8ce5cac9c5b84866129c79190614338565b8a85815181106129d9576129d9614370565b6020026020010151608001518c8e6080015187815181106129fc576129fc614370565b6020026020010151604051612a149493929190614be1565b60405180910390a35b80612a27816145fc565b915050612853565b505050604083810151336000908152600b602052919091208054600290612a6b9084906201000090046bffffffffffffffffffffffff16614338565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508260600151601260000160008282829054906101000a90046bffffffffffffffffffffffff16612ac99190614338565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555050505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314611c0f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610425565b3373ffffffffffffffffffffffffffffffffffffffff821603612c00576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610425565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b73ffffffffffffffffffffffffffffffffffffffff83166000908152600b602090815260408083208151608081018352905460ff80821615801584526101008304909116948301949094526bffffffffffffffffffffffff6201000082048116938301939093526e0100000000000000000000000000009004909116606082015290612e72576000816060015185612d0e9190614c1e565b90506000612d1c8583614c43565b90508083604001818151612d309190614338565b6bffffffffffffffffffffffff16905250612d4b8582614c6e565b83606001818151612d5c9190614338565b6bffffffffffffffffffffffff90811690915273ffffffffffffffffffffffffffffffffffffffff89166000908152600b602090815260409182902087518154928901519389015160608a015186166e010000000000000000000000000000027fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff919096166201000002167fffffffffffff000000000000000000000000000000000000000000000000ffff60ff95909516610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909416939093171792909216179190911790555050505b60400151949350505050565b6000808a8a8a8a8a8a8a8a8a604051602001612ea299989796959493929190614c9e565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179b9a5050505050505050505050565b6000612158825490565b6000612f3e83836133db565b9392505050565b6000612f3e8373ffffffffffffffffffffffffffffffffffffffff8416613405565b6000612f3e8373ffffffffffffffffffffffffffffffffffffffff84166134ff565b6000818160045b600f811015613016577fff000000000000000000000000000000000000000000000000000000000000008216838260208110612fce57612fce614370565b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161461300457506000949350505050565b8061300e816145fc565b915050612f90565b5081600f1a600181111561302c5761302c614b2a565b949350505050565b60008080808560600151600181111561304f5761304f614b2a565b0361307557613061888888888861354e565b61307057600092509050613149565b6130ed565b60018560600151600181111561308d5761308d614b2a565b036130bb5760006130a0898989886136d9565b92509050806130b55750600092509050613149565b506130ed565b6040517ff2b2d41200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84516040015163ffffffff16871061314257877fc3237c8807c467c1b39b8d0395eff077313e691bf0a7388106792564ebfd56368760405161312f9190613b44565b60405180910390a2600092509050613149565b6001925090505b9550959350505050565b60008160600151600181111561316b5761316b614b2a565b036131cf57600083815260046020526040902060010180547fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff16780100000000000000000000000000000000000000000000000063ffffffff851602179055505050565b6001816060015160018111156131e7576131e7614b2a565b036132535760c08101805160009081526008602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055915191517fa4a4e334c0e330143f9437484fe516c13bc560b86b5b0daf58e7084aaac228f29190a25b505050565b60008061326b898886868a8a60016138e7565b60008a8152600460205260408120600101549294509092506c010000000000000000000000009091046bffffffffffffffffffffffff16906132ad8385614338565b9050836bffffffffffffffffffffffff16826bffffffffffffffffffffffff1610156132e157509150600090508180613314565b806bffffffffffffffffffffffff16826bffffffffffffffffffffffff1610156133145750806133118482614c1e565b92505b60008a81526004602052604090206001018054829190600c906133569084906c0100000000000000000000000090046bffffffffffffffffffffffff16614c1e565b82546101009290920a6bffffffffffffffffffffffff81810219909316918316021790915560008c81526004602052604081206001018054859450909261339f91859116614338565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550505097509795505050505050565b60008260000182815481106133f2576133f2614370565b9060005260206000200154905092915050565b600081815260018301602052604081205480156134ee576000613429600183614b8b565b855490915060009061343d90600190614b8b565b90508181146134a257600086600001828154811061345d5761345d614370565b906000526020600020015490508087600001848154811061348057613480614370565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806134b3576134b3614d33565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050612158565b6000915050612158565b5092915050565b600081815260018301602052604081205461354657508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155612158565b506000612158565b600080848060200190518101906135659190614d62565b845160c00151815191925063ffffffff908116911610156135c257867f405288ea7be309e16cfdf481367f90a413e1d4634fcdaf8966546db9b93012e8866040516135b09190613b44565b60405180910390a260009150506136d0565b826101200151801561368357506020810151158015906136835750602081015161014084015182516040517f85df51fd00000000000000000000000000000000000000000000000000000000815263ffffffff909116600482015273ffffffffffffffffffffffffffffffffffffffff909116906385df51fd90602401602060405180830381865afa15801561365c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136809190614634565b14155b806136955750805163ffffffff168611155b156136ca57867f6aa7f60c176da7af894b384daea2249497448137f5943c1237ada8bc92bdc301866040516135b09190613b44565b60019150505b95945050505050565b6000806000848060200190518101906136f29190614dba565b905060008782600001518360200151846040015160405160200161375494939291909384526020840192909252604083015260e01b7fffffffff0000000000000000000000000000000000000000000000000000000016606082015260640190565b604051602081830303815290604052805190602001209050846101200151801561383057506080820151158015906138305750608082015161014086015160608401516040517f85df51fd00000000000000000000000000000000000000000000000000000000815263ffffffff909116600482015273ffffffffffffffffffffffffffffffffffffffff909116906385df51fd90602401602060405180830381865afa158015613809573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061382d9190614634565b14155b80613845575086826060015163ffffffff1610155b1561388f57877f6aa7f60c176da7af894b384daea2249497448137f5943c1237ada8bc92bdc3018760405161387a9190613b44565b60405180910390a26000935091506138de9050565b60008181526008602052604090205460ff16156138d657877f405288ea7be309e16cfdf481367f90a413e1d4634fcdaf8966546db9b93012e88760405161387a9190613b44565b600193509150505b94509492505050565b60008060008960a0015161ffff16866139009190614b74565b905083801561390e5750803a105b1561391657503a5b600085886139248b8d61435d565b61392e9085614b74565b613938919061435d565b61394a90670de0b6b3a7640000614b74565b6139549190614bcd565b905060008b6040015163ffffffff1664e8d4a510006139739190614b74565b60208d0151889063ffffffff168b61398b8f88614b74565b613995919061435d565b6139a390633b9aca00614b74565b6139ad9190614b74565b6139b79190614bcd565b6139c1919061435d565b90506b033b2e3c9fd0803ce80000006139da828461435d565b1115613a12576040517f2ad7547a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b909b909a5098505050505050505050565b5080546000825590600052602060002090810190610bb19190613acb565b828054828255906000526020600020908101928215613abb579160200282015b82811115613abb57825182547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909116178255602090920191600190910190613a61565b50613ac7929150613acb565b5090565b5b80821115613ac75760008155600101613acc565b6000815180845260005b81811015613b0657602081850181015186830182015201613aea565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081526000612f3e6020830184613ae0565b73ffffffffffffffffffffffffffffffffffffffff81168114610bb157600080fd5b8035613b8481613b57565b919050565b60008083601f840112613b9b57600080fd5b50813567ffffffffffffffff811115613bb357600080fd5b602083019150836020828501011115613bcb57600080fd5b9250929050565b60008060008060608587031215613be857600080fd5b8435613bf381613b57565b935060208501359250604085013567ffffffffffffffff811115613c1657600080fd5b613c2287828801613b89565b95989497509550505050565b600080600060408486031215613c4357600080fd5b83359250602084013567ffffffffffffffff811115613c6157600080fd5b613c6d86828701613b89565b9497909650939450505050565b60008083601f840112613c8c57600080fd5b50813567ffffffffffffffff811115613ca457600080fd5b6020830191508360208260051b8501011115613bcb57600080fd5b60008060008060008060008060e0898b031215613cdb57600080fd5b606089018a811115613cec57600080fd5b8998503567ffffffffffffffff80821115613d0657600080fd5b613d128c838d01613b89565b909950975060808b0135915080821115613d2b57600080fd5b613d378c838d01613c7a565b909750955060a08b0135915080821115613d5057600080fd5b50613d5d8b828c01613c7a565b999c989b50969995989497949560c00135949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610220810167ffffffffffffffff81118282101715613dc957613dc9613d76565b60405290565b60405160c0810167ffffffffffffffff81118282101715613dc957613dc9613d76565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613e3957613e39613d76565b604052919050565b600067ffffffffffffffff821115613e5b57613e5b613d76565b5060051b60200190565b600082601f830112613e7657600080fd5b81356020613e8b613e8683613e41565b613df2565b82815260059290921b84018101918181019086841115613eaa57600080fd5b8286015b84811015613ece578035613ec181613b57565b8352918301918301613eae565b509695505050505050565b803560ff81168114613b8457600080fd5b600082601f830112613efb57600080fd5b813567ffffffffffffffff811115613f1557613f15613d76565b613f4660207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613df2565b818152846020838601011115613f5b57600080fd5b816020850160208301376000918101602001919091529392505050565b803567ffffffffffffffff81168114613b8457600080fd5b60008060008060008060c08789031215613fa957600080fd5b863567ffffffffffffffff80821115613fc157600080fd5b613fcd8a838b01613e65565b97506020890135915080821115613fe357600080fd5b613fef8a838b01613e65565b9650613ffd60408a01613ed9565b9550606089013591508082111561401357600080fd5b61401f8a838b01613eea565b945061402d60808a01613f78565b935060a089013591508082111561404357600080fd5b5061405089828a01613eea565b9150509295509295509295565b60006020828403121561406f57600080fd5b8135612f3e81613b57565b63ffffffff81168114610bb157600080fd5b8035613b848161407a565b62ffffff81168114610bb157600080fd5b8035613b8481614097565b61ffff81168114610bb157600080fd5b8035613b84816140b3565b6bffffffffffffffffffffffff81168114610bb157600080fd5b8035613b84816140ce565b8015158114610bb157600080fd5b8035613b84816140f3565b6000610220828403121561411f57600080fd5b614127613da5565b90506141328261408c565b81526141406020830161408c565b60208201526141516040830161408c565b6040820152614162606083016140a8565b6060820152614173608083016140c3565b608082015261418460a083016140e8565b60a082015261419560c0830161408c565b60c08201526141a660e0830161408c565b60e08201526101006141b981840161408c565b908201526101206141cb83820161408c565b90820152610140828101359082015261016080830135908201526101806141f3818401613b79565b908201526101a08281013567ffffffffffffffff81111561421357600080fd5b61421f85828601613e65565b8284015250506101c0614233818401613b79565b908201526101e0614245838201613b79565b90820152610200614257838201614101565b9082015292915050565b60008060008060008060c0878903121561427a57600080fd5b863567ffffffffffffffff8082111561429257600080fd5b61429e8a838b01613e65565b975060208901359150808211156142b457600080fd5b6142c08a838b01613e65565b96506142ce60408a01613ed9565b955060608901359150808211156142e457600080fd5b61401f8a838b0161410c565b60006020828403121561430257600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6bffffffffffffffffffffffff8181168382160190808211156134f8576134f8614309565b8082018082111561215857612158614309565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60ff818116838216019081111561215857612158614309565b8051613b848161407a565b8051613b8481614097565b8051613b84816140b3565b8051613b84816140ce565b8051613b8481613b57565b600082601f83011261440057600080fd5b81516020614410613e8683613e41565b82815260059290921b8401810191818101908684111561442f57600080fd5b8286015b84811015613ece57805161444681613b57565b8352918301918301614433565b8051613b84816140f3565b60006020828403121561447057600080fd5b815167ffffffffffffffff8082111561448857600080fd5b90830190610220828603121561449d57600080fd5b6144a5613da5565b6144ae836143b8565b81526144bc602084016143b8565b60208201526144cd604084016143b8565b60408201526144de606084016143c3565b60608201526144ef608084016143ce565b608082015261450060a084016143d9565b60a082015261451160c084016143b8565b60c082015261452260e084016143b8565b60e08201526101006145358185016143b8565b908201526101206145478482016143b8565b908201526101408381015190820152610160808401519082015261018061456f8185016143e4565b908201526101a0838101518381111561458757600080fd5b614593888287016143ef565b8284015250506101c091506145a98284016143e4565b828201526101e091506145bd8284016143e4565b8282015261020091506145d1828401614453565b91810191909152949350505050565b60ff81811683821602908116908181146134f8576134f8614309565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361462d5761462d614309565b5060010190565b60006020828403121561464657600080fd5b5051919050565b63ffffffff8181168382160190808211156134f8576134f8614309565b600081518084526020808501945080840160005b838110156146b057815173ffffffffffffffffffffffffffffffffffffffff168752958201959082019060010161467e565b509495945050505050565b602081526146d260208201835163ffffffff169052565b600060208301516146eb604084018263ffffffff169052565b50604083015163ffffffff8116606084015250606083015162ffffff8116608084015250608083015161ffff811660a08401525060a08301516bffffffffffffffffffffffff811660c08401525060c083015163ffffffff811660e08401525060e08301516101006147648185018363ffffffff169052565b840151905061012061477d8482018363ffffffff169052565b84015190506101406147968482018363ffffffff169052565b840151610160848101919091528401516101808085019190915284015190506101a06147d98185018373ffffffffffffffffffffffffffffffffffffffff169052565b808501519150506102206101c081818601526147f961024086018461466a565b908601519092506101e06148248682018373ffffffffffffffffffffffffffffffffffffffff169052565b860151905061020061484d8682018373ffffffffffffffffffffffffffffffffffffffff169052565b90950151151593019290925250919050565b600061012063ffffffff808d1684528b6020850152808b1660408501525080606084015261488f8184018a61466a565b905082810360808401526148a3818961466a565b905060ff871660a084015282810360c08401526148c08187613ae0565b905067ffffffffffffffff851660e08401528281036101008401526148e58185613ae0565b9c9b505050505050505050505050565b82815260406020820152600061302c6040830184613ae0565b6000806040838503121561492157600080fd5b825161492c816140f3565b6020939093015192949293505050565b8183823760009101908152919050565b8281526080810160608360208401379392505050565b600082601f83011261497357600080fd5b81356020614983613e8683613e41565b82815260059290921b840181019181810190868411156149a257600080fd5b8286015b84811015613ece57803583529183019183016149a6565b600082601f8301126149ce57600080fd5b813560206149de613e8683613e41565b82815260059290921b840181019181810190868411156149fd57600080fd5b8286015b84811015613ece57803567ffffffffffffffff811115614a215760008081fd5b614a2f8986838b0101613eea565b845250918301918301614a01565b600060208284031215614a4f57600080fd5b813567ffffffffffffffff80821115614a6757600080fd5b9083019060c08286031215614a7b57600080fd5b614a83613dcf565b8235815260208301356020820152604083013582811115614aa357600080fd5b614aaf87828601614962565b604083015250606083013582811115614ac757600080fd5b614ad387828601614962565b606083015250608083013582811115614aeb57600080fd5b614af7878286016149bd565b60808301525060a083013582811115614b0f57600080fd5b614b1b878286016149bd565b60a08301525095945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b61ffff8181168382160190808211156134f8576134f8614309565b808202811582820484141761215857612158614309565b8181038181111561215857612158614309565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082614bdc57614bdc614b9e565b500490565b6bffffffffffffffffffffffff85168152836020820152826040820152608060608201526000614c146080830184613ae0565b9695505050505050565b6bffffffffffffffffffffffff8281168282160390808211156134f8576134f8614309565b60006bffffffffffffffffffffffff80841680614c6257614c62614b9e565b92169190910492915050565b6bffffffffffffffffffffffff818116838216028082169190828114614c9657614c96614309565b505092915050565b60006101208b835273ffffffffffffffffffffffffffffffffffffffff8b16602084015267ffffffffffffffff808b166040850152816060850152614ce58285018b61466a565b91508382036080850152614cf9828a61466a565b915060ff881660a085015283820360c0850152614d168288613ae0565b90861660e085015283810361010085015290506148e58185613ae0565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b600060408284031215614d7457600080fd5b6040516040810181811067ffffffffffffffff82111715614d9757614d97613d76565b6040528251614da58161407a565b81526020928301519281019290925250919050565b600060a08284031215614dcc57600080fd5b60405160a0810181811067ffffffffffffffff82111715614def57614def613d76565b806040525082518152602083015160208201526040830151614e108161407a565b60408201526060830151614e238161407a565b6060820152608092830151928101929092525091905056fea164736f6c6343000813000a", } var AutomationRegistryABI = AutomationRegistryMetaData.ABI diff --git a/core/gethwrappers/generated/automation_registry_wrapper_2_3/automation_registry_wrapper_2_3.go b/core/gethwrappers/generated/automation_registry_wrapper_2_3/automation_registry_wrapper_2_3.go index 1d460036fa..97f0cfb113 100644 --- a/core/gethwrappers/generated/automation_registry_wrapper_2_3/automation_registry_wrapper_2_3.go +++ b/core/gethwrappers/generated/automation_registry_wrapper_2_3/automation_registry_wrapper_2_3.go @@ -76,7 +76,7 @@ type AutomationRegistryBase23PaymentReceipt struct { var AutomationRegistryMetaData = &bind.MetaData{ ABI: "[{\"inputs\":[{\"internalType\":\"contractAutomationRegistryLogicA2_3\",\"name\":\"logicA\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ArrayHasNoEntries\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotCancel\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CheckDataExceedsLimit\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ConfigDigestMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateEntry\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DuplicateSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitCanOnlyIncrease\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasLimitOutsideRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfFaultyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNumberOfSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IndexOutOfRange\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"available\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"requested\",\"type\":\"uint256\"}],\"name\":\"InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientLinkLiquidity\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidDataLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidFeed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRecipient\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReport\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTransmitter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTrigger\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTriggerType\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MigrationNotPermitted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MustSettleOffchain\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MustSettleOnchain\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotAContract\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyActiveTransmitters\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByLINKToken\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByOwnerOrRegistrar\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByProposedPayee\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByUpkeepPrivilegeManager\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyFinanceAdmin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyPausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlySimulatedBackend\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyUnpausedUpkeep\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ParameterLengthError\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReentrantCall\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RegistryPaused\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RepeatedTransmitter\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"reason\",\"type\":\"bytes\"}],\"name\":\"TargetCheckReverted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyOracles\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TranscoderNotSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TransferFailed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepAlreadyExists\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepCancelled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotCanceled\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UpkeepNotNeeded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ValueNotChanged\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddressNotAllowed\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"AdminPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"gasFeePPB\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"flatFeeMilliCents\",\"type\":\"uint24\"}],\"indexed\":false,\"internalType\":\"structAutomationRegistryBase2_3.BillingOverrides\",\"name\":\"overrides\",\"type\":\"tuple\"}],\"name\":\"BillingConfigOverridden\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"BillingConfigOverrideRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"contractIERC20Metadata\",\"name\":\"token\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"gasFeePPB\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"flatFeeMilliCents\",\"type\":\"uint24\"},{\"internalType\":\"contractAggregatorV3Interface\",\"name\":\"priceFeed\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"decimals\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"fallbackPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint96\",\"name\":\"minSpend\",\"type\":\"uint96\"}],\"indexed\":false,\"internalType\":\"structAutomationRegistryBase2_3.BillingConfig\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"BillingConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"CancelledUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newModule\",\"type\":\"address\"}],\"name\":\"ChainSpecificModuleUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"previousConfigBlockNumber\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"configCount\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"onchainConfig\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"offchainConfigVersion\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"dedupKey\",\"type\":\"bytes32\"}],\"name\":\"DedupKeyAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"assetAddress\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"FeesWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"FundsAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"FundsWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"InsufficientFundsUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"payees\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"payments\",\"type\":\"uint256[]\"}],\"name\":\"NOPsSettledOffchain\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"payees\",\"type\":\"address[]\"}],\"name\":\"PayeesUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"PayeeshipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"payee\",\"type\":\"address\"}],\"name\":\"PaymentWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"ReorgedUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"StaleUpkeepReport\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"epoch\",\"type\":\"uint32\"}],\"name\":\"Transmitted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"UpkeepAdminTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"atBlockHeight\",\"type\":\"uint64\"}],\"name\":\"UpkeepCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint96\",\"name\":\"gasChargeInBillingToken\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"premiumInBillingToken\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"gasReimbursementInJuels\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"premiumInJuels\",\"type\":\"uint96\"},{\"internalType\":\"contractIERC20Metadata\",\"name\":\"billingToken\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"linkUSD\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"nativeUSD\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"billingUSD\",\"type\":\"uint96\"}],\"indexed\":false,\"internalType\":\"structAutomationRegistryBase2_3.PaymentReceipt\",\"name\":\"receipt\",\"type\":\"tuple\"}],\"name\":\"UpkeepCharged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"newCheckData\",\"type\":\"bytes\"}],\"name\":\"UpkeepCheckDataSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"gasLimit\",\"type\":\"uint96\"}],\"name\":\"UpkeepGasLimitSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"remainingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"destination\",\"type\":\"address\"}],\"name\":\"UpkeepMigrated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepOffchainConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepPaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"totalPayment\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasUsed\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasOverhead\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"trigger\",\"type\":\"bytes\"}],\"name\":\"UpkeepPerformed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"privilegeConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepPrivilegeConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"startingBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"importedFrom\",\"type\":\"address\"}],\"name\":\"UpkeepReceived\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"performGas\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"}],\"name\":\"UpkeepRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"triggerConfig\",\"type\":\"bytes\"}],\"name\":\"UpkeepTriggerConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"UpkeepUnpaused\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"fallbackTo\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestConfigDetails\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"blockNumber\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestConfigDigestAndEpoch\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"scanLogs\",\"type\":\"bool\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"epoch\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"onchainConfigBytes\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"offchainConfigVersion\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"checkGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformGas\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxCheckDataSize\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"transcoder\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"reorgProtectionEnabled\",\"type\":\"bool\"},{\"internalType\":\"uint24\",\"name\":\"stalenessSeconds\",\"type\":\"uint24\"},{\"internalType\":\"uint32\",\"name\":\"maxPerformDataSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"maxRevertDataSize\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"upkeepPrivilegeManager\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"gasCeilingMultiplier\",\"type\":\"uint16\"},{\"internalType\":\"address\",\"name\":\"financeAdmin\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"fallbackGasPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fallbackLinkPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fallbackNativePrice\",\"type\":\"uint256\"},{\"internalType\":\"address[]\",\"name\":\"registrars\",\"type\":\"address[]\"},{\"internalType\":\"contractIChainModule\",\"name\":\"chainModule\",\"type\":\"address\"}],\"internalType\":\"structAutomationRegistryBase2_3.OnchainConfig\",\"name\":\"onchainConfig\",\"type\":\"tuple\"},{\"internalType\":\"uint64\",\"name\":\"offchainConfigVersion\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"},{\"internalType\":\"contractIERC20Metadata[]\",\"name\":\"billingTokens\",\"type\":\"address[]\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"gasFeePPB\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"flatFeeMilliCents\",\"type\":\"uint24\"},{\"internalType\":\"contractAggregatorV3Interface\",\"name\":\"priceFeed\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"decimals\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"fallbackPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint96\",\"name\":\"minSpend\",\"type\":\"uint96\"}],\"internalType\":\"structAutomationRegistryBase2_3.BillingConfig[]\",\"name\":\"billingConfigs\",\"type\":\"tuple[]\"}],\"name\":\"setConfigTypeSafe\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"rawReport\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"rs\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"ss\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"rawVs\",\"type\":\"bytes32\"}],\"name\":\"transmit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", - Bin: "0x6101806040523480156200001257600080fd5b506040516200651f3803806200651f83398101604081905262000035916200062f565b80816001600160a01b031663ca30e6036040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000075573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200009b91906200062f565b826001600160a01b031663226cf83c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620000da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200010091906200062f565b836001600160a01b031663614486af6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200013f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200016591906200062f565b846001600160a01b0316636709d0e56040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001a4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001ca91906200062f565b856001600160a01b0316635425d8ac6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000209573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200022f91906200062f565b866001600160a01b031663a08714c06040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200026e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200029491906200062f565b876001600160a01b031663c5b964e06040518163ffffffff1660e01b8152600401602060405180830381865afa158015620002d3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002f9919062000656565b886001600160a01b031663ac4dc59a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000338573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200035e91906200062f565b3380600081620003b55760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620003e857620003e8816200056b565b5050506001600160a01b0380891660805287811660a05286811660c05285811660e052848116610100528316610120526025805483919060ff19166001838181111562000439576200043962000679565b0217905550806001600160a01b0316610140816001600160a01b03168152505060c0516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200049a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620004c091906200068f565b60ff1660a0516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000504573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200052a91906200068f565b60ff16146200054c576040516301f86e1760e41b815260040160405180910390fd5b5050506001600160a01b039095166101605250620006b4945050505050565b336001600160a01b03821603620005c55760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401620003ac565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6001600160a01b03811681146200062c57600080fd5b50565b6000602082840312156200064257600080fd5b81516200064f8162000616565b9392505050565b6000602082840312156200066957600080fd5b8151600281106200064f57600080fd5b634e487b7160e01b600052602160045260246000fd5b600060208284031215620006a257600080fd5b815160ff811681146200064f57600080fd5b60805160a05160c05160e05161010051610120516101405161016051615e07620007186000396000818160b30152610186015260005050600050506000505060005050600061379a015260005050600081816113710152612dc30152615e076000f3fe6080604052600436106100b15760003560e01c80638da5cb5b11610069578063b1dc65a41161004e578063b1dc65a4146102cb578063e3d0e712146102eb578063f2fde38b1461030b576100b1565b80638da5cb5b1461025a578063afcb95d714610285576100b1565b8063349e8cca1161009a578063349e8cca1461017757806379ba5097146101cb57806381ff7048146101e0576100b1565b80630870d3a1146100f8578063181f5a7714610118575b7f00000000000000000000000000000000000000000000000000000000000000003660008037600080366000845af43d6000803e8080156100f1573d6000f35b3d6000fd5b005b34801561010457600080fd5b506100f6610113366004614b7b565b61032b565b34801561012457600080fd5b506101616040518060400160405280601881526020017f4175746f6d6174696f6e526567697374727920322e332e30000000000000000081525081565b60405161016e9190614cf7565b60405180910390f35b34801561018357600080fd5b507f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161016e565b3480156101d757600080fd5b506100f6610c0f565b3480156101ec57600080fd5b5061023760175460135463ffffffff74010000000000000000000000000000000000000000830481169378010000000000000000000000000000000000000000000000009093041691565b6040805163ffffffff94851681529390921660208401529082015260600161016e565b34801561026657600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff166101a6565b34801561029157600080fd5b50601354601454604080516000815260208101939093526c0100000000000000000000000090910463ffffffff169082015260600161016e565b3480156102d757600080fd5b506100f66102e6366004614d56565b610d11565b3480156102f757600080fd5b506100f6610306366004614e3b565b611051565b34801561031757600080fd5b506100f6610326366004614f08565b61108b565b61033361109f565b601f8851111561036f576040517f25d0209c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8560ff166000036103ac576040517fe77dba5600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b865188511415806103cb57506103c3866003614f54565b60ff16885111155b15610402576040517f1d2d1c5800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805182511461043d576040517fcf54c06a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6104478282611122565b610451888861175a565b604051806101200160405280601460000160009054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff168152602001600063ffffffff1681526020018660a0015162ffffff16815260200186610120015161ffff1681526020018760ff168152602001601460000160169054906101000a900460ff1615158152602001601460000160179054906101000a900460ff1615158152602001866080015115158152602001866101e0015173ffffffffffffffffffffffffffffffffffffffff16815250601460008201518160000160006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550602082015181600001600c6101000a81548163ffffffff021916908363ffffffff16021790555060408201518160000160106101000a81548162ffffff021916908362ffffff16021790555060608201518160000160136101000a81548161ffff021916908361ffff16021790555060808201518160000160156101000a81548160ff021916908360ff16021790555060a08201518160000160166101000a81548160ff02191690831515021790555060c08201518160000160176101000a81548160ff02191690831515021790555060e08201518160000160186101000a81548160ff0219169083151502179055506101008201518160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509050506000601660010160189054906101000a900463ffffffff1690506000866101e0015173ffffffffffffffffffffffffffffffffffffffff166357e871e76040518163ffffffff1660e01b8152600401602060405180830381865afa158015610701573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107259190614f70565b6017549091506000906107579074010000000000000000000000000000000000000000900463ffffffff166001614f89565b9050604051806101600160405280896060015173ffffffffffffffffffffffffffffffffffffffff168152602001896000015163ffffffff168152602001896020015163ffffffff1681526020016016600001601c9054906101000a900463ffffffff1663ffffffff16815260200189610100015173ffffffffffffffffffffffffffffffffffffffff1681526020018263ffffffff1681526020018363ffffffff168152602001896040015163ffffffff16815260200189610140015173ffffffffffffffffffffffffffffffffffffffff1681526020018960c0015163ffffffff1681526020018960e0015163ffffffff16815250601660008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160000160146101000a81548163ffffffff021916908363ffffffff16021790555060408201518160000160186101000a81548163ffffffff021916908363ffffffff160217905550606082015181600001601c6101000a81548163ffffffff021916908363ffffffff16021790555060808201518160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060a08201518160010160146101000a81548163ffffffff021916908363ffffffff16021790555060c08201518160010160186101000a81548163ffffffff021916908363ffffffff16021790555060e082015181600101601c6101000a81548163ffffffff021916908363ffffffff1602179055506101008201518160020160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506101208201518160020160146101000a81548163ffffffff021916908363ffffffff1602179055506101408201518160020160186101000a81548163ffffffff021916908363ffffffff160217905550905050876101600151601981905550876101800151601a81905550876101a00151601b81905550600088604051602001610a9a9190614ff7565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0018152919052601754909150610aff904690309074010000000000000000000000000000000000000000900463ffffffff168f8f8f878f8f611e19565b6013556000610b0e6009611ec3565b90505b8015610b4b57610b38610b30610b2860018461517d565b600990611ed3565b600990611ee6565b5080610b4381615190565b915050610b11565b5060005b896101c0015151811015610ba257610b8f8a6101c001518281518110610b7757610b776151c5565b60200260200101516009611f0890919063ffffffff16565b5080610b9a816151f4565b915050610b4f565b507f1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e0584601354601660010160149054906101000a900463ffffffff168f8f8f878f8f604051610bf99998979695949392919061522c565b60405180910390a1505050505050505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610c95576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b60005a90506000610d238660406152c2565b610d2f896101446152d9565b610d3991906152d9565b9050368114610d74576040517fdfe9309000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051610120810182526014546bffffffffffffffffffffffff8116825263ffffffff6c01000000000000000000000000820416602083015262ffffff7001000000000000000000000000000000008204169282019290925261ffff730100000000000000000000000000000000000000830416606082015260ff75010000000000000000000000000000000000000000008304811660808301527601000000000000000000000000000000000000000000008304811615801560a08401527701000000000000000000000000000000000000000000000084048216151560c0840152780100000000000000000000000000000000000000000000000090930416151560e082015260155473ffffffffffffffffffffffffffffffffffffffff1661010082015290610ed3576040517f24522f3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152600b602052604090205460ff16610f1c576040517f1099ed7500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6013548b3514610f58576040517fdfdcf8e700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6080810151610f689060016152ec565b60ff1687141580610f795750868514155b15610fb0576040517f0244f71a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610fc08b8b8b8b8b8b8b8b611f2a565b6000610fcc8b8b612193565b905060208c0135600881901c63ffffffff16610fe984848861224c565b836020015163ffffffff168163ffffffff16111561104157601480547fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff166c0100000000000000000000000063ffffffff8416021790555b5050505050505050505050505050565b60008060008580602001905181019061106a9190615472565b925092509250611080898989868989888861032b565b505050505050505050565b61109361109f565b61109c81612e39565b50565b60005473ffffffffffffffffffffffffffffffffffffffff163314611120576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610c8c565b565b60005b6024548110156111e0576022600060248381548110611146576111466151c5565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff168352820192909252604001812080547fffffffff00000000000000000000000000000000000000000000000000000000168155600181019190915560020180547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000169055806111d8816151f4565b915050611125565b506111ed602460006145cf565b60255460ff1660005b8351811015611754576000848281518110611213576112136151c5565b602002602001015190506000848381518110611231576112316151c5565b602002602001015190508173ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015611286573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112aa919061561d565b60ff16816060015160ff161415806113385750806040015173ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561130c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611330919061561d565b60ff16600814155b1561136f576040517fc1ab6dc100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161480156113db575060018460018111156113d9576113d961563a565b145b15611412576040517fc1ab6dc100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8216158061144d5750604081015173ffffffffffffffffffffffffffffffffffffffff16155b15611484576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff828116600090815260226020526040902054670100000000000000900416156114ee576040517f357d0cc400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6024805460018181019092557f7cd332d19b93bcabe3cce7ca0c18a052f57e5fd03b4758a09f30f5ddc4b22ec401805473ffffffffffffffffffffffffffffffffffffffff8086167fffffffffffffffffffffffff0000000000000000000000000000000000000000909216821790925560008181526022602090815260409182902086518154928801518489015160608a015160ff167b01000000000000000000000000000000000000000000000000000000027fffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffff9190981667010000000000000002167fffffffff000000000000000000000000000000000000000000ffffffffffffff62ffffff909216640100000000027fffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000090951663ffffffff9093169290921793909317929092169190911793909317835560808501519383019390935560a0840151600290920180546bffffffffffffffffffffffff9093167fffffffffffffffffffffffffffffffffffffffff0000000000000000000000009093169290921790915590517fca93cbe727c73163ec538f71be6c0a64877d7f1f6dd35d5ca7cbaef3a3e34ba390611737908490600060c08201905063ffffffff835116825262ffffff602084015116602083015273ffffffffffffffffffffffffffffffffffffffff604084015116604083015260ff6060840151166060830152608083015160808301526bffffffffffffffffffffffff60a08401511660a083015292915050565b60405180910390a25050808061174c906151f4565b9150506111f6565b50505050565b600e546014546bffffffffffffffffffffffff1660005b600e548110156117cd576117ba600e8281548110611791576117916151c5565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff168385612f2e565b50806117c5816151f4565b915050611771565b5060255460009060ff16815b600e5481101561193e57600e81815481106117f6576117f66151c5565b6000918252602082200154600d805473ffffffffffffffffffffffffffffffffffffffff9092169550600c929184908110611833576118336151c5565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff9081168452838201949094526040928301822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001690559286168152600b909252902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905560018260018111156118d5576118d561563a565b14801561191a575073ffffffffffffffffffffffffffffffffffffffff83166000908152600b60205260409020546201000090046bffffffffffffffffffffffff1615155b1561192c5761192a600f84611f08565b505b80611936816151f4565b9150506117d9565b5061194b600d60006145cf565b611957600e60006145cf565b6040805160808101825260008082526020820181905291810182905260608101829052905b8751811015611de757600c600089838151811061199b5761199b6151c5565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040016000205460ff1615611a06576040517f77cea0fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16888281518110611a3057611a306151c5565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1603611a85576040517f815e1d6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405180604001604052806001151581526020018260ff16815250600c60008a8481518110611ab657611ab66151c5565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528181019290925260400160002082518154939092015160ff16610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909316929092171790558651879082908110611b5e57611b5e6151c5565b60200260200101519350600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1603611bce576040517f58a70a0a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff84166000908152600b60209081526040918290208251608081018452905460ff80821615801584526101008304909116938301939093526bffffffffffffffffffffffff6201000082048116948301949094526e01000000000000000000000000000090049092166060830152909250611c89576040517f6a7281ad00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180835260ff80831660208086019182526014546bffffffffffffffffffffffff9081166060880190815273ffffffffffffffffffffffffffffffffffffffff8a166000908152600b909352604092839020885181549551948a0151925184166e010000000000000000000000000000027fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff939094166201000002929092167fffffffffffff000000000000000000000000000000000000000000000000ffff94909616610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000090951694909417179190911692909217919091179055836001811115611dc357611dc361563a565b03611dd557611dd3600f85611ee6565b505b80611ddf816151f4565b91505061197c565b508651611dfb90600d9060208a01906145ed565b508551611e0f90600e9060208901906145ed565b5050505050505050565b6000808a8a8a8a8a8a8a8a8a604051602001611e3d99989796959493929190615669565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179b9a5050505050505050505050565b6000611ecd825490565b92915050565b6000611edf8383613136565b9392505050565b6000611edf8373ffffffffffffffffffffffffffffffffffffffff8416613160565b6000611edf8373ffffffffffffffffffffffffffffffffffffffff841661325a565b60008787604051611f3c9291906156fe565b604051908190038120611f53918b9060200161570e565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201208383019092526000808452908301819052909250906000805b8881101561212a57600185878360208110611fbf57611fbf6151c5565b611fcc91901a601b6152ec565b8c8c85818110611fde57611fde6151c5565b905060200201358b8b86818110611ff757611ff76151c5565b9050602002013560405160008152602001604052604051612034949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa158015612056573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081015173ffffffffffffffffffffffffffffffffffffffff81166000908152600c602090815290849020838501909452925460ff8082161515808552610100909204169383019390935290955093509050612104576040517f0f4c073700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b826020015160080260ff166001901b840193508080612122906151f4565b915050611fa2565b50827e01010101010101010101010101010101010101010101010101010101010101841614612185576040517fc103be2e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050505050505050565b6121cc6040518060c001604052806000815260200160008152602001606081526020016060815260200160608152602001606081525090565b60006121da838501856157ff565b60408101515160608201515191925090811415806121fd57508082608001515114155b8061220d5750808260a001515114155b15612244576040517fb55ac75400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b509392505050565b600082604001515167ffffffffffffffff81111561226c5761226c614684565b60405190808252806020026020018201604052801561233857816020015b6040805161020081018252600060e08201818152610100830182905261012083018290526101408301829052610160830182905261018083018290526101a083018290526101c083018290526101e0830182905282526020808301829052928201819052606082018190526080820181905260a0820181905260c082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90920191018161228a5790505b50905060006040518060800160405280600061ffff16815260200160006bffffffffffffffffffffffff16815260200160006bffffffffffffffffffffffff16815260200160008152509050600085610100015173ffffffffffffffffffffffffffffffffffffffff166357e871e76040518163ffffffff1660e01b8152600401602060405180830381865afa1580156123d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123fa9190614f70565b9050600086610100015173ffffffffffffffffffffffffffffffffffffffff166318b8f6136040518163ffffffff1660e01b8152600401602060405180830381865afa15801561244e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124729190614f70565b905060005b86604001515181101561290257600460008860400151838151811061249e5761249e6151c5565b6020908102919091018101518252818101929092526040908101600020815161012081018352815460ff8082161515835261010080830490911615159583019590955263ffffffff620100008204811694830194909452660100000000000081048416606083015273ffffffffffffffffffffffffffffffffffffffff6a01000000000000000000009091048116608083015260018301546fffffffffffffffffffffffffffffffff811660a08401526bffffffffffffffffffffffff70010000000000000000000000000000000082041660c08401527c0100000000000000000000000000000000000000000000000000000000900490931660e08201526002909101549091169181019190915285518690839081106125c1576125c16151c5565b6020026020010151600001819052506125f6876040015182815181106125e9576125e96151c5565b60200260200101516132a9565b858281518110612608576126086151c5565b60200260200101516060019060018111156126255761262561563a565b908160018111156126385761263861563a565b8152505061269c87604001518281518110612655576126556151c5565b60200260200101518489608001518481518110612674576126746151c5565b602002602001015188858151811061268e5761268e6151c5565b60200260200101518c613354565b8683815181106126ae576126ae6151c5565b60200260200101516020018784815181106126cb576126cb6151c5565b602002602001015160c00182815250821515151581525050508481815181106126f6576126f66151c5565b602002602001015160200151156127265760018460000181815161271a91906158ec565b61ffff1690525061272b565b6128f0565b612791858281518110612740576127406151c5565b6020026020010151600001516080015188606001518381518110612766576127666151c5565b60200260200101518960a001518481518110612784576127846151c5565b6020026020010151613473565b8683815181106127a3576127a36151c5565b60200260200101516040018784815181106127c0576127c06151c5565b60200260200101516080018281525082151515158152505050876080015160016127ea91906152ec565b6127f89060ff1660406152c2565b6103a48860a001518381518110612811576128116151c5565b60200260200101515161282491906152d9565b61282e91906152d9565b858281518110612840576128406151c5565b602002602001015160a0018181525050848181518110612862576128626151c5565b602002602001015160a001518460600181815161287f91906152d9565b9052508451859082908110612896576128966151c5565b602002602001015160800151866128ad919061517d565b95506128f0876040015182815181106128c8576128c86151c5565b6020026020010151848784815181106128e3576128e36151c5565b602002602001015161368e565b806128fa816151f4565b915050612477565b50825161ffff166000036129195750505050505050565b61c8006129273660106152c2565b5a612932908861517d565b61293c91906152d9565b61294691906152d9565b83519095506137789061295d9061ffff1687615936565b61296791906152d9565b604080516080810182526000808252602082018190529181018290526060810182905291965061299689613793565b905060005b886040015151811015612cd2578681815181106129ba576129ba6151c5565b60200260200101516020015115612cc057801580612a525750866129df60018361517d565b815181106129ef576129ef6151c5565b602002602001015160000151610100015173ffffffffffffffffffffffffffffffffffffffff16878281518110612a2857612a286151c5565b602002602001015160000151610100015173ffffffffffffffffffffffffffffffffffffffff1614155b15612a8657612a838a888381518110612a6d57612a6d6151c5565b602002602001015160000151610100015161387d565b92505b6000612ba48b6040518061012001604052808b8681518110612aaa57612aaa6151c5565b60200260200101516080015181526020018c81526020018a606001518c8781518110612ad857612ad86151c5565b602002602001015160a001518a612aef91906152c2565b612af99190615936565b81526020018d6000015181526020018d6020015181526020018681526020018b8681518110612b2a57612b2a6151c5565b602002602001015160000151610100015173ffffffffffffffffffffffffffffffffffffffff168152602001878152602001600115158152508c604001518581518110612b7957612b796151c5565b60200260200101518b8681518110612b9357612b936151c5565b6020026020010151600001516139f9565b9050806060015187604001818151612bbc919061594a565b6bffffffffffffffffffffffff169052506040810151602088018051612be390839061594a565b6bffffffffffffffffffffffff169052508751889083908110612c0857612c086151c5565b60200260200101516040015115158a604001518381518110612c2c57612c2c6151c5565b60200260200101517fad8cc9579b21dfe2c2f6ea35ba15b656e46b4f5b0cb424f52739b8ce5cac9c5b83602001518460000151612c69919061594a565b8b8681518110612c7b57612c7b6151c5565b6020026020010151608001518d8f608001518881518110612c9e57612c9e6151c5565b6020026020010151604051612cb6949392919061596f565b60405180910390a3505b80612cca816151f4565b91505061299b565b505050602083810151336000908152600b90925260409091208054600290612d0f9084906201000090046bffffffffffffffffffffffff1661594a565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508260400151601460000160008282829054906101000a90046bffffffffffffffffffffffff16612d6d919061594a565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555082604001518360200151612daf919061594a565b6bffffffffffffffffffffffff16602160007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254612e2b91906152d9565b909155505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff821603612eb8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610c8c565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b73ffffffffffffffffffffffffffffffffffffffff83166000908152600b602090815260408083208151608081018352905460ff80821615801584526101008304909116948301949094526bffffffffffffffffffffffff6201000082048116938301939093526e010000000000000000000000000000900490911660608201529061312a576000816060015185612fc691906159ac565b90506000612fd485836159d1565b90508083604001818151612fe8919061594a565b6bffffffffffffffffffffffff1690525061300385826159fc565b83606001818151613014919061594a565b6bffffffffffffffffffffffff90811690915273ffffffffffffffffffffffffffffffffffffffff89166000908152600b602090815260409182902087518154928901519389015160608a015186166e010000000000000000000000000000027fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff919096166201000002167fffffffffffff000000000000000000000000000000000000000000000000ffff60ff95909516610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909416939093171792909216179190911790555050505b60400151949350505050565b600082600001828154811061314d5761314d6151c5565b9060005260206000200154905092915050565b6000818152600183016020526040812054801561324957600061318460018361517d565b85549091506000906131989060019061517d565b90508181146131fd5760008660000182815481106131b8576131b86151c5565b90600052602060002001549050808760000184815481106131db576131db6151c5565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061320e5761320e615a2c565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050611ecd565b6000915050611ecd565b5092915050565b60008181526001830160205260408120546132a157508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155611ecd565b506000611ecd565b6000818160045b600f811015613336577fff0000000000000000000000000000000000000000000000000000000000000082168382602081106132ee576132ee6151c5565b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161461332457506000949350505050565b8061332e816151f4565b9150506132b0565b5081600f1a600181111561334c5761334c61563a565b949350505050565b60008080808560600151600181111561336f5761336f61563a565b03613395576133818888888888613e68565b61339057600092509050613469565b61340d565b6001856060015160018111156133ad576133ad61563a565b036133db5760006133c089898988613ff2565b92509050806133d55750600092509050613469565b5061340d565b6040517ff2b2d41200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84516060015163ffffffff16871061346257877fc3237c8807c467c1b39b8d0395eff077313e691bf0a7388106792564ebfd56368760405161344f9190614cf7565b60405180910390a2600092509050613469565b6001925090505b9550959350505050565b601454600090819077010000000000000000000000000000000000000000000000900460ff16156134d0576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601480547fffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffff16770100000000000000000000000000000000000000000000001790556040517f4585e33b0000000000000000000000000000000000000000000000000000000090613545908590602401614cf7565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290517f79188d1600000000000000000000000000000000000000000000000000000000815290935073ffffffffffffffffffffffffffffffffffffffff8616906379188d16906136189087908790600401615a5b565b60408051808303816000875af1158015613636573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061365a9190615a74565b601480547fffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffff16905590969095509350505050565b6000816060015160018111156136a6576136a661563a565b0361370a57600083815260046020526040902060010180547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167c010000000000000000000000000000000000000000000000000000000063ffffffff851602179055505050565b6001816060015160018111156137225761372261563a565b0361378e5760c08101805160009081526008602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055915191517fa4a4e334c0e330143f9437484fe516c13bc560b86b5b0daf58e7084aaac228f29190a25b505050565b60008060007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015613803573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138279190615abc565b5093505092505060008213158061383d57508042105b8061386d57506000846040015162ffffff1611801561386d5750613861814261517d565b846040015162ffffff16105b15613253575050601b5492915050565b60408051608081018252600080825260208083018281528385018381526060850184905273ffffffffffffffffffffffffffffffffffffffff878116855260229093528584208054640100000000810462ffffff1690925263ffffffff82169092527b01000000000000000000000000000000000000000000000000000000810460ff16855285517ffeaf968c00000000000000000000000000000000000000000000000000000000815295519495919484936701000000000000009092049091169163feaf968c9160048083019260a09291908290030181865afa15801561396a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061398e9190615abc565b509350509250506000821315806139a457508042105b806139d457506000866040015162ffffff161180156139d457506139c8814261517d565b866040015162ffffff16105b156139e857600183015460608501526139f0565b606084018290525b50505092915050565b604080516101008101825260008082526020808301829052928201819052606082018190526080820181905260a0820181905260c0820181905260e08201529082015115613a8f5760008381526023602090815260409182902082518084018452905463ffffffff811680835262ffffff640100000000909204821692840192835260e089018051909401529051915191169101525b6000613a9b86866141ff565b60c0840151602082015182519293509091600091613ab89161594a565b60e08801515190915060ff16600060128210613ad5576001613aeb565b613ae082601261517d565b613aeb90600a615c2c565b9050600060128311613afe576001613b14565b613b0960128461517d565b613b1490600a615c2c565b905085600001516bffffffffffffffffffffffff16856bffffffffffffffffffffffff161015613bbc57849350613b90818b60800151613b5491906152c2565b838c60e0015160600151886bffffffffffffffffffffffff16613b7791906152c2565b613b8191906152c2565b613b8b9190615936565b61452d565b6bffffffffffffffffffffffff9081166040880152600060608801819052602088015285168652613ce2565b836bffffffffffffffffffffffff16856bffffffffffffffffffffffff161015613ce257849350613c4a86604001516bffffffffffffffffffffffff16828c60800151613c0991906152c2565b848d60e0015160600151896bffffffffffffffffffffffff16613c2c91906152c2565b613c3691906152c2565b613c409190615936565b613b8b919061517d565b6bffffffffffffffffffffffff1660608088019190915260e08b01510151613cce90613c779084906152c2565b6001848d60e0015160600151613c8d91906152c2565b613c97919061517d565b838d608001518a606001516bffffffffffffffffffffffff16613cba91906152c2565b613cc491906152c2565b613b8191906152d9565b6bffffffffffffffffffffffff1660208701525b60008981526004602052604090206001018054859190601090613d2890849070010000000000000000000000000000000090046bffffffffffffffffffffffff166159ac565b82546101009290920a6bffffffffffffffffffffffff81810219909316918316021790915560008b81526004602052604081206001018054928816935091613d839084906fffffffffffffffffffffffffffffffff16615c38565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff160217905550836bffffffffffffffffffffffff16602160008c60c0015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254613e1a919061517d565b92505081905550887f801ba6ed51146ffe3e99d1dbd9dd0f4de6292e78a9a34c39c0183de17b3f40fc87604051613e519190615c61565b60405180910390a250939998505050505050505050565b60008084806020019051810190613e7f9190615d21565b845160e00151815191925063ffffffff90811691161015613edc57867f405288ea7be309e16cfdf481367f90a413e1d4634fcdaf8966546db9b93012e886604051613eca9190614cf7565b60405180910390a26000915050613fe9565b8260e001518015613f9c5750602081015115801590613f9c5750602081015161010084015182516040517f85df51fd00000000000000000000000000000000000000000000000000000000815263ffffffff909116600482015273ffffffffffffffffffffffffffffffffffffffff909116906385df51fd90602401602060405180830381865afa158015613f75573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f999190614f70565b14155b80613fae5750805163ffffffff168611155b15613fe357867f6aa7f60c176da7af894b384daea2249497448137f5943c1237ada8bc92bdc30186604051613eca9190614cf7565b60019150505b95945050505050565b60008060008480602001905181019061400b9190615d79565b905060008782600001518360200151846040015160405160200161406d94939291909384526020840192909252604083015260e01b7fffffffff0000000000000000000000000000000000000000000000000000000016606082015260640190565b6040516020818303038152906040528051906020012090508460e00151801561414857506080820151158015906141485750608082015161010086015160608401516040517f85df51fd00000000000000000000000000000000000000000000000000000000815263ffffffff909116600482015273ffffffffffffffffffffffffffffffffffffffff909116906385df51fd90602401602060405180830381865afa158015614121573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141459190614f70565b14155b8061415d575086826060015163ffffffff1610155b156141a757877f6aa7f60c176da7af894b384daea2249497448137f5943c1237ada8bc92bdc301876040516141929190614cf7565b60405180910390a26000935091506141f69050565b60008181526008602052604090205460ff16156141ee57877f405288ea7be309e16cfdf481367f90a413e1d4634fcdaf8966546db9b93012e8876040516141929190614cf7565b600193509150505b94509492505050565b6040805161010081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081019190915260008260e001516000015160ff1690506000846060015161ffff16846060015161426a91906152c2565b9050836101000151801561427d5750803a105b1561428557503a5b6000601283116142965760016142ac565b6142a160128461517d565b6142ac90600a615c2c565b90506000601284106142bf5760016142d5565b6142ca84601261517d565b6142d590600a615c2c565b905060008660a001518760400151886020015189600001516142f791906152d9565b61430190876152c2565b61430b91906152d9565b61431591906152c2565b9050614358828860e001516060015161432e91906152c2565b6001848a60e001516060015161434491906152c2565b61434e919061517d565b613cc486856152c2565b6bffffffffffffffffffffffff168652608087015161437b90613b8b9083615936565b6bffffffffffffffffffffffff1660408088019190915260e088015101516000906143b49062ffffff16683635c9adc5dea000006152c2565b9050600081633b9aca008a60a001518b60e001516020015163ffffffff168c604001518d600001518b6143e791906152c2565b6143f191906152d9565b6143fb91906152c2565b61440591906152c2565b61440f9190615936565b61441991906152d9565b905061445c848a60e001516060015161443291906152c2565b6001868c60e001516060015161444891906152c2565b614452919061517d565b613cc488856152c2565b6bffffffffffffffffffffffff166020890152608089015161448290613b8b9083615936565b6bffffffffffffffffffffffff16606089015260c089015173ffffffffffffffffffffffffffffffffffffffff166080808a01919091528901516144c59061452d565b6bffffffffffffffffffffffff1660a0808a01919091528901516144e89061452d565b6bffffffffffffffffffffffff1660c089015260e08901516060015161450d9061452d565b6bffffffffffffffffffffffff1660e08901525050505050505092915050565b60006bffffffffffffffffffffffff8211156145cb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201527f36206269747300000000000000000000000000000000000000000000000000006064820152608401610c8c565b5090565b508054600082559060005260206000209081019061109c919061466f565b828054828255906000526020600020908101928215614667579160200282015b8281111561466757825182547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90911617825560209092019160019091019061460d565b506145cb9291505b5b808211156145cb5760008155600101614670565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610200810167ffffffffffffffff811182821017156146d7576146d7614684565b60405290565b60405160c0810167ffffffffffffffff811182821017156146d7576146d7614684565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561474757614747614684565b604052919050565b600067ffffffffffffffff82111561476957614769614684565b5060051b60200190565b73ffffffffffffffffffffffffffffffffffffffff8116811461109c57600080fd5b80356147a081614773565b919050565b600082601f8301126147b657600080fd5b813560206147cb6147c68361474f565b614700565b82815260059290921b840181019181810190868411156147ea57600080fd5b8286015b8481101561480e57803561480181614773565b83529183019183016147ee565b509695505050505050565b60ff8116811461109c57600080fd5b80356147a081614819565b63ffffffff8116811461109c57600080fd5b80356147a081614833565b801515811461109c57600080fd5b80356147a081614850565b62ffffff8116811461109c57600080fd5b80356147a081614869565b61ffff8116811461109c57600080fd5b80356147a081614885565b600061020082840312156148b357600080fd5b6148bb6146b3565b90506148c682614845565b81526148d460208301614845565b60208201526148e560408301614845565b60408201526148f660608301614795565b60608201526149076080830161485e565b608082015261491860a0830161487a565b60a082015261492960c08301614845565b60c082015261493a60e08301614845565b60e082015261010061494d818401614795565b9082015261012061495f838201614895565b90820152610140614971838201614795565b90820152610160828101359082015261018080830135908201526101a080830135908201526101c08083013567ffffffffffffffff8111156149b257600080fd5b6149be858286016147a5565b8284015250506101e06149d2818401614795565b9082015292915050565b803567ffffffffffffffff811681146147a057600080fd5b600082601f830112614a0557600080fd5b813567ffffffffffffffff811115614a1f57614a1f614684565b614a5060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601614700565b818152846020838601011115614a6557600080fd5b816020850160208301376000918101602001919091529392505050565b6bffffffffffffffffffffffff8116811461109c57600080fd5b600082601f830112614aad57600080fd5b81356020614abd6147c68361474f565b82815260c09283028501820192828201919087851115614adc57600080fd5b8387015b85811015614b6e5781818a031215614af85760008081fd5b614b006146dd565b8135614b0b81614833565b815281860135614b1a81614869565b81870152604082810135614b2d81614773565b90820152606082810135614b4081614819565b908201526080828101359082015260a080830135614b5d81614a82565b908201528452928401928101614ae0565b5090979650505050505050565b600080600080600080600080610100898b031215614b9857600080fd5b883567ffffffffffffffff80821115614bb057600080fd5b614bbc8c838d016147a5565b995060208b0135915080821115614bd257600080fd5b614bde8c838d016147a5565b9850614bec60408c01614828565b975060608b0135915080821115614c0257600080fd5b614c0e8c838d016148a0565b9650614c1c60808c016149dc565b955060a08b0135915080821115614c3257600080fd5b614c3e8c838d016149f4565b945060c08b0135915080821115614c5457600080fd5b614c608c838d016147a5565b935060e08b0135915080821115614c7657600080fd5b50614c838b828c01614a9c565b9150509295985092959890939650565b6000815180845260005b81811015614cb957602081850181015186830182015201614c9d565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081526000611edf6020830184614c93565b60008083601f840112614d1c57600080fd5b50813567ffffffffffffffff811115614d3457600080fd5b6020830191508360208260051b8501011115614d4f57600080fd5b9250929050565b60008060008060008060008060e0898b031215614d7257600080fd5b606089018a811115614d8357600080fd5b8998503567ffffffffffffffff80821115614d9d57600080fd5b818b0191508b601f830112614db157600080fd5b813581811115614dc057600080fd5b8c6020828501011115614dd257600080fd5b6020830199508098505060808b0135915080821115614df057600080fd5b614dfc8c838d01614d0a565b909750955060a08b0135915080821115614e1557600080fd5b50614e228b828c01614d0a565b999c989b50969995989497949560c00135949350505050565b60008060008060008060c08789031215614e5457600080fd5b863567ffffffffffffffff80821115614e6c57600080fd5b614e788a838b016147a5565b97506020890135915080821115614e8e57600080fd5b614e9a8a838b016147a5565b9650614ea860408a01614828565b95506060890135915080821115614ebe57600080fd5b614eca8a838b016149f4565b9450614ed860808a016149dc565b935060a0890135915080821115614eee57600080fd5b50614efb89828a016149f4565b9150509295509295509295565b600060208284031215614f1a57600080fd5b8135611edf81614773565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60ff818116838216029081169081811461325357613253614f25565b600060208284031215614f8257600080fd5b5051919050565b63ffffffff81811683821601908082111561325357613253614f25565b600081518084526020808501945080840160005b83811015614fec57815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101614fba565b509495945050505050565b6020815261500e60208201835163ffffffff169052565b60006020830151615027604084018263ffffffff169052565b50604083015163ffffffff8116606084015250606083015173ffffffffffffffffffffffffffffffffffffffff8116608084015250608083015180151560a08401525060a083015162ffffff811660c08401525060c083015163ffffffff811660e08401525060e08301516101006150a68185018363ffffffff169052565b84015190506101206150cf8482018373ffffffffffffffffffffffffffffffffffffffff169052565b84015190506101406150e68482018361ffff169052565b840151905061016061510f8482018373ffffffffffffffffffffffffffffffffffffffff169052565b840151610180848101919091528401516101a0808501919091528401516101c0808501919091528401516102006101e080860182905291925090615157610220860184614fa6565b95015173ffffffffffffffffffffffffffffffffffffffff169301929092525090919050565b81810381811115611ecd57611ecd614f25565b60008161519f5761519f614f25565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361522557615225614f25565b5060010190565b600061012063ffffffff808d1684528b6020850152808b1660408501525080606084015261525c8184018a614fa6565b905082810360808401526152708189614fa6565b905060ff871660a084015282810360c084015261528d8187614c93565b905067ffffffffffffffff851660e08401528281036101008401526152b28185614c93565b9c9b505050505050505050505050565b8082028115828204841417611ecd57611ecd614f25565b80820180821115611ecd57611ecd614f25565b60ff8181168382160190811115611ecd57611ecd614f25565b80516147a081614833565b80516147a081614773565b80516147a081614850565b80516147a081614869565b80516147a081614885565b600082601f83011261534d57600080fd5b8151602061535d6147c68361474f565b82815260059290921b8401810191818101908684111561537c57600080fd5b8286015b8481101561480e57805161539381614773565b8352918301918301615380565b600082601f8301126153b157600080fd5b815160206153c16147c68361474f565b82815260c092830285018201928282019190878511156153e057600080fd5b8387015b85811015614b6e5781818a0312156153fc5760008081fd5b6154046146dd565b815161540f81614833565b81528186015161541e81614869565b8187015260408281015161543181614773565b9082015260608281015161544481614819565b908201526080828101519082015260a08083015161546181614a82565b9082015284529284019281016153e4565b60008060006060848603121561548757600080fd5b835167ffffffffffffffff8082111561549f57600080fd5b9085019061020082880312156154b457600080fd5b6154bc6146b3565b6154c583615305565b81526154d360208401615305565b60208201526154e460408401615305565b60408201526154f560608401615310565b60608201526155066080840161531b565b608082015261551760a08401615326565b60a082015261552860c08401615305565b60c082015261553960e08401615305565b60e082015261010061554c818501615310565b9082015261012061555e848201615331565b90820152610140615570848201615310565b90820152610160838101519082015261018080840151908201526101a080840151908201526101c080840151838111156155a957600080fd5b6155b58a82870161533c565b8284015250506101e06155c9818501615310565b9082015260208701519095509150808211156155e457600080fd5b6155f08783880161533c565b9350604086015191508082111561560657600080fd5b50615613868287016153a0565b9150509250925092565b60006020828403121561562f57600080fd5b8151611edf81614819565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60006101208b835273ffffffffffffffffffffffffffffffffffffffff8b16602084015267ffffffffffffffff808b1660408501528160608501526156b08285018b614fa6565b915083820360808501526156c4828a614fa6565b915060ff881660a085015283820360c08501526156e18288614c93565b90861660e085015283810361010085015290506152b28185614c93565b8183823760009101908152919050565b8281526080810160608360208401379392505050565b600082601f83011261573557600080fd5b813560206157456147c68361474f565b82815260059290921b8401810191818101908684111561576457600080fd5b8286015b8481101561480e5780358352918301918301615768565b600082601f83011261579057600080fd5b813560206157a06147c68361474f565b82815260059290921b840181019181810190868411156157bf57600080fd5b8286015b8481101561480e57803567ffffffffffffffff8111156157e35760008081fd5b6157f18986838b01016149f4565b8452509183019183016157c3565b60006020828403121561581157600080fd5b813567ffffffffffffffff8082111561582957600080fd5b9083019060c0828603121561583d57600080fd5b6158456146dd565b823581526020830135602082015260408301358281111561586557600080fd5b61587187828601615724565b60408301525060608301358281111561588957600080fd5b61589587828601615724565b6060830152506080830135828111156158ad57600080fd5b6158b98782860161577f565b60808301525060a0830135828111156158d157600080fd5b6158dd8782860161577f565b60a08301525095945050505050565b61ffff81811683821601908082111561325357613253614f25565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60008261594557615945615907565b500490565b6bffffffffffffffffffffffff81811683821601908082111561325357613253614f25565b6bffffffffffffffffffffffff851681528360208201528260408201526080606082015260006159a26080830184614c93565b9695505050505050565b6bffffffffffffffffffffffff82811682821603908082111561325357613253614f25565b60006bffffffffffffffffffffffff808416806159f0576159f0615907565b92169190910492915050565b6bffffffffffffffffffffffff818116838216028082169190828114615a2457615a24614f25565b505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b82815260406020820152600061334c6040830184614c93565b60008060408385031215615a8757600080fd5b8251615a9281614850565b6020939093015192949293505050565b805169ffffffffffffffffffff811681146147a057600080fd5b600080600080600060a08688031215615ad457600080fd5b615add86615aa2565b9450602086015193506040860151925060608601519150615b0060808701615aa2565b90509295509295909350565b600181815b80851115615b6557817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115615b4b57615b4b614f25565b80851615615b5857918102915b93841c9390800290615b11565b509250929050565b600082615b7c57506001611ecd565b81615b8957506000611ecd565b8160018114615b9f5760028114615ba957615bc5565b6001915050611ecd565b60ff841115615bba57615bba614f25565b50506001821b611ecd565b5060208310610133831016604e8410600b8410161715615be8575081810a611ecd565b615bf28383615b0c565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115615c2457615c24614f25565b029392505050565b6000611edf8383615b6d565b6fffffffffffffffffffffffffffffffff81811683821601908082111561325357613253614f25565b6000610100820190506bffffffffffffffffffffffff8084511683528060208501511660208401528060408501511660408401528060608501511660608401525073ffffffffffffffffffffffffffffffffffffffff608084015116608083015260a0830151615ce160a08401826bffffffffffffffffffffffff169052565b5060c0830151615d0160c08401826bffffffffffffffffffffffff169052565b5060e083015161325360e08401826bffffffffffffffffffffffff169052565b600060408284031215615d3357600080fd5b6040516040810181811067ffffffffffffffff82111715615d5657615d56614684565b6040528251615d6481614833565b81526020928301519281019290925250919050565b600060a08284031215615d8b57600080fd5b60405160a0810181811067ffffffffffffffff82111715615dae57615dae614684565b806040525082518152602083015160208201526040830151615dcf81614833565b60408201526060830151615de281614833565b6060820152608092830151928101929092525091905056fea164736f6c6343000813000a", + Bin: "0x6101806040523480156200001257600080fd5b50604051620065423803806200654283398101604081905262000035916200062f565b80816001600160a01b031663ca30e6036040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000075573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200009b91906200062f565b826001600160a01b031663226cf83c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620000da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200010091906200062f565b836001600160a01b031663614486af6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200013f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200016591906200062f565b846001600160a01b0316636709d0e56040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001a4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001ca91906200062f565b856001600160a01b0316635425d8ac6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000209573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200022f91906200062f565b866001600160a01b031663a08714c06040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200026e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200029491906200062f565b876001600160a01b031663c5b964e06040518163ffffffff1660e01b8152600401602060405180830381865afa158015620002d3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002f9919062000656565b886001600160a01b031663ac4dc59a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000338573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200035e91906200062f565b3380600081620003b55760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620003e857620003e8816200056b565b5050506001600160a01b0380891660805287811660a05286811660c05285811660e052848116610100528316610120526025805483919060ff19166001838181111562000439576200043962000679565b0217905550806001600160a01b0316610140816001600160a01b03168152505060c0516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200049a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620004c091906200068f565b60ff1660a0516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000504573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200052a91906200068f565b60ff16146200054c576040516301f86e1760e41b815260040160405180910390fd5b5050506001600160a01b039095166101605250620006b4945050505050565b336001600160a01b03821603620005c55760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401620003ac565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6001600160a01b03811681146200062c57600080fd5b50565b6000602082840312156200064257600080fd5b81516200064f8162000616565b9392505050565b6000602082840312156200066957600080fd5b8151600281106200064f57600080fd5b634e487b7160e01b600052602160045260246000fd5b600060208284031215620006a257600080fd5b815160ff811681146200064f57600080fd5b60805160a05160c05160e05161010051610120516101405161016051615e2a620007186000396000818160b3015261018601526000505060005050600050506000505060006137bd015260005050600081816113710152612de60152615e2a6000f3fe6080604052600436106100b15760003560e01c80638da5cb5b11610069578063b1dc65a41161004e578063b1dc65a4146102cb578063e3d0e712146102eb578063f2fde38b1461030b576100b1565b80638da5cb5b1461025a578063afcb95d714610285576100b1565b8063349e8cca1161009a578063349e8cca1461017757806379ba5097146101cb57806381ff7048146101e0576100b1565b80630870d3a1146100f8578063181f5a7714610118575b7f00000000000000000000000000000000000000000000000000000000000000003660008037600080366000845af43d6000803e8080156100f1573d6000f35b3d6000fd5b005b34801561010457600080fd5b506100f6610113366004614b9e565b61032b565b34801561012457600080fd5b506101616040518060400160405280601881526020017f4175746f6d6174696f6e526567697374727920322e332e30000000000000000081525081565b60405161016e9190614d1a565b60405180910390f35b34801561018357600080fd5b507f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161016e565b3480156101d757600080fd5b506100f6610c0f565b3480156101ec57600080fd5b5061023760175460135463ffffffff74010000000000000000000000000000000000000000830481169378010000000000000000000000000000000000000000000000009093041691565b6040805163ffffffff94851681529390921660208401529082015260600161016e565b34801561026657600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff166101a6565b34801561029157600080fd5b50601354601454604080516000815260208101939093526c0100000000000000000000000090910463ffffffff169082015260600161016e565b3480156102d757600080fd5b506100f66102e6366004614d79565b610d11565b3480156102f757600080fd5b506100f6610306366004614e5e565b611051565b34801561031757600080fd5b506100f6610326366004614f2b565b61108b565b61033361109f565b601f8851111561036f576040517f25d0209c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8560ff166000036103ac576040517fe77dba5600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b865188511415806103cb57506103c3866003614f77565b60ff16885111155b15610402576040517f1d2d1c5800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805182511461043d576040517fcf54c06a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6104478282611122565b610451888861175a565b604051806101200160405280601460000160009054906101000a90046bffffffffffffffffffffffff166bffffffffffffffffffffffff168152602001600063ffffffff1681526020018660a0015162ffffff16815260200186610120015161ffff1681526020018760ff168152602001601460000160169054906101000a900460ff1615158152602001601460000160179054906101000a900460ff1615158152602001866080015115158152602001866101e0015173ffffffffffffffffffffffffffffffffffffffff16815250601460008201518160000160006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550602082015181600001600c6101000a81548163ffffffff021916908363ffffffff16021790555060408201518160000160106101000a81548162ffffff021916908362ffffff16021790555060608201518160000160136101000a81548161ffff021916908361ffff16021790555060808201518160000160156101000a81548160ff021916908360ff16021790555060a08201518160000160166101000a81548160ff02191690831515021790555060c08201518160000160176101000a81548160ff02191690831515021790555060e08201518160000160186101000a81548160ff0219169083151502179055506101008201518160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509050506000601660010160189054906101000a900463ffffffff1690506000866101e0015173ffffffffffffffffffffffffffffffffffffffff166357e871e76040518163ffffffff1660e01b8152600401602060405180830381865afa158015610701573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107259190614f93565b6017549091506000906107579074010000000000000000000000000000000000000000900463ffffffff166001614fac565b9050604051806101600160405280896060015173ffffffffffffffffffffffffffffffffffffffff168152602001896000015163ffffffff168152602001896020015163ffffffff1681526020016016600001601c9054906101000a900463ffffffff1663ffffffff16815260200189610100015173ffffffffffffffffffffffffffffffffffffffff1681526020018263ffffffff1681526020018363ffffffff168152602001896040015163ffffffff16815260200189610140015173ffffffffffffffffffffffffffffffffffffffff1681526020018960c0015163ffffffff1681526020018960e0015163ffffffff16815250601660008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160000160146101000a81548163ffffffff021916908363ffffffff16021790555060408201518160000160186101000a81548163ffffffff021916908363ffffffff160217905550606082015181600001601c6101000a81548163ffffffff021916908363ffffffff16021790555060808201518160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060a08201518160010160146101000a81548163ffffffff021916908363ffffffff16021790555060c08201518160010160186101000a81548163ffffffff021916908363ffffffff16021790555060e082015181600101601c6101000a81548163ffffffff021916908363ffffffff1602179055506101008201518160020160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506101208201518160020160146101000a81548163ffffffff021916908363ffffffff1602179055506101408201518160020160186101000a81548163ffffffff021916908363ffffffff160217905550905050876101600151601981905550876101800151601a81905550876101a00151601b81905550600088604051602001610a9a919061501a565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0018152919052601754909150610aff904690309074010000000000000000000000000000000000000000900463ffffffff168f8f8f878f8f611e19565b6013556000610b0e6009611ec3565b90505b8015610b4b57610b38610b30610b286001846151a0565b600990611ed3565b600990611ee6565b5080610b43816151b3565b915050610b11565b5060005b896101c0015151811015610ba257610b8f8a6101c001518281518110610b7757610b776151e8565b60200260200101516009611f0890919063ffffffff16565b5080610b9a81615217565b915050610b4f565b507f1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e0584601354601660010160149054906101000a900463ffffffff168f8f8f878f8f604051610bf99998979695949392919061524f565b60405180910390a1505050505050505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610c95576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b60005a90506000610d238660406152e5565b610d2f896101446152fc565b610d3991906152fc565b9050368114610d74576040517fdfe9309000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051610120810182526014546bffffffffffffffffffffffff8116825263ffffffff6c01000000000000000000000000820416602083015262ffffff7001000000000000000000000000000000008204169282019290925261ffff730100000000000000000000000000000000000000830416606082015260ff75010000000000000000000000000000000000000000008304811660808301527601000000000000000000000000000000000000000000008304811615801560a08401527701000000000000000000000000000000000000000000000084048216151560c0840152780100000000000000000000000000000000000000000000000090930416151560e082015260155473ffffffffffffffffffffffffffffffffffffffff1661010082015290610ed3576040517f24522f3400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152600b602052604090205460ff16610f1c576040517f1099ed7500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6013548b3514610f58576040517fdfdcf8e700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6080810151610f6890600161530f565b60ff1687141580610f795750868514155b15610fb0576040517f0244f71a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610fc08b8b8b8b8b8b8b8b611f2a565b6000610fcc8b8b612193565b905060208c0135600881901c63ffffffff16610fe984848861224c565b836020015163ffffffff168163ffffffff16111561104157601480547fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff166c0100000000000000000000000063ffffffff8416021790555b5050505050505050505050505050565b60008060008580602001905181019061106a9190615495565b925092509250611080898989868989888861032b565b505050505050505050565b61109361109f565b61109c81612e5c565b50565b60005473ffffffffffffffffffffffffffffffffffffffff163314611120576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610c8c565b565b60005b6024548110156111e0576022600060248381548110611146576111466151e8565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff168352820192909252604001812080547fffffffff00000000000000000000000000000000000000000000000000000000168155600181019190915560020180547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000169055806111d881615217565b915050611125565b506111ed602460006145f2565b60255460ff1660005b8351811015611754576000848281518110611213576112136151e8565b602002602001015190506000848381518110611231576112316151e8565b602002602001015190508173ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015611286573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112aa9190615640565b60ff16816060015160ff161415806113385750806040015173ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561130c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113309190615640565b60ff16600814155b1561136f576040517fc1ab6dc100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161480156113db575060018460018111156113d9576113d961565d565b145b15611412576040517fc1ab6dc100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8216158061144d5750604081015173ffffffffffffffffffffffffffffffffffffffff16155b15611484576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff828116600090815260226020526040902054670100000000000000900416156114ee576040517f357d0cc400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6024805460018181019092557f7cd332d19b93bcabe3cce7ca0c18a052f57e5fd03b4758a09f30f5ddc4b22ec401805473ffffffffffffffffffffffffffffffffffffffff8086167fffffffffffffffffffffffff0000000000000000000000000000000000000000909216821790925560008181526022602090815260409182902086518154928801518489015160608a015160ff167b01000000000000000000000000000000000000000000000000000000027fffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffff9190981667010000000000000002167fffffffff000000000000000000000000000000000000000000ffffffffffffff62ffffff909216640100000000027fffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000090951663ffffffff9093169290921793909317929092169190911793909317835560808501519383019390935560a0840151600290920180546bffffffffffffffffffffffff9093167fffffffffffffffffffffffffffffffffffffffff0000000000000000000000009093169290921790915590517fca93cbe727c73163ec538f71be6c0a64877d7f1f6dd35d5ca7cbaef3a3e34ba390611737908490600060c08201905063ffffffff835116825262ffffff602084015116602083015273ffffffffffffffffffffffffffffffffffffffff604084015116604083015260ff6060840151166060830152608083015160808301526bffffffffffffffffffffffff60a08401511660a083015292915050565b60405180910390a25050808061174c90615217565b9150506111f6565b50505050565b600e546014546bffffffffffffffffffffffff1660005b600e548110156117cd576117ba600e8281548110611791576117916151e8565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff168385612f51565b50806117c581615217565b915050611771565b5060255460009060ff16815b600e5481101561193e57600e81815481106117f6576117f66151e8565b6000918252602082200154600d805473ffffffffffffffffffffffffffffffffffffffff9092169550600c929184908110611833576118336151e8565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff9081168452838201949094526040928301822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001690559286168152600b909252902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905560018260018111156118d5576118d561565d565b14801561191a575073ffffffffffffffffffffffffffffffffffffffff83166000908152600b60205260409020546201000090046bffffffffffffffffffffffff1615155b1561192c5761192a600f84611f08565b505b8061193681615217565b9150506117d9565b5061194b600d60006145f2565b611957600e60006145f2565b6040805160808101825260008082526020820181905291810182905260608101829052905b8751811015611de757600c600089838151811061199b5761199b6151e8565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040016000205460ff1615611a06576040517f77cea0fa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff16888281518110611a3057611a306151e8565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1603611a85576040517f815e1d6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405180604001604052806001151581526020018260ff16815250600c60008a8481518110611ab657611ab66151e8565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff1682528181019290925260400160002082518154939092015160ff16610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909316929092171790558651879082908110611b5e57611b5e6151e8565b60200260200101519350600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1603611bce576040517f58a70a0a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff84166000908152600b60209081526040918290208251608081018452905460ff80821615801584526101008304909116938301939093526bffffffffffffffffffffffff6201000082048116948301949094526e01000000000000000000000000000090049092166060830152909250611c89576040517f6a7281ad00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180835260ff80831660208086019182526014546bffffffffffffffffffffffff9081166060880190815273ffffffffffffffffffffffffffffffffffffffff8a166000908152600b909352604092839020885181549551948a0151925184166e010000000000000000000000000000027fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff939094166201000002929092167fffffffffffff000000000000000000000000000000000000000000000000ffff94909616610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000090951694909417179190911692909217919091179055836001811115611dc357611dc361565d565b03611dd557611dd3600f85611ee6565b505b80611ddf81615217565b91505061197c565b508651611dfb90600d9060208a0190614610565b508551611e0f90600e906020890190614610565b5050505050505050565b6000808a8a8a8a8a8a8a8a8a604051602001611e3d9998979695949392919061568c565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179b9a5050505050505050505050565b6000611ecd825490565b92915050565b6000611edf8383613159565b9392505050565b6000611edf8373ffffffffffffffffffffffffffffffffffffffff8416613183565b6000611edf8373ffffffffffffffffffffffffffffffffffffffff841661327d565b60008787604051611f3c929190615721565b604051908190038120611f53918b90602001615731565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201208383019092526000808452908301819052909250906000805b8881101561212a57600185878360208110611fbf57611fbf6151e8565b611fcc91901a601b61530f565b8c8c85818110611fde57611fde6151e8565b905060200201358b8b86818110611ff757611ff76151e8565b9050602002013560405160008152602001604052604051612034949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa158015612056573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081015173ffffffffffffffffffffffffffffffffffffffff81166000908152600c602090815290849020838501909452925460ff8082161515808552610100909204169383019390935290955093509050612104576040517f0f4c073700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b826020015160080260ff166001901b84019350808061212290615217565b915050611fa2565b50827e01010101010101010101010101010101010101010101010101010101010101841614612185576040517fc103be2e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050505050505050565b6121cc6040518060c001604052806000815260200160008152602001606081526020016060815260200160608152602001606081525090565b60006121da83850185615822565b60408101515160608201515191925090811415806121fd57508082608001515114155b8061220d5750808260a001515114155b15612244576040517fb55ac75400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b509392505050565b600082604001515167ffffffffffffffff81111561226c5761226c6146a7565b60405190808252806020026020018201604052801561233857816020015b6040805161020081018252600060e08201818152610100830182905261012083018290526101408301829052610160830182905261018083018290526101a083018290526101c083018290526101e0830182905282526020808301829052928201819052606082018190526080820181905260a0820181905260c082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90920191018161228a5790505b50905060006040518060800160405280600061ffff16815260200160006bffffffffffffffffffffffff16815260200160006bffffffffffffffffffffffff16815260200160008152509050600085610100015173ffffffffffffffffffffffffffffffffffffffff166357e871e76040518163ffffffff1660e01b8152600401602060405180830381865afa1580156123d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123fa9190614f93565b6101008701516040517f7810d12a00000000000000000000000000000000000000000000000000000000815236600482015291925060009173ffffffffffffffffffffffffffffffffffffffff90911690637810d12a90602401602060405180830381865afa158015612471573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124959190614f93565b905060005b8660400151518110156129255760046000886040015183815181106124c1576124c16151e8565b6020908102919091018101518252818101929092526040908101600020815161012081018352815460ff8082161515835261010080830490911615159583019590955263ffffffff620100008204811694830194909452660100000000000081048416606083015273ffffffffffffffffffffffffffffffffffffffff6a01000000000000000000009091048116608083015260018301546fffffffffffffffffffffffffffffffff811660a08401526bffffffffffffffffffffffff70010000000000000000000000000000000082041660c08401527c0100000000000000000000000000000000000000000000000000000000900490931660e08201526002909101549091169181019190915285518690839081106125e4576125e46151e8565b6020026020010151600001819052506126198760400151828151811061260c5761260c6151e8565b60200260200101516132cc565b85828151811061262b5761262b6151e8565b60200260200101516060019060018111156126485761264861565d565b9081600181111561265b5761265b61565d565b815250506126bf87604001518281518110612678576126786151e8565b60200260200101518489608001518481518110612697576126976151e8565b60200260200101518885815181106126b1576126b16151e8565b60200260200101518c613377565b8683815181106126d1576126d16151e8565b60200260200101516020018784815181106126ee576126ee6151e8565b602002602001015160c0018281525082151515158152505050848181518110612719576127196151e8565b602002602001015160200151156127495760018460000181815161273d919061590f565b61ffff1690525061274e565b612913565b6127b4858281518110612763576127636151e8565b6020026020010151600001516080015188606001518381518110612789576127896151e8565b60200260200101518960a0015184815181106127a7576127a76151e8565b6020026020010151613496565b8683815181106127c6576127c66151e8565b60200260200101516040018784815181106127e3576127e36151e8565b602002602001015160800182815250821515151581525050508760800151600161280d919061530f565b61281b9060ff1660406152e5565b6103a48860a001518381518110612834576128346151e8565b60200260200101515161284791906152fc565b61285191906152fc565b858281518110612863576128636151e8565b602002602001015160a0018181525050848181518110612885576128856151e8565b602002602001015160a00151846060018181516128a291906152fc565b90525084518590829081106128b9576128b96151e8565b602002602001015160800151866128d091906151a0565b9550612913876040015182815181106128eb576128eb6151e8565b602002602001015184878481518110612906576129066151e8565b60200260200101516136b1565b8061291d81615217565b91505061249a565b50825161ffff1660000361293c5750505050505050565b61c80061294a3660106152e5565b5a61295590886151a0565b61295f91906152fc565b61296991906152fc565b8351909550613778906129809061ffff1687615959565b61298a91906152fc565b60408051608081018252600080825260208201819052918101829052606081018290529196506129b9896137b6565b905060005b886040015151811015612cf5578681815181106129dd576129dd6151e8565b60200260200101516020015115612ce357801580612a75575086612a026001836151a0565b81518110612a1257612a126151e8565b602002602001015160000151610100015173ffffffffffffffffffffffffffffffffffffffff16878281518110612a4b57612a4b6151e8565b602002602001015160000151610100015173ffffffffffffffffffffffffffffffffffffffff1614155b15612aa957612aa68a888381518110612a9057612a906151e8565b60200260200101516000015161010001516138a0565b92505b6000612bc78b6040518061012001604052808b8681518110612acd57612acd6151e8565b60200260200101516080015181526020018c81526020018a606001518c8781518110612afb57612afb6151e8565b602002602001015160a001518a612b1291906152e5565b612b1c9190615959565b81526020018d6000015181526020018d6020015181526020018681526020018b8681518110612b4d57612b4d6151e8565b602002602001015160000151610100015173ffffffffffffffffffffffffffffffffffffffff168152602001878152602001600115158152508c604001518581518110612b9c57612b9c6151e8565b60200260200101518b8681518110612bb657612bb66151e8565b602002602001015160000151613a1c565b9050806060015187604001818151612bdf919061596d565b6bffffffffffffffffffffffff169052506040810151602088018051612c0690839061596d565b6bffffffffffffffffffffffff169052508751889083908110612c2b57612c2b6151e8565b60200260200101516040015115158a604001518381518110612c4f57612c4f6151e8565b60200260200101517fad8cc9579b21dfe2c2f6ea35ba15b656e46b4f5b0cb424f52739b8ce5cac9c5b83602001518460000151612c8c919061596d565b8b8681518110612c9e57612c9e6151e8565b6020026020010151608001518d8f608001518881518110612cc157612cc16151e8565b6020026020010151604051612cd99493929190615992565b60405180910390a3505b80612ced81615217565b9150506129be565b505050602083810151336000908152600b90925260409091208054600290612d329084906201000090046bffffffffffffffffffffffff1661596d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508260400151601460000160008282829054906101000a90046bffffffffffffffffffffffff16612d90919061596d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555082604001518360200151612dd2919061596d565b6bffffffffffffffffffffffff16602160007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254612e4e91906152fc565b909155505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff821603612edb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610c8c565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b73ffffffffffffffffffffffffffffffffffffffff83166000908152600b602090815260408083208151608081018352905460ff80821615801584526101008304909116948301949094526bffffffffffffffffffffffff6201000082048116938301939093526e010000000000000000000000000000900490911660608201529061314d576000816060015185612fe991906159cf565b90506000612ff785836159f4565b9050808360400181815161300b919061596d565b6bffffffffffffffffffffffff169052506130268582615a1f565b83606001818151613037919061596d565b6bffffffffffffffffffffffff90811690915273ffffffffffffffffffffffffffffffffffffffff89166000908152600b602090815260409182902087518154928901519389015160608a015186166e010000000000000000000000000000027fffffffffffff000000000000000000000000ffffffffffffffffffffffffffff919096166201000002167fffffffffffff000000000000000000000000000000000000000000000000ffff60ff95909516610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff921515929092167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000909416939093171792909216179190911790555050505b60400151949350505050565b6000826000018281548110613170576131706151e8565b9060005260206000200154905092915050565b6000818152600183016020526040812054801561326c5760006131a76001836151a0565b85549091506000906131bb906001906151a0565b90508181146132205760008660000182815481106131db576131db6151e8565b90600052602060002001549050808760000184815481106131fe576131fe6151e8565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061323157613231615a4f565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050611ecd565b6000915050611ecd565b5092915050565b60008181526001830160205260408120546132c457508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155611ecd565b506000611ecd565b6000818160045b600f811015613359577fff000000000000000000000000000000000000000000000000000000000000008216838260208110613311576133116151e8565b1a60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161461334757506000949350505050565b8061335181615217565b9150506132d3565b5081600f1a600181111561336f5761336f61565d565b949350505050565b6000808080856060015160018111156133925761339261565d565b036133b8576133a48888888888613e8b565b6133b35760009250905061348c565b613430565b6001856060015160018111156133d0576133d061565d565b036133fe5760006133e389898988614015565b92509050806133f8575060009250905061348c565b50613430565b6040517ff2b2d41200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84516060015163ffffffff16871061348557877fc3237c8807c467c1b39b8d0395eff077313e691bf0a7388106792564ebfd5636876040516134729190614d1a565b60405180910390a260009250905061348c565b6001925090505b9550959350505050565b601454600090819077010000000000000000000000000000000000000000000000900460ff16156134f3576040517f37ed32e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601480547fffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffff16770100000000000000000000000000000000000000000000001790556040517f4585e33b0000000000000000000000000000000000000000000000000000000090613568908590602401614d1a565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290517f79188d1600000000000000000000000000000000000000000000000000000000815290935073ffffffffffffffffffffffffffffffffffffffff8616906379188d169061363b9087908790600401615a7e565b60408051808303816000875af1158015613659573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061367d9190615a97565b601480547fffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffff16905590969095509350505050565b6000816060015160018111156136c9576136c961565d565b0361372d57600083815260046020526040902060010180547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167c010000000000000000000000000000000000000000000000000000000063ffffffff851602179055505050565b6001816060015160018111156137455761374561565d565b036137b15760c08101805160009081526008602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055915191517fa4a4e334c0e330143f9437484fe516c13bc560b86b5b0daf58e7084aaac228f29190a25b505050565b60008060007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015613826573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061384a9190615adf565b5093505092505060008213158061386057508042105b8061389057506000846040015162ffffff16118015613890575061388481426151a0565b846040015162ffffff16105b15613276575050601b5492915050565b60408051608081018252600080825260208083018281528385018381526060850184905273ffffffffffffffffffffffffffffffffffffffff878116855260229093528584208054640100000000810462ffffff1690925263ffffffff82169092527b01000000000000000000000000000000000000000000000000000000810460ff16855285517ffeaf968c00000000000000000000000000000000000000000000000000000000815295519495919484936701000000000000009092049091169163feaf968c9160048083019260a09291908290030181865afa15801561398d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139b19190615adf565b509350509250506000821315806139c757508042105b806139f757506000866040015162ffffff161180156139f757506139eb81426151a0565b866040015162ffffff16105b15613a0b5760018301546060850152613a13565b606084018290525b50505092915050565b604080516101008101825260008082526020808301829052928201819052606082018190526080820181905260a0820181905260c0820181905260e08201529082015115613ab25760008381526023602090815260409182902082518084018452905463ffffffff811680835262ffffff640100000000909204821692840192835260e089018051909401529051915191169101525b6000613abe8686614222565b60c0840151602082015182519293509091600091613adb9161596d565b60e08801515190915060ff16600060128210613af8576001613b0e565b613b038260126151a0565b613b0e90600a615c4f565b9050600060128311613b21576001613b37565b613b2c6012846151a0565b613b3790600a615c4f565b905085600001516bffffffffffffffffffffffff16856bffffffffffffffffffffffff161015613bdf57849350613bb3818b60800151613b7791906152e5565b838c60e0015160600151886bffffffffffffffffffffffff16613b9a91906152e5565b613ba491906152e5565b613bae9190615959565b614550565b6bffffffffffffffffffffffff9081166040880152600060608801819052602088015285168652613d05565b836bffffffffffffffffffffffff16856bffffffffffffffffffffffff161015613d0557849350613c6d86604001516bffffffffffffffffffffffff16828c60800151613c2c91906152e5565b848d60e0015160600151896bffffffffffffffffffffffff16613c4f91906152e5565b613c5991906152e5565b613c639190615959565b613bae91906151a0565b6bffffffffffffffffffffffff1660608088019190915260e08b01510151613cf190613c9a9084906152e5565b6001848d60e0015160600151613cb091906152e5565b613cba91906151a0565b838d608001518a606001516bffffffffffffffffffffffff16613cdd91906152e5565b613ce791906152e5565b613ba491906152fc565b6bffffffffffffffffffffffff1660208701525b60008981526004602052604090206001018054859190601090613d4b90849070010000000000000000000000000000000090046bffffffffffffffffffffffff166159cf565b82546101009290920a6bffffffffffffffffffffffff81810219909316918316021790915560008b81526004602052604081206001018054928816935091613da69084906fffffffffffffffffffffffffffffffff16615c5b565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff160217905550836bffffffffffffffffffffffff16602160008c60c0015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254613e3d91906151a0565b92505081905550887f801ba6ed51146ffe3e99d1dbd9dd0f4de6292e78a9a34c39c0183de17b3f40fc87604051613e749190615c84565b60405180910390a250939998505050505050505050565b60008084806020019051810190613ea29190615d44565b845160e00151815191925063ffffffff90811691161015613eff57867f405288ea7be309e16cfdf481367f90a413e1d4634fcdaf8966546db9b93012e886604051613eed9190614d1a565b60405180910390a2600091505061400c565b8260e001518015613fbf5750602081015115801590613fbf5750602081015161010084015182516040517f85df51fd00000000000000000000000000000000000000000000000000000000815263ffffffff909116600482015273ffffffffffffffffffffffffffffffffffffffff909116906385df51fd90602401602060405180830381865afa158015613f98573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613fbc9190614f93565b14155b80613fd15750805163ffffffff168611155b1561400657867f6aa7f60c176da7af894b384daea2249497448137f5943c1237ada8bc92bdc30186604051613eed9190614d1a565b60019150505b95945050505050565b60008060008480602001905181019061402e9190615d9c565b905060008782600001518360200151846040015160405160200161409094939291909384526020840192909252604083015260e01b7fffffffff0000000000000000000000000000000000000000000000000000000016606082015260640190565b6040516020818303038152906040528051906020012090508460e00151801561416b575060808201511580159061416b5750608082015161010086015160608401516040517f85df51fd00000000000000000000000000000000000000000000000000000000815263ffffffff909116600482015273ffffffffffffffffffffffffffffffffffffffff909116906385df51fd90602401602060405180830381865afa158015614144573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141689190614f93565b14155b80614180575086826060015163ffffffff1610155b156141ca57877f6aa7f60c176da7af894b384daea2249497448137f5943c1237ada8bc92bdc301876040516141b59190614d1a565b60405180910390a26000935091506142199050565b60008181526008602052604090205460ff161561421157877f405288ea7be309e16cfdf481367f90a413e1d4634fcdaf8966546db9b93012e8876040516141b59190614d1a565b600193509150505b94509492505050565b6040805161010081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081019190915260008260e001516000015160ff1690506000846060015161ffff16846060015161428d91906152e5565b905083610100015180156142a05750803a105b156142a857503a5b6000601283116142b95760016142cf565b6142c46012846151a0565b6142cf90600a615c4f565b90506000601284106142e25760016142f8565b6142ed8460126151a0565b6142f890600a615c4f565b905060008660a0015187604001518860200151896000015161431a91906152fc565b61432490876152e5565b61432e91906152fc565b61433891906152e5565b905061437b828860e001516060015161435191906152e5565b6001848a60e001516060015161436791906152e5565b61437191906151a0565b613ce786856152e5565b6bffffffffffffffffffffffff168652608087015161439e90613bae9083615959565b6bffffffffffffffffffffffff1660408088019190915260e088015101516000906143d79062ffffff16683635c9adc5dea000006152e5565b9050600081633b9aca008a60a001518b60e001516020015163ffffffff168c604001518d600001518b61440a91906152e5565b61441491906152fc565b61441e91906152e5565b61442891906152e5565b6144329190615959565b61443c91906152fc565b905061447f848a60e001516060015161445591906152e5565b6001868c60e001516060015161446b91906152e5565b61447591906151a0565b613ce788856152e5565b6bffffffffffffffffffffffff16602089015260808901516144a590613bae9083615959565b6bffffffffffffffffffffffff16606089015260c089015173ffffffffffffffffffffffffffffffffffffffff166080808a01919091528901516144e890614550565b6bffffffffffffffffffffffff1660a0808a019190915289015161450b90614550565b6bffffffffffffffffffffffff1660c089015260e08901516060015161453090614550565b6bffffffffffffffffffffffff1660e08901525050505050505092915050565b60006bffffffffffffffffffffffff8211156145ee576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201527f36206269747300000000000000000000000000000000000000000000000000006064820152608401610c8c565b5090565b508054600082559060005260206000209081019061109c9190614692565b82805482825590600052602060002090810192821561468a579160200282015b8281111561468a57825182547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909116178255602090920191600190910190614630565b506145ee9291505b5b808211156145ee5760008155600101614693565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610200810167ffffffffffffffff811182821017156146fa576146fa6146a7565b60405290565b60405160c0810167ffffffffffffffff811182821017156146fa576146fa6146a7565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561476a5761476a6146a7565b604052919050565b600067ffffffffffffffff82111561478c5761478c6146a7565b5060051b60200190565b73ffffffffffffffffffffffffffffffffffffffff8116811461109c57600080fd5b80356147c381614796565b919050565b600082601f8301126147d957600080fd5b813560206147ee6147e983614772565b614723565b82815260059290921b8401810191818101908684111561480d57600080fd5b8286015b8481101561483157803561482481614796565b8352918301918301614811565b509695505050505050565b60ff8116811461109c57600080fd5b80356147c38161483c565b63ffffffff8116811461109c57600080fd5b80356147c381614856565b801515811461109c57600080fd5b80356147c381614873565b62ffffff8116811461109c57600080fd5b80356147c38161488c565b61ffff8116811461109c57600080fd5b80356147c3816148a8565b600061020082840312156148d657600080fd5b6148de6146d6565b90506148e982614868565b81526148f760208301614868565b602082015261490860408301614868565b6040820152614919606083016147b8565b606082015261492a60808301614881565b608082015261493b60a0830161489d565b60a082015261494c60c08301614868565b60c082015261495d60e08301614868565b60e08201526101006149708184016147b8565b908201526101206149828382016148b8565b908201526101406149948382016147b8565b90820152610160828101359082015261018080830135908201526101a080830135908201526101c08083013567ffffffffffffffff8111156149d557600080fd5b6149e1858286016147c8565b8284015250506101e06149f58184016147b8565b9082015292915050565b803567ffffffffffffffff811681146147c357600080fd5b600082601f830112614a2857600080fd5b813567ffffffffffffffff811115614a4257614a426146a7565b614a7360207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601614723565b818152846020838601011115614a8857600080fd5b816020850160208301376000918101602001919091529392505050565b6bffffffffffffffffffffffff8116811461109c57600080fd5b600082601f830112614ad057600080fd5b81356020614ae06147e983614772565b82815260c09283028501820192828201919087851115614aff57600080fd5b8387015b85811015614b915781818a031215614b1b5760008081fd5b614b23614700565b8135614b2e81614856565b815281860135614b3d8161488c565b81870152604082810135614b5081614796565b90820152606082810135614b638161483c565b908201526080828101359082015260a080830135614b8081614aa5565b908201528452928401928101614b03565b5090979650505050505050565b600080600080600080600080610100898b031215614bbb57600080fd5b883567ffffffffffffffff80821115614bd357600080fd5b614bdf8c838d016147c8565b995060208b0135915080821115614bf557600080fd5b614c018c838d016147c8565b9850614c0f60408c0161484b565b975060608b0135915080821115614c2557600080fd5b614c318c838d016148c3565b9650614c3f60808c016149ff565b955060a08b0135915080821115614c5557600080fd5b614c618c838d01614a17565b945060c08b0135915080821115614c7757600080fd5b614c838c838d016147c8565b935060e08b0135915080821115614c9957600080fd5b50614ca68b828c01614abf565b9150509295985092959890939650565b6000815180845260005b81811015614cdc57602081850181015186830182015201614cc0565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081526000611edf6020830184614cb6565b60008083601f840112614d3f57600080fd5b50813567ffffffffffffffff811115614d5757600080fd5b6020830191508360208260051b8501011115614d7257600080fd5b9250929050565b60008060008060008060008060e0898b031215614d9557600080fd5b606089018a811115614da657600080fd5b8998503567ffffffffffffffff80821115614dc057600080fd5b818b0191508b601f830112614dd457600080fd5b813581811115614de357600080fd5b8c6020828501011115614df557600080fd5b6020830199508098505060808b0135915080821115614e1357600080fd5b614e1f8c838d01614d2d565b909750955060a08b0135915080821115614e3857600080fd5b50614e458b828c01614d2d565b999c989b50969995989497949560c00135949350505050565b60008060008060008060c08789031215614e7757600080fd5b863567ffffffffffffffff80821115614e8f57600080fd5b614e9b8a838b016147c8565b97506020890135915080821115614eb157600080fd5b614ebd8a838b016147c8565b9650614ecb60408a0161484b565b95506060890135915080821115614ee157600080fd5b614eed8a838b01614a17565b9450614efb60808a016149ff565b935060a0890135915080821115614f1157600080fd5b50614f1e89828a01614a17565b9150509295509295509295565b600060208284031215614f3d57600080fd5b8135611edf81614796565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60ff818116838216029081169081811461327657613276614f48565b600060208284031215614fa557600080fd5b5051919050565b63ffffffff81811683821601908082111561327657613276614f48565b600081518084526020808501945080840160005b8381101561500f57815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101614fdd565b509495945050505050565b6020815261503160208201835163ffffffff169052565b6000602083015161504a604084018263ffffffff169052565b50604083015163ffffffff8116606084015250606083015173ffffffffffffffffffffffffffffffffffffffff8116608084015250608083015180151560a08401525060a083015162ffffff811660c08401525060c083015163ffffffff811660e08401525060e08301516101006150c98185018363ffffffff169052565b84015190506101206150f28482018373ffffffffffffffffffffffffffffffffffffffff169052565b84015190506101406151098482018361ffff169052565b84015190506101606151328482018373ffffffffffffffffffffffffffffffffffffffff169052565b840151610180848101919091528401516101a0808501919091528401516101c0808501919091528401516102006101e08086018290529192509061517a610220860184614fc9565b95015173ffffffffffffffffffffffffffffffffffffffff169301929092525090919050565b81810381811115611ecd57611ecd614f48565b6000816151c2576151c2614f48565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361524857615248614f48565b5060010190565b600061012063ffffffff808d1684528b6020850152808b1660408501525080606084015261527f8184018a614fc9565b905082810360808401526152938189614fc9565b905060ff871660a084015282810360c08401526152b08187614cb6565b905067ffffffffffffffff851660e08401528281036101008401526152d58185614cb6565b9c9b505050505050505050505050565b8082028115828204841417611ecd57611ecd614f48565b80820180821115611ecd57611ecd614f48565b60ff8181168382160190811115611ecd57611ecd614f48565b80516147c381614856565b80516147c381614796565b80516147c381614873565b80516147c38161488c565b80516147c3816148a8565b600082601f83011261537057600080fd5b815160206153806147e983614772565b82815260059290921b8401810191818101908684111561539f57600080fd5b8286015b848110156148315780516153b681614796565b83529183019183016153a3565b600082601f8301126153d457600080fd5b815160206153e46147e983614772565b82815260c0928302850182019282820191908785111561540357600080fd5b8387015b85811015614b915781818a03121561541f5760008081fd5b615427614700565b815161543281614856565b8152818601516154418161488c565b8187015260408281015161545481614796565b908201526060828101516154678161483c565b908201526080828101519082015260a08083015161548481614aa5565b908201528452928401928101615407565b6000806000606084860312156154aa57600080fd5b835167ffffffffffffffff808211156154c257600080fd5b9085019061020082880312156154d757600080fd5b6154df6146d6565b6154e883615328565b81526154f660208401615328565b602082015261550760408401615328565b604082015261551860608401615333565b60608201526155296080840161533e565b608082015261553a60a08401615349565b60a082015261554b60c08401615328565b60c082015261555c60e08401615328565b60e082015261010061556f818501615333565b90820152610120615581848201615354565b90820152610140615593848201615333565b90820152610160838101519082015261018080840151908201526101a080840151908201526101c080840151838111156155cc57600080fd5b6155d88a82870161535f565b8284015250506101e06155ec818501615333565b90820152602087015190955091508082111561560757600080fd5b6156138783880161535f565b9350604086015191508082111561562957600080fd5b50615636868287016153c3565b9150509250925092565b60006020828403121561565257600080fd5b8151611edf8161483c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60006101208b835273ffffffffffffffffffffffffffffffffffffffff8b16602084015267ffffffffffffffff808b1660408501528160608501526156d38285018b614fc9565b915083820360808501526156e7828a614fc9565b915060ff881660a085015283820360c08501526157048288614cb6565b90861660e085015283810361010085015290506152d58185614cb6565b8183823760009101908152919050565b8281526080810160608360208401379392505050565b600082601f83011261575857600080fd5b813560206157686147e983614772565b82815260059290921b8401810191818101908684111561578757600080fd5b8286015b84811015614831578035835291830191830161578b565b600082601f8301126157b357600080fd5b813560206157c36147e983614772565b82815260059290921b840181019181810190868411156157e257600080fd5b8286015b8481101561483157803567ffffffffffffffff8111156158065760008081fd5b6158148986838b0101614a17565b8452509183019183016157e6565b60006020828403121561583457600080fd5b813567ffffffffffffffff8082111561584c57600080fd5b9083019060c0828603121561586057600080fd5b615868614700565b823581526020830135602082015260408301358281111561588857600080fd5b61589487828601615747565b6040830152506060830135828111156158ac57600080fd5b6158b887828601615747565b6060830152506080830135828111156158d057600080fd5b6158dc878286016157a2565b60808301525060a0830135828111156158f457600080fd5b615900878286016157a2565b60a08301525095945050505050565b61ffff81811683821601908082111561327657613276614f48565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826159685761596861592a565b500490565b6bffffffffffffffffffffffff81811683821601908082111561327657613276614f48565b6bffffffffffffffffffffffff851681528360208201528260408201526080606082015260006159c56080830184614cb6565b9695505050505050565b6bffffffffffffffffffffffff82811682821603908082111561327657613276614f48565b60006bffffffffffffffffffffffff80841680615a1357615a1361592a565b92169190910492915050565b6bffffffffffffffffffffffff818116838216028082169190828114615a4757615a47614f48565b505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b82815260406020820152600061336f6040830184614cb6565b60008060408385031215615aaa57600080fd5b8251615ab581614873565b6020939093015192949293505050565b805169ffffffffffffffffffff811681146147c357600080fd5b600080600080600060a08688031215615af757600080fd5b615b0086615ac5565b9450602086015193506040860151925060608601519150615b2360808701615ac5565b90509295509295909350565b600181815b80851115615b8857817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115615b6e57615b6e614f48565b80851615615b7b57918102915b93841c9390800290615b34565b509250929050565b600082615b9f57506001611ecd565b81615bac57506000611ecd565b8160018114615bc25760028114615bcc57615be8565b6001915050611ecd565b60ff841115615bdd57615bdd614f48565b50506001821b611ecd565b5060208310610133831016604e8410600b8410161715615c0b575081810a611ecd565b615c158383615b2f565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115615c4757615c47614f48565b029392505050565b6000611edf8383615b90565b6fffffffffffffffffffffffffffffffff81811683821601908082111561327657613276614f48565b6000610100820190506bffffffffffffffffffffffff8084511683528060208501511660208401528060408501511660408401528060608501511660608401525073ffffffffffffffffffffffffffffffffffffffff608084015116608083015260a0830151615d0460a08401826bffffffffffffffffffffffff169052565b5060c0830151615d2460c08401826bffffffffffffffffffffffff169052565b5060e083015161327660e08401826bffffffffffffffffffffffff169052565b600060408284031215615d5657600080fd5b6040516040810181811067ffffffffffffffff82111715615d7957615d796146a7565b6040528251615d8781614856565b81526020928301519281019290925250919050565b600060a08284031215615dae57600080fd5b60405160a0810181811067ffffffffffffffff82111715615dd157615dd16146a7565b806040525082518152602083015160208201526040830151615df281614856565b60408201526060830151615e0581614856565b6060820152608092830151928101929092525091905056fea164736f6c6343000813000a", } var AutomationRegistryABI = AutomationRegistryMetaData.ABI diff --git a/core/gethwrappers/generated/chain_module_base/chain_module_base.go b/core/gethwrappers/generated/chain_module_base/chain_module_base.go index bf98608aab..19bc49298f 100644 --- a/core/gethwrappers/generated/chain_module_base/chain_module_base.go +++ b/core/gethwrappers/generated/chain_module_base/chain_module_base.go @@ -29,8 +29,8 @@ var ( ) 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: "0x608060405234801561001057600080fd5b5061015c806100206000396000f3fe608060405234801561001057600080fd5b50600436106100675760003560e01c806357e871e71161005057806357e871e71461009a57806385df51fd146100a0578063de9ee35e146100b357600080fd5b8063125441401461006c57806318b8f61314610093575b600080fd5b61008061007a3660046100f6565b50600090565b6040519081526020015b60405180910390f35b6000610080565b43610080565b6100806100ae3660046100f6565b6100c9565b6040805161012c8152600060208201520161008a565b600043821015806100e457506101006100e2834361010f565b115b156100f157506000919050565b504090565b60006020828403121561010857600080fd5b5035919050565b81810381811115610149577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b9291505056fea164736f6c6343000813000a", + 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\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"getCurrentL1Fee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"l1Fee\",\"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\":\"maxL1Fee\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b50610155806100206000396000f3fe608060405234801561001057600080fd5b50600436106100675760003560e01c80637810d12a116100505780637810d12a1461006c57806385df51fd14610099578063de9ee35e146100ac57600080fd5b8063125441401461006c57806357e871e714610093575b600080fd5b61008061007a3660046100ef565b50600090565b6040519081526020015b60405180910390f35b43610080565b6100806100a73660046100ef565b6100c2565b6040805161012c8152600060208201520161008a565b600043821015806100dd57506101006100db8343610108565b115b156100ea57506000919050565b504090565b60006020828403121561010157600080fd5b5035919050565b81810381811115610142577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b9291505056fea164736f6c6343000813000a", } var ChainModuleBaseABI = ChainModuleBaseMetaData.ABI @@ -213,9 +213,9 @@ func (_ChainModuleBase *ChainModuleBaseCallerSession) BlockNumber() (*big.Int, e return _ChainModuleBase.Contract.BlockNumber(&_ChainModuleBase.CallOpts) } -func (_ChainModuleBase *ChainModuleBaseCaller) GetCurrentL1Fee(opts *bind.CallOpts) (*big.Int, error) { +func (_ChainModuleBase *ChainModuleBaseCaller) GetCurrentL1Fee(opts *bind.CallOpts, arg0 *big.Int) (*big.Int, error) { var out []interface{} - err := _ChainModuleBase.contract.Call(opts, &out, "getCurrentL1Fee") + err := _ChainModuleBase.contract.Call(opts, &out, "getCurrentL1Fee", arg0) if err != nil { return *new(*big.Int), err @@ -227,12 +227,12 @@ func (_ChainModuleBase *ChainModuleBaseCaller) GetCurrentL1Fee(opts *bind.CallOp } -func (_ChainModuleBase *ChainModuleBaseSession) GetCurrentL1Fee() (*big.Int, error) { - return _ChainModuleBase.Contract.GetCurrentL1Fee(&_ChainModuleBase.CallOpts) +func (_ChainModuleBase *ChainModuleBaseSession) GetCurrentL1Fee(arg0 *big.Int) (*big.Int, error) { + return _ChainModuleBase.Contract.GetCurrentL1Fee(&_ChainModuleBase.CallOpts, arg0) } -func (_ChainModuleBase *ChainModuleBaseCallerSession) GetCurrentL1Fee() (*big.Int, error) { - return _ChainModuleBase.Contract.GetCurrentL1Fee(&_ChainModuleBase.CallOpts) +func (_ChainModuleBase *ChainModuleBaseCallerSession) GetCurrentL1Fee(arg0 *big.Int) (*big.Int, error) { + return _ChainModuleBase.Contract.GetCurrentL1Fee(&_ChainModuleBase.CallOpts, arg0) } func (_ChainModuleBase *ChainModuleBaseCaller) GetGasOverhead(opts *bind.CallOpts) (GetGasOverhead, @@ -301,7 +301,7 @@ type ChainModuleBaseInterface interface { BlockNumber(opts *bind.CallOpts) (*big.Int, error) - GetCurrentL1Fee(opts *bind.CallOpts) (*big.Int, error) + GetCurrentL1Fee(opts *bind.CallOpts, arg0 *big.Int) (*big.Int, error) GetGasOverhead(opts *bind.CallOpts) (GetGasOverhead, diff --git a/core/gethwrappers/generated/chain_reader_tester/chain_reader_tester.go b/core/gethwrappers/generated/chain_reader_tester/chain_reader_tester.go index 751df82269..c59a6f0f0d 100644 --- a/core/gethwrappers/generated/chain_reader_tester/chain_reader_tester.go +++ b/core/gethwrappers/generated/chain_reader_tester/chain_reader_tester.go @@ -52,8 +52,8 @@ type TestStruct struct { } var ChainReaderTesterMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"int32\",\"name\":\"field\",\"type\":\"int32\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"differentField\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"oracleId\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"uint8[32]\",\"name\":\"oracleIds\",\"type\":\"uint8[32]\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"Account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"Accounts\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"int192\",\"name\":\"bigField\",\"type\":\"int192\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"FixedBytes\",\"type\":\"bytes2\"},{\"components\":[{\"internalType\":\"int64\",\"name\":\"IntVal\",\"type\":\"int64\"},{\"internalType\":\"string\",\"name\":\"S\",\"type\":\"string\"}],\"internalType\":\"structInnerTestStruct\",\"name\":\"Inner\",\"type\":\"tuple\"}],\"indexed\":false,\"internalType\":\"structMidLevelTestStruct\",\"name\":\"nestedStruct\",\"type\":\"tuple\"}],\"name\":\"Triggered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"string\",\"name\":\"fieldHash\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"field\",\"type\":\"string\"}],\"name\":\"TriggeredEventWithDynamicTopic\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"int32\",\"name\":\"field1\",\"type\":\"int32\"},{\"indexed\":true,\"internalType\":\"int32\",\"name\":\"field2\",\"type\":\"int32\"},{\"indexed\":true,\"internalType\":\"int32\",\"name\":\"field3\",\"type\":\"int32\"}],\"name\":\"TriggeredWithFourTopics\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"int32\",\"name\":\"field\",\"type\":\"int32\"},{\"internalType\":\"string\",\"name\":\"differentField\",\"type\":\"string\"},{\"internalType\":\"uint8\",\"name\":\"oracleId\",\"type\":\"uint8\"},{\"internalType\":\"uint8[32]\",\"name\":\"oracleIds\",\"type\":\"uint8[32]\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"accounts\",\"type\":\"address[]\"},{\"internalType\":\"int192\",\"name\":\"bigField\",\"type\":\"int192\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"FixedBytes\",\"type\":\"bytes2\"},{\"components\":[{\"internalType\":\"int64\",\"name\":\"IntVal\",\"type\":\"int64\"},{\"internalType\":\"string\",\"name\":\"S\",\"type\":\"string\"}],\"internalType\":\"structInnerTestStruct\",\"name\":\"Inner\",\"type\":\"tuple\"}],\"internalType\":\"structMidLevelTestStruct\",\"name\":\"nestedStruct\",\"type\":\"tuple\"}],\"name\":\"addTestStruct\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAlterablePrimitiveValue\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDifferentPrimitiveValue\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"i\",\"type\":\"uint256\"}],\"name\":\"getElementAtIndex\",\"outputs\":[{\"components\":[{\"internalType\":\"int32\",\"name\":\"Field\",\"type\":\"int32\"},{\"internalType\":\"string\",\"name\":\"DifferentField\",\"type\":\"string\"},{\"internalType\":\"uint8\",\"name\":\"OracleId\",\"type\":\"uint8\"},{\"internalType\":\"uint8[32]\",\"name\":\"OracleIds\",\"type\":\"uint8[32]\"},{\"internalType\":\"address\",\"name\":\"Account\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"Accounts\",\"type\":\"address[]\"},{\"internalType\":\"int192\",\"name\":\"BigField\",\"type\":\"int192\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"FixedBytes\",\"type\":\"bytes2\"},{\"components\":[{\"internalType\":\"int64\",\"name\":\"IntVal\",\"type\":\"int64\"},{\"internalType\":\"string\",\"name\":\"S\",\"type\":\"string\"}],\"internalType\":\"structInnerTestStruct\",\"name\":\"Inner\",\"type\":\"tuple\"}],\"internalType\":\"structMidLevelTestStruct\",\"name\":\"NestedStruct\",\"type\":\"tuple\"}],\"internalType\":\"structTestStruct\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getPrimitiveValue\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getSliceValue\",\"outputs\":[{\"internalType\":\"uint64[]\",\"name\":\"\",\"type\":\"uint64[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int32\",\"name\":\"field\",\"type\":\"int32\"},{\"internalType\":\"string\",\"name\":\"differentField\",\"type\":\"string\"},{\"internalType\":\"uint8\",\"name\":\"oracleId\",\"type\":\"uint8\"},{\"internalType\":\"uint8[32]\",\"name\":\"oracleIds\",\"type\":\"uint8[32]\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"accounts\",\"type\":\"address[]\"},{\"internalType\":\"int192\",\"name\":\"bigField\",\"type\":\"int192\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"FixedBytes\",\"type\":\"bytes2\"},{\"components\":[{\"internalType\":\"int64\",\"name\":\"IntVal\",\"type\":\"int64\"},{\"internalType\":\"string\",\"name\":\"S\",\"type\":\"string\"}],\"internalType\":\"structInnerTestStruct\",\"name\":\"Inner\",\"type\":\"tuple\"}],\"internalType\":\"structMidLevelTestStruct\",\"name\":\"nestedStruct\",\"type\":\"tuple\"}],\"name\":\"returnSeen\",\"outputs\":[{\"components\":[{\"internalType\":\"int32\",\"name\":\"Field\",\"type\":\"int32\"},{\"internalType\":\"string\",\"name\":\"DifferentField\",\"type\":\"string\"},{\"internalType\":\"uint8\",\"name\":\"OracleId\",\"type\":\"uint8\"},{\"internalType\":\"uint8[32]\",\"name\":\"OracleIds\",\"type\":\"uint8[32]\"},{\"internalType\":\"address\",\"name\":\"Account\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"Accounts\",\"type\":\"address[]\"},{\"internalType\":\"int192\",\"name\":\"BigField\",\"type\":\"int192\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"FixedBytes\",\"type\":\"bytes2\"},{\"components\":[{\"internalType\":\"int64\",\"name\":\"IntVal\",\"type\":\"int64\"},{\"internalType\":\"string\",\"name\":\"S\",\"type\":\"string\"}],\"internalType\":\"structInnerTestStruct\",\"name\":\"Inner\",\"type\":\"tuple\"}],\"internalType\":\"structMidLevelTestStruct\",\"name\":\"NestedStruct\",\"type\":\"tuple\"}],\"internalType\":\"structTestStruct\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"value\",\"type\":\"uint64\"}],\"name\":\"setAlterablePrimitiveValue\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int32\",\"name\":\"field\",\"type\":\"int32\"},{\"internalType\":\"string\",\"name\":\"differentField\",\"type\":\"string\"},{\"internalType\":\"uint8\",\"name\":\"oracleId\",\"type\":\"uint8\"},{\"internalType\":\"uint8[32]\",\"name\":\"oracleIds\",\"type\":\"uint8[32]\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"accounts\",\"type\":\"address[]\"},{\"internalType\":\"int192\",\"name\":\"bigField\",\"type\":\"int192\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"FixedBytes\",\"type\":\"bytes2\"},{\"components\":[{\"internalType\":\"int64\",\"name\":\"IntVal\",\"type\":\"int64\"},{\"internalType\":\"string\",\"name\":\"S\",\"type\":\"string\"}],\"internalType\":\"structInnerTestStruct\",\"name\":\"Inner\",\"type\":\"tuple\"}],\"internalType\":\"structMidLevelTestStruct\",\"name\":\"nestedStruct\",\"type\":\"tuple\"}],\"name\":\"triggerEvent\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"field\",\"type\":\"string\"}],\"name\":\"triggerEventWithDynamicTopic\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int32\",\"name\":\"field1\",\"type\":\"int32\"},{\"internalType\":\"int32\",\"name\":\"field2\",\"type\":\"int32\"},{\"internalType\":\"int32\",\"name\":\"field3\",\"type\":\"int32\"}],\"name\":\"triggerWithFourTopics\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x608060405234801561001057600080fd5b50600180548082018255600082905260048082047fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6908101805460086003958616810261010090810a8088026001600160401b0391820219909416939093179093558654808801909755848704909301805496909516909202900a91820291021990921691909117905561181e806100a96000396000f3fe608060405234801561001057600080fd5b50600436106100c95760003560e01c80637f002d6711610081578063ef4e1ced1161005b578063ef4e1ced146101c0578063f6f871c8146101c7578063fbe9fbf6146101da57600080fd5b80637f002d671461017d578063ab5e0b3814610190578063dbfd7332146101ad57600080fd5b806349eac2ac116100b257806349eac2ac1461010c578063679004a41461011f5780636c9a43b61461013457600080fd5b80632c45576f146100ce5780633272b66c146100f7575b600080fd5b6100e16100dc366004610c2b565b6101ec565b6040516100ee9190610d8a565b60405180910390f35b61010a610105366004610ec9565b6104c7565b005b61010a61011a366004610fde565b61051c565b61012761081f565b6040516100ee91906110d0565b61010a61014236600461111e565b600280547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff92909216919091179055565b61010a61018b366004610fde565b6108ab565b6107c65b60405167ffffffffffffffff90911681526020016100ee565b61010a6101bb36600461114f565b610902565b6003610194565b6100e16101d5366004610fde565b61093f565b60025467ffffffffffffffff16610194565b6101f4610a48565b6000610201600184611192565b81548110610211576102116111cc565b6000918252602091829020604080516101008101909152600a90920201805460030b8252600181018054929391929184019161024c906111fb565b80601f0160208091040260200160405190810160405280929190818152602001828054610278906111fb565b80156102c55780601f1061029a576101008083540402835291602001916102c5565b820191906000526020600020905b8154815290600101906020018083116102a857829003601f168201915b5050509183525050600282015460ff166020808301919091526040805161040081018083529190930192916003850191826000855b825461010083900a900460ff168152602060019283018181049485019490930390920291018084116102fa57505050928452505050600482015473ffffffffffffffffffffffffffffffffffffffff1660208083019190915260058301805460408051828502810185018252828152940193928301828280156103b357602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311610388575b5050509183525050600682015460170b6020808301919091526040805180820182526007808601805460f01b7fffff0000000000000000000000000000000000000000000000000000000000001683528351808501855260088801805490930b81526009880180549590970196939591948683019491939284019190610438906111fb565b80601f0160208091040260200160405190810160405280929190818152602001828054610464906111fb565b80156104b15780601f10610486576101008083540402835291602001916104b1565b820191906000526020600020905b81548152906001019060200180831161049457829003601f168201915b5050509190925250505090525090525092915050565b81816040516104d7929190611248565b60405180910390207f3d969732b1bbbb9f1d7eb9f3f14e4cb50a74d950b3ef916a397b85dfbab93c6783836040516105109291906112a1565b60405180910390a25050565b60006040518061010001604052808c60030b81526020018b8b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525060ff8a166020808301919091526040805161040081810183529190930192918b9183908390808284376000920191909152505050815273ffffffffffffffffffffffffffffffffffffffff8816602080830191909152604080518883028181018401835289825291909301929189918991829190850190849080828437600092019190915250505090825250601785900b602082015260400161060e8461139e565b905281546001808201845560009384526020938490208351600a9093020180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff90931692909217825592820151919290919082019061067490826114f8565b5060408201516002820180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff90921691909117905560608201516106c29060038301906020610a97565b5060808201516004820180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90921691909117905560a08201518051610729916005840191602090910190610b2a565b5060c08201516006820180547fffffffffffffffff0000000000000000000000000000000000000000000000001677ffffffffffffffffffffffffffffffffffffffffffffffff90921691909117905560e082015180516007830180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001660f09290921c91909117815560208083015180516008860180547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff90921691909117815591810151909190600986019061080c90826114f8565b5050505050505050505050505050505050565b606060018054806020026020016040519081016040528092919081815260200182805480156108a157602002820191906000526020600020906000905b82829054906101000a900467ffffffffffffffff1667ffffffffffffffff168152602001906008019060208260070104928301926001038202915080841161085c5790505b5050505050905090565b8960030b7f7188419dcd8b51877b71766f075f3626586c0ff190e7d056aa65ce9acb649a3d8a8a8a8a8a8a8a8a8a6040516108ee99989796959493929190611757565b60405180910390a250505050505050505050565b8060030b8260030b8460030b7f91c80dc390f3d041b3a04b0099b19634499541ea26972250986ee4b24a12fac560405160405180910390a4505050565b610947610a48565b6040518061010001604052808c60030b81526020018b8b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525060ff8a166020808301919091526040805161040081810183529190930192918b9183908390808284376000920191909152505050815273ffffffffffffffffffffffffffffffffffffffff8816602080830191909152604080518883028181018401835289825291909301929189918991829190850190849080828437600092019190915250505090825250601785900b6020820152604001610a378461139e565b90529b9a5050505050505050505050565b6040805161010081018252600080825260606020830181905292820152908101610a70610ba4565b8152600060208201819052606060408301819052820152608001610a92610bc3565b905290565b600183019183908215610b1a5791602002820160005b83821115610aeb57835183826101000a81548160ff021916908360ff1602179055509260200192600101602081600001049283019260010302610aad565b8015610b185782816101000a81549060ff0219169055600101602081600001049283019260010302610aeb565b505b50610b26929150610c16565b5090565b828054828255906000526020600020908101928215610b1a579160200282015b82811115610b1a57825182547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909116178255602090920191600190910190610b4a565b6040518061040001604052806020906020820280368337509192915050565b604051806040016040528060007dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001610a926040518060400160405280600060070b8152602001606081525090565b5b80821115610b265760008155600101610c17565b600060208284031215610c3d57600080fd5b5035919050565b6000815180845260005b81811015610c6a57602081850181015186830182015201610c4e565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b8060005b6020808210610cbb5750610cd2565b825160ff1685529384019390910190600101610cac565b50505050565b600081518084526020808501945080840160005b83811015610d1e57815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101610cec565b509495945050505050565b7fffff00000000000000000000000000000000000000000000000000000000000081511682526000602082015160406020850152805160070b60408501526020810151905060406060850152610d826080850182610c44565b949350505050565b60208152610d9e60208201835160030b9052565b600060208301516104e0806040850152610dbc610500850183610c44565b91506040850151610dd2606086018260ff169052565b506060850151610de56080860182610ca8565b50608085015173ffffffffffffffffffffffffffffffffffffffff1661048085015260a08501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe085840381016104a0870152610e428483610cd8565b935060c08701519150610e5b6104c087018360170b9052565b60e0870151915080868503018387015250610e768382610d29565b9695505050505050565b60008083601f840112610e9257600080fd5b50813567ffffffffffffffff811115610eaa57600080fd5b602083019150836020828501011115610ec257600080fd5b9250929050565b60008060208385031215610edc57600080fd5b823567ffffffffffffffff811115610ef357600080fd5b610eff85828601610e80565b90969095509350505050565b8035600381900b8114610f1d57600080fd5b919050565b803560ff81168114610f1d57600080fd5b806104008101831015610f4557600080fd5b92915050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610f1d57600080fd5b60008083601f840112610f8157600080fd5b50813567ffffffffffffffff811115610f9957600080fd5b6020830191508360208260051b8501011115610ec257600080fd5b8035601781900b8114610f1d57600080fd5b600060408284031215610fd857600080fd5b50919050565b6000806000806000806000806000806104e08b8d031215610ffe57600080fd5b6110078b610f0b565b995060208b013567ffffffffffffffff8082111561102457600080fd5b6110308e838f01610e80565b909b50995089915061104460408e01610f22565b98506110538e60608f01610f33565b97506110626104608e01610f4b565b96506104808d013591508082111561107957600080fd5b6110858e838f01610f6f565b909650945084915061109a6104a08e01610fb4565b93506104c08d01359150808211156110b157600080fd5b506110be8d828e01610fc6565b9150509295989b9194979a5092959850565b6020808252825182820181905260009190848201906040850190845b8181101561111257835167ffffffffffffffff16835292840192918401916001016110ec565b50909695505050505050565b60006020828403121561113057600080fd5b813567ffffffffffffffff8116811461114857600080fd5b9392505050565b60008060006060848603121561116457600080fd5b61116d84610f0b565b925061117b60208501610f0b565b915061118960408501610f0b565b90509250925092565b81810381811115610f45577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600181811c9082168061120f57607f821691505b602082108103610fd8577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b8183823760009101908152919050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b602081526000610d82602083018486611258565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715611307576113076112b5565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715611354576113546112b5565b604052919050565b80357fffff00000000000000000000000000000000000000000000000000000000000081168114610f1d57600080fd5b8035600781900b8114610f1d57600080fd5b6000604082360312156113b057600080fd5b6113b86112e4565b6113c18361135c565b815260208084013567ffffffffffffffff808211156113df57600080fd5b8186019150604082360312156113f457600080fd5b6113fc6112e4565b6114058361138c565b8152838301358281111561141857600080fd5b929092019136601f84011261142c57600080fd5b82358281111561143e5761143e6112b5565b61146e857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161130d565b9250808352368582860101111561148457600080fd5b8085850186850137600090830185015280840191909152918301919091525092915050565b601f8211156114f357600081815260208120601f850160051c810160208610156114d05750805b601f850160051c820191505b818110156114ef578281556001016114dc565b5050505b505050565b815167ffffffffffffffff811115611512576115126112b5565b6115268161152084546111fb565b846114a9565b602080601f83116001811461157957600084156115435750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556114ef565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b828110156115c6578886015182559484019460019091019084016115a7565b508582101561160257878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b8183526000602080850194508260005b85811015610d1e5773ffffffffffffffffffffffffffffffffffffffff61164883610f4b565b1687529582019590820190600101611622565b7fffff0000000000000000000000000000000000000000000000000000000000006116858261135c565b168252600060208201357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18336030181126116bf57600080fd5b6040602085015282016116d18161138c565b60070b604085015260208101357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe182360301811261170e57600080fd5b0160208101903567ffffffffffffffff81111561172a57600080fd5b80360382131561173957600080fd5b6040606086015261174e608086018284611258565b95945050505050565b60006104c080835261176c8184018c8e611258565b9050602060ff808c1682860152604085018b60005b848110156117a6578361179383610f22565b1683529184019190840190600101611781565b505050505073ffffffffffffffffffffffffffffffffffffffff88166104408401528281036104608401526117dc818789611612565b90506117ee61048084018660170b9052565b8281036104a0840152611801818561165b565b9c9b50505050505050505050505056fea164736f6c6343000813000a", + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"int32\",\"name\":\"field\",\"type\":\"int32\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"differentField\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"oracleId\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"uint8[32]\",\"name\":\"oracleIds\",\"type\":\"uint8[32]\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"Account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"Accounts\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"int192\",\"name\":\"bigField\",\"type\":\"int192\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"FixedBytes\",\"type\":\"bytes2\"},{\"components\":[{\"internalType\":\"int64\",\"name\":\"IntVal\",\"type\":\"int64\"},{\"internalType\":\"string\",\"name\":\"S\",\"type\":\"string\"}],\"internalType\":\"structInnerTestStruct\",\"name\":\"Inner\",\"type\":\"tuple\"}],\"indexed\":false,\"internalType\":\"structMidLevelTestStruct\",\"name\":\"nestedStruct\",\"type\":\"tuple\"}],\"name\":\"Triggered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"string\",\"name\":\"fieldHash\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"field\",\"type\":\"string\"}],\"name\":\"TriggeredEventWithDynamicTopic\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"int32\",\"name\":\"field1\",\"type\":\"int32\"},{\"indexed\":true,\"internalType\":\"int32\",\"name\":\"field2\",\"type\":\"int32\"},{\"indexed\":true,\"internalType\":\"int32\",\"name\":\"field3\",\"type\":\"int32\"}],\"name\":\"TriggeredWithFourTopics\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"string\",\"name\":\"field1\",\"type\":\"string\"},{\"indexed\":true,\"internalType\":\"uint8[32]\",\"name\":\"field2\",\"type\":\"uint8[32]\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"field3\",\"type\":\"bytes32\"}],\"name\":\"TriggeredWithFourTopicsWithHashed\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"int32\",\"name\":\"field\",\"type\":\"int32\"},{\"internalType\":\"string\",\"name\":\"differentField\",\"type\":\"string\"},{\"internalType\":\"uint8\",\"name\":\"oracleId\",\"type\":\"uint8\"},{\"internalType\":\"uint8[32]\",\"name\":\"oracleIds\",\"type\":\"uint8[32]\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"accounts\",\"type\":\"address[]\"},{\"internalType\":\"int192\",\"name\":\"bigField\",\"type\":\"int192\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"FixedBytes\",\"type\":\"bytes2\"},{\"components\":[{\"internalType\":\"int64\",\"name\":\"IntVal\",\"type\":\"int64\"},{\"internalType\":\"string\",\"name\":\"S\",\"type\":\"string\"}],\"internalType\":\"structInnerTestStruct\",\"name\":\"Inner\",\"type\":\"tuple\"}],\"internalType\":\"structMidLevelTestStruct\",\"name\":\"nestedStruct\",\"type\":\"tuple\"}],\"name\":\"addTestStruct\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAlterablePrimitiveValue\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDifferentPrimitiveValue\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"i\",\"type\":\"uint256\"}],\"name\":\"getElementAtIndex\",\"outputs\":[{\"components\":[{\"internalType\":\"int32\",\"name\":\"Field\",\"type\":\"int32\"},{\"internalType\":\"string\",\"name\":\"DifferentField\",\"type\":\"string\"},{\"internalType\":\"uint8\",\"name\":\"OracleId\",\"type\":\"uint8\"},{\"internalType\":\"uint8[32]\",\"name\":\"OracleIds\",\"type\":\"uint8[32]\"},{\"internalType\":\"address\",\"name\":\"Account\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"Accounts\",\"type\":\"address[]\"},{\"internalType\":\"int192\",\"name\":\"BigField\",\"type\":\"int192\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"FixedBytes\",\"type\":\"bytes2\"},{\"components\":[{\"internalType\":\"int64\",\"name\":\"IntVal\",\"type\":\"int64\"},{\"internalType\":\"string\",\"name\":\"S\",\"type\":\"string\"}],\"internalType\":\"structInnerTestStruct\",\"name\":\"Inner\",\"type\":\"tuple\"}],\"internalType\":\"structMidLevelTestStruct\",\"name\":\"NestedStruct\",\"type\":\"tuple\"}],\"internalType\":\"structTestStruct\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getPrimitiveValue\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getSliceValue\",\"outputs\":[{\"internalType\":\"uint64[]\",\"name\":\"\",\"type\":\"uint64[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int32\",\"name\":\"field\",\"type\":\"int32\"},{\"internalType\":\"string\",\"name\":\"differentField\",\"type\":\"string\"},{\"internalType\":\"uint8\",\"name\":\"oracleId\",\"type\":\"uint8\"},{\"internalType\":\"uint8[32]\",\"name\":\"oracleIds\",\"type\":\"uint8[32]\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"accounts\",\"type\":\"address[]\"},{\"internalType\":\"int192\",\"name\":\"bigField\",\"type\":\"int192\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"FixedBytes\",\"type\":\"bytes2\"},{\"components\":[{\"internalType\":\"int64\",\"name\":\"IntVal\",\"type\":\"int64\"},{\"internalType\":\"string\",\"name\":\"S\",\"type\":\"string\"}],\"internalType\":\"structInnerTestStruct\",\"name\":\"Inner\",\"type\":\"tuple\"}],\"internalType\":\"structMidLevelTestStruct\",\"name\":\"nestedStruct\",\"type\":\"tuple\"}],\"name\":\"returnSeen\",\"outputs\":[{\"components\":[{\"internalType\":\"int32\",\"name\":\"Field\",\"type\":\"int32\"},{\"internalType\":\"string\",\"name\":\"DifferentField\",\"type\":\"string\"},{\"internalType\":\"uint8\",\"name\":\"OracleId\",\"type\":\"uint8\"},{\"internalType\":\"uint8[32]\",\"name\":\"OracleIds\",\"type\":\"uint8[32]\"},{\"internalType\":\"address\",\"name\":\"Account\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"Accounts\",\"type\":\"address[]\"},{\"internalType\":\"int192\",\"name\":\"BigField\",\"type\":\"int192\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"FixedBytes\",\"type\":\"bytes2\"},{\"components\":[{\"internalType\":\"int64\",\"name\":\"IntVal\",\"type\":\"int64\"},{\"internalType\":\"string\",\"name\":\"S\",\"type\":\"string\"}],\"internalType\":\"structInnerTestStruct\",\"name\":\"Inner\",\"type\":\"tuple\"}],\"internalType\":\"structMidLevelTestStruct\",\"name\":\"NestedStruct\",\"type\":\"tuple\"}],\"internalType\":\"structTestStruct\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"value\",\"type\":\"uint64\"}],\"name\":\"setAlterablePrimitiveValue\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int32\",\"name\":\"field\",\"type\":\"int32\"},{\"internalType\":\"string\",\"name\":\"differentField\",\"type\":\"string\"},{\"internalType\":\"uint8\",\"name\":\"oracleId\",\"type\":\"uint8\"},{\"internalType\":\"uint8[32]\",\"name\":\"oracleIds\",\"type\":\"uint8[32]\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"accounts\",\"type\":\"address[]\"},{\"internalType\":\"int192\",\"name\":\"bigField\",\"type\":\"int192\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"FixedBytes\",\"type\":\"bytes2\"},{\"components\":[{\"internalType\":\"int64\",\"name\":\"IntVal\",\"type\":\"int64\"},{\"internalType\":\"string\",\"name\":\"S\",\"type\":\"string\"}],\"internalType\":\"structInnerTestStruct\",\"name\":\"Inner\",\"type\":\"tuple\"}],\"internalType\":\"structMidLevelTestStruct\",\"name\":\"nestedStruct\",\"type\":\"tuple\"}],\"name\":\"triggerEvent\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"field\",\"type\":\"string\"}],\"name\":\"triggerEventWithDynamicTopic\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int32\",\"name\":\"field1\",\"type\":\"int32\"},{\"internalType\":\"int32\",\"name\":\"field2\",\"type\":\"int32\"},{\"internalType\":\"int32\",\"name\":\"field3\",\"type\":\"int32\"}],\"name\":\"triggerWithFourTopics\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"field1\",\"type\":\"string\"},{\"internalType\":\"uint8[32]\",\"name\":\"field2\",\"type\":\"uint8[32]\"},{\"internalType\":\"bytes32\",\"name\":\"field3\",\"type\":\"bytes32\"}],\"name\":\"triggerWithFourTopicsWithHashed\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b50600180548082018255600082905260048082047fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6908101805460086003958616810261010090810a8088026001600160401b0391820219909416939093179093558654808801909755848704909301805496909516909202900a91820291021990921691909117905561199c806100a96000396000f3fe608060405234801561001057600080fd5b50600436106100d45760003560e01c8063a90e199811610081578063ef4e1ced1161005b578063ef4e1ced146101de578063f6f871c8146101e5578063fbe9fbf6146101f857600080fd5b8063a90e19981461019b578063ab5e0b38146101ae578063dbfd7332146101cb57600080fd5b8063679004a4116100b2578063679004a41461012a5780636c9a43b61461013f5780637f002d671461018857600080fd5b80632c45576f146100d95780633272b66c1461010257806349eac2ac14610117575b600080fd5b6100ec6100e7366004610ca3565b61020a565b6040516100f99190610e0c565b60405180910390f35b610115610110366004610f4b565b6104e5565b005b610115610125366004611060565b61053a565b61013261083d565b6040516100f99190611152565b61011561014d3660046111a0565b600280547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff92909216919091179055565b610115610196366004611060565b6108c9565b6101156101a93660046112d4565b610920565b6107c65b60405167ffffffffffffffff90911681526020016100f9565b6101156101d9366004611389565b61097a565b60036101b2565b6100ec6101f3366004611060565b6109b7565b60025467ffffffffffffffff166101b2565b610212610ac0565b600061021f6001846113cc565b8154811061022f5761022f611406565b6000918252602091829020604080516101008101909152600a90920201805460030b8252600181018054929391929184019161026a90611435565b80601f016020809104026020016040519081016040528092919081815260200182805461029690611435565b80156102e35780601f106102b8576101008083540402835291602001916102e3565b820191906000526020600020905b8154815290600101906020018083116102c657829003601f168201915b5050509183525050600282015460ff166020808301919091526040805161040081018083529190930192916003850191826000855b825461010083900a900460ff1681526020600192830181810494850194909303909202910180841161031857505050928452505050600482015473ffffffffffffffffffffffffffffffffffffffff1660208083019190915260058301805460408051828502810185018252828152940193928301828280156103d157602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116103a6575b5050509183525050600682015460170b6020808301919091526040805180820182526007808601805460f01b7fffff0000000000000000000000000000000000000000000000000000000000001683528351808501855260088801805490930b8152600988018054959097019693959194868301949193928401919061045690611435565b80601f016020809104026020016040519081016040528092919081815260200182805461048290611435565b80156104cf5780601f106104a4576101008083540402835291602001916104cf565b820191906000526020600020905b8154815290600101906020018083116104b257829003601f168201915b5050509190925250505090525090525092915050565b81816040516104f5929190611482565b60405180910390207f3d969732b1bbbb9f1d7eb9f3f14e4cb50a74d950b3ef916a397b85dfbab93c67838360405161052e9291906114db565b60405180910390a25050565b60006040518061010001604052808c60030b81526020018b8b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525060ff8a166020808301919091526040805161040081810183529190930192918b9183908390808284376000920191909152505050815273ffffffffffffffffffffffffffffffffffffffff8816602080830191909152604080518883028181018401835289825291909301929189918991829190850190849080828437600092019190915250505090825250601785900b602082015260400161062c84611531565b905281546001808201845560009384526020938490208351600a9093020180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff909316929092178255928201519192909190820190610692908261161e565b5060408201516002820180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff90921691909117905560608201516106e09060038301906020610b0f565b5060808201516004820180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90921691909117905560a08201518051610747916005840191602090910190610ba2565b5060c08201516006820180547fffffffffffffffff0000000000000000000000000000000000000000000000001677ffffffffffffffffffffffffffffffffffffffffffffffff90921691909117905560e082015180516007830180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001660f09290921c91909117815560208083015180516008860180547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff90921691909117815591810151909190600986019061082a908261161e565b5050505050505050505050505050505050565b606060018054806020026020016040519081016040528092919081815260200182805480156108bf57602002820191906000526020600020906000905b82829054906101000a900467ffffffffffffffff1667ffffffffffffffff168152602001906008019060208260070104928301926001038202915080841161087a5790505b5050505050905090565b8960030b7f7188419dcd8b51877b71766f075f3626586c0ff190e7d056aa65ce9acb649a3d8a8a8a8a8a8a8a8a8a60405161090c9998979695949392919061187d565b60405180910390a250505050505050505050565b808260405161092f9190611937565b6040518091039020846040516109459190611973565b604051908190038120907f7220e4dbe4e9d0ed5f71acd022bc89c26748ac6784f2c548bc17bb8e52af34b090600090a4505050565b8060030b8260030b8460030b7f91c80dc390f3d041b3a04b0099b19634499541ea26972250986ee4b24a12fac560405160405180910390a4505050565b6109bf610ac0565b6040518061010001604052808c60030b81526020018b8b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525060ff8a166020808301919091526040805161040081810183529190930192918b9183908390808284376000920191909152505050815273ffffffffffffffffffffffffffffffffffffffff8816602080830191909152604080518883028181018401835289825291909301929189918991829190850190849080828437600092019190915250505090825250601785900b6020820152604001610aaf84611531565b90529b9a5050505050505050505050565b6040805161010081018252600080825260606020830181905292820152908101610ae8610c1c565b8152600060208201819052606060408301819052820152608001610b0a610c3b565b905290565b600183019183908215610b925791602002820160005b83821115610b6357835183826101000a81548160ff021916908360ff1602179055509260200192600101602081600001049283019260010302610b25565b8015610b905782816101000a81549060ff0219169055600101602081600001049283019260010302610b63565b505b50610b9e929150610c8e565b5090565b828054828255906000526020600020908101928215610b92579160200282015b82811115610b9257825182547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909116178255602090920191600190910190610bc2565b6040518061040001604052806020906020820280368337509192915050565b604051806040016040528060007dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001610b0a6040518060400160405280600060070b8152602001606081525090565b5b80821115610b9e5760008155600101610c8f565b600060208284031215610cb557600080fd5b5035919050565b60005b83811015610cd7578181015183820152602001610cbf565b50506000910152565b60008151808452610cf8816020860160208601610cbc565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b8060005b6020808210610d3d5750610d54565b825160ff1685529384019390910190600101610d2e565b50505050565b600081518084526020808501945080840160005b83811015610da057815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101610d6e565b509495945050505050565b7fffff00000000000000000000000000000000000000000000000000000000000081511682526000602082015160406020850152805160070b60408501526020810151905060406060850152610e046080850182610ce0565b949350505050565b60208152610e2060208201835160030b9052565b600060208301516104e0806040850152610e3e610500850183610ce0565b91506040850151610e54606086018260ff169052565b506060850151610e676080860182610d2a565b50608085015173ffffffffffffffffffffffffffffffffffffffff1661048085015260a08501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe085840381016104a0870152610ec48483610d5a565b935060c08701519150610edd6104c087018360170b9052565b60e0870151915080868503018387015250610ef88382610dab565b9695505050505050565b60008083601f840112610f1457600080fd5b50813567ffffffffffffffff811115610f2c57600080fd5b602083019150836020828501011115610f4457600080fd5b9250929050565b60008060208385031215610f5e57600080fd5b823567ffffffffffffffff811115610f7557600080fd5b610f8185828601610f02565b90969095509350505050565b8035600381900b8114610f9f57600080fd5b919050565b803560ff81168114610f9f57600080fd5b806104008101831015610fc757600080fd5b92915050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610f9f57600080fd5b60008083601f84011261100357600080fd5b50813567ffffffffffffffff81111561101b57600080fd5b6020830191508360208260051b8501011115610f4457600080fd5b8035601781900b8114610f9f57600080fd5b60006040828403121561105a57600080fd5b50919050565b6000806000806000806000806000806104e08b8d03121561108057600080fd5b6110898b610f8d565b995060208b013567ffffffffffffffff808211156110a657600080fd5b6110b28e838f01610f02565b909b5099508991506110c660408e01610fa4565b98506110d58e60608f01610fb5565b97506110e46104608e01610fcd565b96506104808d01359150808211156110fb57600080fd5b6111078e838f01610ff1565b909650945084915061111c6104a08e01611036565b93506104c08d013591508082111561113357600080fd5b506111408d828e01611048565b9150509295989b9194979a5092959850565b6020808252825182820181905260009190848201906040850190845b8181101561119457835167ffffffffffffffff168352928401929184019160010161116e565b50909695505050505050565b6000602082840312156111b257600080fd5b813567ffffffffffffffff811681146111ca57600080fd5b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715611223576112236111d1565b60405290565b600082601f83011261123a57600080fd5b813567ffffffffffffffff80821115611255576112556111d1565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561129b5761129b6111d1565b816040528381528660208588010111156112b457600080fd5b836020870160208301376000602085830101528094505050505092915050565b600080600061044084860312156112ea57600080fd5b833567ffffffffffffffff8082111561130257600080fd5b61130e87838801611229565b94506020915086603f87011261132357600080fd5b6040516104008101818110838211171561133f5761133f6111d1565b60405290508061042087018881111561135757600080fd5b8388015b818110156113795761136c81610fa4565b845292840192840161135b565b5095989097509435955050505050565b60008060006060848603121561139e57600080fd5b6113a784610f8d565b92506113b560208501610f8d565b91506113c360408501610f8d565b90509250925092565b81810381811115610fc7577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600181811c9082168061144957607f821691505b60208210810361105a577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b8183823760009101908152919050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b602081526000610e04602083018486611492565b80357fffff00000000000000000000000000000000000000000000000000000000000081168114610f9f57600080fd5b8035600781900b8114610f9f57600080fd5b60006040823603121561154357600080fd5b61154b611200565b611554836114ef565b8152602083013567ffffffffffffffff8082111561157157600080fd5b81850191506040823603121561158657600080fd5b61158e611200565b6115978361151f565b81526020830135828111156115ab57600080fd5b6115b736828601611229565b60208301525080602085015250505080915050919050565b601f82111561161957600081815260208120601f850160051c810160208610156115f65750805b601f850160051c820191505b8181101561161557828155600101611602565b5050505b505050565b815167ffffffffffffffff811115611638576116386111d1565b61164c816116468454611435565b846115cf565b602080601f83116001811461169f57600084156116695750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555611615565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b828110156116ec578886015182559484019460019091019084016116cd565b508582101561172857878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b8183526000602080850194508260005b85811015610da05773ffffffffffffffffffffffffffffffffffffffff61176e83610fcd565b1687529582019590820190600101611748565b7fffff0000000000000000000000000000000000000000000000000000000000006117ab826114ef565b168252600060208201357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18336030181126117e557600080fd5b6040602085015282016117f78161151f565b60070b604085015260208101357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe182360301811261183457600080fd5b0160208101903567ffffffffffffffff81111561185057600080fd5b80360382131561185f57600080fd5b60406060860152611874608086018284611492565b95945050505050565b60006104c08083526118928184018c8e611492565b9050602060ff808c1682860152604085018b60005b848110156118cc57836118b983610fa4565b16835291840191908401906001016118a7565b505050505073ffffffffffffffffffffffffffffffffffffffff8816610440840152828103610460840152611902818789611738565b905061191461048084018660170b9052565b8281036104a08401526119278185611781565b9c9b505050505050505050505050565b60008183825b602080821061194c5750611963565b825160ff168452928301929091019060010161193d565b5050506104008201905092915050565b60008251611985818460208701610cbc565b919091019291505056fea164736f6c6343000813000a", } var ChainReaderTesterABI = ChainReaderTesterMetaData.ABI @@ -384,6 +384,18 @@ func (_ChainReaderTester *ChainReaderTesterTransactorSession) TriggerWithFourTop return _ChainReaderTester.Contract.TriggerWithFourTopics(&_ChainReaderTester.TransactOpts, field1, field2, field3) } +func (_ChainReaderTester *ChainReaderTesterTransactor) TriggerWithFourTopicsWithHashed(opts *bind.TransactOpts, field1 string, field2 [32]uint8, field3 [32]byte) (*types.Transaction, error) { + return _ChainReaderTester.contract.Transact(opts, "triggerWithFourTopicsWithHashed", field1, field2, field3) +} + +func (_ChainReaderTester *ChainReaderTesterSession) TriggerWithFourTopicsWithHashed(field1 string, field2 [32]uint8, field3 [32]byte) (*types.Transaction, error) { + return _ChainReaderTester.Contract.TriggerWithFourTopicsWithHashed(&_ChainReaderTester.TransactOpts, field1, field2, field3) +} + +func (_ChainReaderTester *ChainReaderTesterTransactorSession) TriggerWithFourTopicsWithHashed(field1 string, field2 [32]uint8, field3 [32]byte) (*types.Transaction, error) { + return _ChainReaderTester.Contract.TriggerWithFourTopicsWithHashed(&_ChainReaderTester.TransactOpts, field1, field2, field3) +} + type ChainReaderTesterTriggeredIterator struct { Event *ChainReaderTesterTriggered @@ -791,6 +803,151 @@ func (_ChainReaderTester *ChainReaderTesterFilterer) ParseTriggeredWithFourTopic return event, nil } +type ChainReaderTesterTriggeredWithFourTopicsWithHashedIterator struct { + Event *ChainReaderTesterTriggeredWithFourTopicsWithHashed + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *ChainReaderTesterTriggeredWithFourTopicsWithHashedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(ChainReaderTesterTriggeredWithFourTopicsWithHashed) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(ChainReaderTesterTriggeredWithFourTopicsWithHashed) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *ChainReaderTesterTriggeredWithFourTopicsWithHashedIterator) Error() error { + return it.fail +} + +func (it *ChainReaderTesterTriggeredWithFourTopicsWithHashedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type ChainReaderTesterTriggeredWithFourTopicsWithHashed struct { + Field1 common.Hash + Field2 [32]uint8 + Field3 [32]byte + Raw types.Log +} + +func (_ChainReaderTester *ChainReaderTesterFilterer) FilterTriggeredWithFourTopicsWithHashed(opts *bind.FilterOpts, field1 []string, field2 [][32]uint8, field3 [][32]byte) (*ChainReaderTesterTriggeredWithFourTopicsWithHashedIterator, error) { + + var field1Rule []interface{} + for _, field1Item := range field1 { + field1Rule = append(field1Rule, field1Item) + } + var field2Rule []interface{} + for _, field2Item := range field2 { + field2Rule = append(field2Rule, field2Item) + } + var field3Rule []interface{} + for _, field3Item := range field3 { + field3Rule = append(field3Rule, field3Item) + } + + logs, sub, err := _ChainReaderTester.contract.FilterLogs(opts, "TriggeredWithFourTopicsWithHashed", field1Rule, field2Rule, field3Rule) + if err != nil { + return nil, err + } + return &ChainReaderTesterTriggeredWithFourTopicsWithHashedIterator{contract: _ChainReaderTester.contract, event: "TriggeredWithFourTopicsWithHashed", logs: logs, sub: sub}, nil +} + +func (_ChainReaderTester *ChainReaderTesterFilterer) WatchTriggeredWithFourTopicsWithHashed(opts *bind.WatchOpts, sink chan<- *ChainReaderTesterTriggeredWithFourTopicsWithHashed, field1 []string, field2 [][32]uint8, field3 [][32]byte) (event.Subscription, error) { + + var field1Rule []interface{} + for _, field1Item := range field1 { + field1Rule = append(field1Rule, field1Item) + } + var field2Rule []interface{} + for _, field2Item := range field2 { + field2Rule = append(field2Rule, field2Item) + } + var field3Rule []interface{} + for _, field3Item := range field3 { + field3Rule = append(field3Rule, field3Item) + } + + logs, sub, err := _ChainReaderTester.contract.WatchLogs(opts, "TriggeredWithFourTopicsWithHashed", field1Rule, field2Rule, field3Rule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(ChainReaderTesterTriggeredWithFourTopicsWithHashed) + if err := _ChainReaderTester.contract.UnpackLog(event, "TriggeredWithFourTopicsWithHashed", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_ChainReaderTester *ChainReaderTesterFilterer) ParseTriggeredWithFourTopicsWithHashed(log types.Log) (*ChainReaderTesterTriggeredWithFourTopicsWithHashed, error) { + event := new(ChainReaderTesterTriggeredWithFourTopicsWithHashed) + if err := _ChainReaderTester.contract.UnpackLog(event, "TriggeredWithFourTopicsWithHashed", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + func (_ChainReaderTester *ChainReaderTester) ParseLog(log types.Log) (generated.AbigenLog, error) { switch log.Topics[0] { case _ChainReaderTester.abi.Events["Triggered"].ID: @@ -799,6 +956,8 @@ func (_ChainReaderTester *ChainReaderTester) ParseLog(log types.Log) (generated. return _ChainReaderTester.ParseTriggeredEventWithDynamicTopic(log) case _ChainReaderTester.abi.Events["TriggeredWithFourTopics"].ID: return _ChainReaderTester.ParseTriggeredWithFourTopics(log) + case _ChainReaderTester.abi.Events["TriggeredWithFourTopicsWithHashed"].ID: + return _ChainReaderTester.ParseTriggeredWithFourTopicsWithHashed(log) default: return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) @@ -817,6 +976,10 @@ func (ChainReaderTesterTriggeredWithFourTopics) Topic() common.Hash { return common.HexToHash("0x91c80dc390f3d041b3a04b0099b19634499541ea26972250986ee4b24a12fac5") } +func (ChainReaderTesterTriggeredWithFourTopicsWithHashed) Topic() common.Hash { + return common.HexToHash("0x7220e4dbe4e9d0ed5f71acd022bc89c26748ac6784f2c548bc17bb8e52af34b0") +} + func (_ChainReaderTester *ChainReaderTester) Address() common.Address { return _ChainReaderTester.address } @@ -844,6 +1007,8 @@ type ChainReaderTesterInterface interface { TriggerWithFourTopics(opts *bind.TransactOpts, field1 int32, field2 int32, field3 int32) (*types.Transaction, error) + TriggerWithFourTopicsWithHashed(opts *bind.TransactOpts, field1 string, field2 [32]uint8, field3 [32]byte) (*types.Transaction, error) + FilterTriggered(opts *bind.FilterOpts, field []int32) (*ChainReaderTesterTriggeredIterator, error) WatchTriggered(opts *bind.WatchOpts, sink chan<- *ChainReaderTesterTriggered, field []int32) (event.Subscription, error) @@ -862,6 +1027,12 @@ type ChainReaderTesterInterface interface { ParseTriggeredWithFourTopics(log types.Log) (*ChainReaderTesterTriggeredWithFourTopics, error) + FilterTriggeredWithFourTopicsWithHashed(opts *bind.FilterOpts, field1 []string, field2 [][32]uint8, field3 [][32]byte) (*ChainReaderTesterTriggeredWithFourTopicsWithHashedIterator, error) + + WatchTriggeredWithFourTopicsWithHashed(opts *bind.WatchOpts, sink chan<- *ChainReaderTesterTriggeredWithFourTopicsWithHashed, field1 []string, field2 [][32]uint8, field3 [][32]byte) (event.Subscription, error) + + ParseTriggeredWithFourTopicsWithHashed(log types.Log) (*ChainReaderTesterTriggeredWithFourTopicsWithHashed, error) + ParseLog(log types.Log) (generated.AbigenLog, 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 index 23cec8fb9c..e432d912cc 100644 --- a/core/gethwrappers/generated/i_chain_module/i_chain_module.go +++ b/core/gethwrappers/generated/i_chain_module/i_chain_module.go @@ -29,7 +29,7 @@ var ( ) 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\"}]", + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"name\":\"blockHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"blockNumber\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"dataSize\",\"type\":\"uint256\"}],\"name\":\"getCurrentL1Fee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"l1Fee\",\"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\":\"maxL1Fee\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", } var IChainModuleABI = IChainModuleMetaData.ABI @@ -150,9 +150,9 @@ func (_IChainModule *IChainModuleTransactorRaw) Transact(opts *bind.TransactOpts return _IChainModule.Contract.contract.Transact(opts, method, params...) } -func (_IChainModule *IChainModuleCaller) BlockHash(opts *bind.CallOpts, arg0 *big.Int) ([32]byte, error) { +func (_IChainModule *IChainModuleCaller) BlockHash(opts *bind.CallOpts, blockNumber *big.Int) ([32]byte, error) { var out []interface{} - err := _IChainModule.contract.Call(opts, &out, "blockHash", arg0) + err := _IChainModule.contract.Call(opts, &out, "blockHash", blockNumber) if err != nil { return *new([32]byte), err @@ -164,12 +164,12 @@ func (_IChainModule *IChainModuleCaller) BlockHash(opts *bind.CallOpts, arg0 *bi } -func (_IChainModule *IChainModuleSession) BlockHash(arg0 *big.Int) ([32]byte, error) { - return _IChainModule.Contract.BlockHash(&_IChainModule.CallOpts, arg0) +func (_IChainModule *IChainModuleSession) BlockHash(blockNumber *big.Int) ([32]byte, error) { + return _IChainModule.Contract.BlockHash(&_IChainModule.CallOpts, blockNumber) } -func (_IChainModule *IChainModuleCallerSession) BlockHash(arg0 *big.Int) ([32]byte, error) { - return _IChainModule.Contract.BlockHash(&_IChainModule.CallOpts, arg0) +func (_IChainModule *IChainModuleCallerSession) BlockHash(blockNumber *big.Int) ([32]byte, error) { + return _IChainModule.Contract.BlockHash(&_IChainModule.CallOpts, blockNumber) } func (_IChainModule *IChainModuleCaller) BlockNumber(opts *bind.CallOpts) (*big.Int, error) { @@ -194,9 +194,9 @@ func (_IChainModule *IChainModuleCallerSession) BlockNumber() (*big.Int, error) return _IChainModule.Contract.BlockNumber(&_IChainModule.CallOpts) } -func (_IChainModule *IChainModuleCaller) GetCurrentL1Fee(opts *bind.CallOpts) (*big.Int, error) { +func (_IChainModule *IChainModuleCaller) GetCurrentL1Fee(opts *bind.CallOpts, dataSize *big.Int) (*big.Int, error) { var out []interface{} - err := _IChainModule.contract.Call(opts, &out, "getCurrentL1Fee") + err := _IChainModule.contract.Call(opts, &out, "getCurrentL1Fee", dataSize) if err != nil { return *new(*big.Int), err @@ -208,12 +208,12 @@ func (_IChainModule *IChainModuleCaller) GetCurrentL1Fee(opts *bind.CallOpts) (* } -func (_IChainModule *IChainModuleSession) GetCurrentL1Fee() (*big.Int, error) { - return _IChainModule.Contract.GetCurrentL1Fee(&_IChainModule.CallOpts) +func (_IChainModule *IChainModuleSession) GetCurrentL1Fee(dataSize *big.Int) (*big.Int, error) { + return _IChainModule.Contract.GetCurrentL1Fee(&_IChainModule.CallOpts, dataSize) } -func (_IChainModule *IChainModuleCallerSession) GetCurrentL1Fee() (*big.Int, error) { - return _IChainModule.Contract.GetCurrentL1Fee(&_IChainModule.CallOpts) +func (_IChainModule *IChainModuleCallerSession) GetCurrentL1Fee(dataSize *big.Int) (*big.Int, error) { + return _IChainModule.Contract.GetCurrentL1Fee(&_IChainModule.CallOpts, dataSize) } func (_IChainModule *IChainModuleCaller) GetGasOverhead(opts *bind.CallOpts) (GetGasOverhead, @@ -278,11 +278,11 @@ func (_IChainModule *IChainModule) Address() common.Address { } type IChainModuleInterface interface { - BlockHash(opts *bind.CallOpts, arg0 *big.Int) ([32]byte, error) + BlockHash(opts *bind.CallOpts, blockNumber *big.Int) ([32]byte, error) BlockNumber(opts *bind.CallOpts) (*big.Int, error) - GetCurrentL1Fee(opts *bind.CallOpts) (*big.Int, error) + GetCurrentL1Fee(opts *bind.CallOpts, dataSize *big.Int) (*big.Int, error) GetGasOverhead(opts *bind.CallOpts) (GetGasOverhead, diff --git a/core/gethwrappers/generated/optimism_module/optimism_module.go b/core/gethwrappers/generated/optimism_module/optimism_module.go index 009ab6d8cf..89b6a758fc 100644 --- a/core/gethwrappers/generated/optimism_module/optimism_module.go +++ b/core/gethwrappers/generated/optimism_module/optimism_module.go @@ -29,8 +29,8 @@ var ( ) 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: "0x608060405234801561001057600080fd5b506104d1806100206000396000f3fe608060405234801561001057600080fd5b50600436106100675760003560e01c806357e871e71161005057806357e871e71461009a57806385df51fd146100a0578063de9ee35e146100b357600080fd5b8063125441401461006c57806318b8f61314610092575b600080fd5b61007f61007a3660046102e9565b6100ca565b6040519081526020015b60405180910390f35b61007f6101eb565b4361007f565b61007f6100ae3660046102e9565b6102bc565b6040805161ea60815261010e602082015201610089565b6000806100d8836004610331565b67ffffffffffffffff8111156100f0576100f061034e565b6040519080825280601f01601f19166020018201604052801561011a576020820181803683370190505b50905073420000000000000000000000000000000000000f73ffffffffffffffffffffffffffffffffffffffff166349948e0e82604051806080016040528060508152602001610475605091396040516020016101789291906103a1565b6040516020818303038152906040526040518263ffffffff1660e01b81526004016101a391906103d0565b602060405180830381865afa1580156101c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101e49190610421565b9392505050565b600073420000000000000000000000000000000000000f73ffffffffffffffffffffffffffffffffffffffff166349948e0e6000366040518060800160405280605081526020016104756050913960405160200161024b9392919061043a565b6040516020818303038152906040526040518263ffffffff1660e01b815260040161027691906103d0565b602060405180830381865afa158015610293573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102b79190610421565b905090565b600043821015806102d757506101006102d58343610461565b115b156102e457506000919050565b504090565b6000602082840312156102fb57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808202811582820484141761034857610348610302565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60005b83811015610398578181015183820152602001610380565b50506000910152565b600083516103b381846020880161037d565b8351908301906103c781836020880161037d565b01949350505050565b60208152600082518060208401526103ef81604085016020870161037d565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b60006020828403121561043357600080fd5b5051919050565b82848237600083820160008152835161045781836020880161037d565b0195945050505050565b818103818111156103485761034861030256feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa164736f6c6343000813000a", + 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\":[{\"internalType\":\"uint256\",\"name\":\"dataSize\",\"type\":\"uint256\"}],\"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: "0x608060405234801561001057600080fd5b506103dc806100206000396000f3fe608060405234801561001057600080fd5b50600436106100675760003560e01c80637810d12a116100505780637810d12a1461006c57806385df51fd14610098578063de9ee35e146100ab57600080fd5b8063125441401461006c57806357e871e714610092575b600080fd5b61007f61007a366004610221565b6100c2565b6040519081526020015b60405180910390f35b4361007f565b61007f6100a6366004610221565b6100d3565b6040805161ea60815261010e602082015201610089565b60006100cd82610100565b92915050565b600043821015806100ee57506101006100ec8343610269565b115b156100fb57506000919050565b504090565b60008061010e83600461027c565b67ffffffffffffffff81111561012657610126610293565b6040519080825280601f01601f191660200182016040528015610150576020820181803683370190505b50905073420000000000000000000000000000000000000f73ffffffffffffffffffffffffffffffffffffffff166349948e0e82604051806080016040528060508152602001610380605091396040516020016101ae9291906102e6565b6040516020818303038152906040526040518263ffffffff1660e01b81526004016101d99190610315565b602060405180830381865afa1580156101f6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061021a9190610366565b9392505050565b60006020828403121561023357600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156100cd576100cd61023a565b80820281158282048414176100cd576100cd61023a565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60005b838110156102dd5781810151838201526020016102c5565b50506000910152565b600083516102f88184602088016102c2565b83519083019061030c8183602088016102c2565b01949350505050565b60208152600082518060208401526103348160408501602087016102c2565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b60006020828403121561037857600080fd5b505191905056feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa164736f6c6343000813000a", } var OptimismModuleABI = OptimismModuleMetaData.ABI @@ -213,9 +213,9 @@ func (_OptimismModule *OptimismModuleCallerSession) BlockNumber() (*big.Int, err return _OptimismModule.Contract.BlockNumber(&_OptimismModule.CallOpts) } -func (_OptimismModule *OptimismModuleCaller) GetCurrentL1Fee(opts *bind.CallOpts) (*big.Int, error) { +func (_OptimismModule *OptimismModuleCaller) GetCurrentL1Fee(opts *bind.CallOpts, dataSize *big.Int) (*big.Int, error) { var out []interface{} - err := _OptimismModule.contract.Call(opts, &out, "getCurrentL1Fee") + err := _OptimismModule.contract.Call(opts, &out, "getCurrentL1Fee", dataSize) if err != nil { return *new(*big.Int), err @@ -227,12 +227,12 @@ func (_OptimismModule *OptimismModuleCaller) GetCurrentL1Fee(opts *bind.CallOpts } -func (_OptimismModule *OptimismModuleSession) GetCurrentL1Fee() (*big.Int, error) { - return _OptimismModule.Contract.GetCurrentL1Fee(&_OptimismModule.CallOpts) +func (_OptimismModule *OptimismModuleSession) GetCurrentL1Fee(dataSize *big.Int) (*big.Int, error) { + return _OptimismModule.Contract.GetCurrentL1Fee(&_OptimismModule.CallOpts, dataSize) } -func (_OptimismModule *OptimismModuleCallerSession) GetCurrentL1Fee() (*big.Int, error) { - return _OptimismModule.Contract.GetCurrentL1Fee(&_OptimismModule.CallOpts) +func (_OptimismModule *OptimismModuleCallerSession) GetCurrentL1Fee(dataSize *big.Int) (*big.Int, error) { + return _OptimismModule.Contract.GetCurrentL1Fee(&_OptimismModule.CallOpts, dataSize) } func (_OptimismModule *OptimismModuleCaller) GetGasOverhead(opts *bind.CallOpts) (GetGasOverhead, @@ -301,7 +301,7 @@ type OptimismModuleInterface interface { BlockNumber(opts *bind.CallOpts) (*big.Int, error) - GetCurrentL1Fee(opts *bind.CallOpts) (*big.Int, error) + GetCurrentL1Fee(opts *bind.CallOpts, dataSize *big.Int) (*big.Int, error) GetGasOverhead(opts *bind.CallOpts) (GetGasOverhead, diff --git a/core/gethwrappers/generated/optimism_module_v2/optimism_module_v2.go b/core/gethwrappers/generated/optimism_module_v2/optimism_module_v2.go new file mode 100644 index 0000000000..abad079caa --- /dev/null +++ b/core/gethwrappers/generated/optimism_module_v2/optimism_module_v2.go @@ -0,0 +1,840 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package optimism_module_v2 + +import ( + "errors" + "fmt" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" +) + +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +var OptimismModuleV2MetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"coefficient\",\"type\":\"uint8\"}],\"name\":\"InvalidL1FeeCoefficient\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"coefficient\",\"type\":\"uint8\"}],\"name\":\"L1FeeCoefficientSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"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\":[{\"internalType\":\"uint256\",\"name\":\"dataSize\",\"type\":\"uint256\"}],\"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\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getL1FeeCoefficient\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"coefficient\",\"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\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"coefficient\",\"type\":\"uint8\"}],\"name\":\"setL1FeeCalculation\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60806040526001805460ff60a01b1916601960a21b17905534801561002357600080fd5b50338060008161007a5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b03848116919091179091558116156100aa576100aa816100b2565b50505061015b565b336001600160a01b0382160361010a5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610071565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b61073f8061016a6000396000f3fe608060405234801561001057600080fd5b50600436106100be5760003560e01c80638da5cb5b11610076578063de9ee35e1161005b578063de9ee35e1461016a578063f22559a014610180578063f2fde38b1461019357600080fd5b80638da5cb5b1461011f578063d10a944e1461014757600080fd5b80637810d12a116100a75780637810d12a146100ef57806379ba50971461010257806385df51fd1461010c57600080fd5b806312544140146100c357806357e871e7146100e9575b600080fd5b6100d66100d136600461060c565b6101a6565b6040519081526020015b60405180910390f35b436100d6565b6100d66100fd36600461060c565b6101b7565b61010a6101f6565b005b6100d661011a36600461060c565b6102f8565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100e0565b60015474010000000000000000000000000000000000000000900460ff166100d6565b60408051616d60815260006020820152016100e0565b61010a61018e366004610625565b610325565b61010a6101a136600461064f565b6103f0565b60006101b182610404565b92915050565b600060646101c483610404565b6001546101ec919074010000000000000000000000000000000000000000900460ff166106b4565b6101b191906106cb565b60015473ffffffffffffffffffffffffffffffffffffffff16331461027c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b6000438210158061031357506101006103118343610706565b115b1561032057506000919050565b504090565b61032d610494565b60648160ff161115610370576040517f1a8a06a000000000000000000000000000000000000000000000000000000000815260ff82166004820152602401610273565b600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000060ff8416908102919091179091556040519081527f29ec9e31de0d3fe0208a7ccb792bbc26a854f123146110daa3a77219cb74a5549060200160405180910390a150565b6103f8610494565b61040181610517565b50565b6040517ff1c7a58b0000000000000000000000000000000000000000000000000000000081526004810182905260009073420000000000000000000000000000000000000f9063f1c7a58b90602401602060405180830381865afa158015610470573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101b19190610719565b60005473ffffffffffffffffffffffffffffffffffffffff163314610515576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610273565b565b3373ffffffffffffffffffffffffffffffffffffffff821603610596576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610273565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60006020828403121561061e57600080fd5b5035919050565b60006020828403121561063757600080fd5b813560ff8116811461064857600080fd5b9392505050565b60006020828403121561066157600080fd5b813573ffffffffffffffffffffffffffffffffffffffff8116811461064857600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820281158282048414176101b1576101b1610685565b600082610701577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b818103818111156101b1576101b1610685565b60006020828403121561072b57600080fd5b505191905056fea164736f6c6343000813000a", +} + +var OptimismModuleV2ABI = OptimismModuleV2MetaData.ABI + +var OptimismModuleV2Bin = OptimismModuleV2MetaData.Bin + +func DeployOptimismModuleV2(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *OptimismModuleV2, error) { + parsed, err := OptimismModuleV2MetaData.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(OptimismModuleV2Bin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &OptimismModuleV2{address: address, abi: *parsed, OptimismModuleV2Caller: OptimismModuleV2Caller{contract: contract}, OptimismModuleV2Transactor: OptimismModuleV2Transactor{contract: contract}, OptimismModuleV2Filterer: OptimismModuleV2Filterer{contract: contract}}, nil +} + +type OptimismModuleV2 struct { + address common.Address + abi abi.ABI + OptimismModuleV2Caller + OptimismModuleV2Transactor + OptimismModuleV2Filterer +} + +type OptimismModuleV2Caller struct { + contract *bind.BoundContract +} + +type OptimismModuleV2Transactor struct { + contract *bind.BoundContract +} + +type OptimismModuleV2Filterer struct { + contract *bind.BoundContract +} + +type OptimismModuleV2Session struct { + Contract *OptimismModuleV2 + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type OptimismModuleV2CallerSession struct { + Contract *OptimismModuleV2Caller + CallOpts bind.CallOpts +} + +type OptimismModuleV2TransactorSession struct { + Contract *OptimismModuleV2Transactor + TransactOpts bind.TransactOpts +} + +type OptimismModuleV2Raw struct { + Contract *OptimismModuleV2 +} + +type OptimismModuleV2CallerRaw struct { + Contract *OptimismModuleV2Caller +} + +type OptimismModuleV2TransactorRaw struct { + Contract *OptimismModuleV2Transactor +} + +func NewOptimismModuleV2(address common.Address, backend bind.ContractBackend) (*OptimismModuleV2, error) { + abi, err := abi.JSON(strings.NewReader(OptimismModuleV2ABI)) + if err != nil { + return nil, err + } + contract, err := bindOptimismModuleV2(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &OptimismModuleV2{address: address, abi: abi, OptimismModuleV2Caller: OptimismModuleV2Caller{contract: contract}, OptimismModuleV2Transactor: OptimismModuleV2Transactor{contract: contract}, OptimismModuleV2Filterer: OptimismModuleV2Filterer{contract: contract}}, nil +} + +func NewOptimismModuleV2Caller(address common.Address, caller bind.ContractCaller) (*OptimismModuleV2Caller, error) { + contract, err := bindOptimismModuleV2(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &OptimismModuleV2Caller{contract: contract}, nil +} + +func NewOptimismModuleV2Transactor(address common.Address, transactor bind.ContractTransactor) (*OptimismModuleV2Transactor, error) { + contract, err := bindOptimismModuleV2(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &OptimismModuleV2Transactor{contract: contract}, nil +} + +func NewOptimismModuleV2Filterer(address common.Address, filterer bind.ContractFilterer) (*OptimismModuleV2Filterer, error) { + contract, err := bindOptimismModuleV2(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &OptimismModuleV2Filterer{contract: contract}, nil +} + +func bindOptimismModuleV2(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := OptimismModuleV2MetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_OptimismModuleV2 *OptimismModuleV2Raw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _OptimismModuleV2.Contract.OptimismModuleV2Caller.contract.Call(opts, result, method, params...) +} + +func (_OptimismModuleV2 *OptimismModuleV2Raw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _OptimismModuleV2.Contract.OptimismModuleV2Transactor.contract.Transfer(opts) +} + +func (_OptimismModuleV2 *OptimismModuleV2Raw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _OptimismModuleV2.Contract.OptimismModuleV2Transactor.contract.Transact(opts, method, params...) +} + +func (_OptimismModuleV2 *OptimismModuleV2CallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _OptimismModuleV2.Contract.contract.Call(opts, result, method, params...) +} + +func (_OptimismModuleV2 *OptimismModuleV2TransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _OptimismModuleV2.Contract.contract.Transfer(opts) +} + +func (_OptimismModuleV2 *OptimismModuleV2TransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _OptimismModuleV2.Contract.contract.Transact(opts, method, params...) +} + +func (_OptimismModuleV2 *OptimismModuleV2Caller) BlockHash(opts *bind.CallOpts, n *big.Int) ([32]byte, error) { + var out []interface{} + err := _OptimismModuleV2.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 (_OptimismModuleV2 *OptimismModuleV2Session) BlockHash(n *big.Int) ([32]byte, error) { + return _OptimismModuleV2.Contract.BlockHash(&_OptimismModuleV2.CallOpts, n) +} + +func (_OptimismModuleV2 *OptimismModuleV2CallerSession) BlockHash(n *big.Int) ([32]byte, error) { + return _OptimismModuleV2.Contract.BlockHash(&_OptimismModuleV2.CallOpts, n) +} + +func (_OptimismModuleV2 *OptimismModuleV2Caller) BlockNumber(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _OptimismModuleV2.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 (_OptimismModuleV2 *OptimismModuleV2Session) BlockNumber() (*big.Int, error) { + return _OptimismModuleV2.Contract.BlockNumber(&_OptimismModuleV2.CallOpts) +} + +func (_OptimismModuleV2 *OptimismModuleV2CallerSession) BlockNumber() (*big.Int, error) { + return _OptimismModuleV2.Contract.BlockNumber(&_OptimismModuleV2.CallOpts) +} + +func (_OptimismModuleV2 *OptimismModuleV2Caller) GetCurrentL1Fee(opts *bind.CallOpts, dataSize *big.Int) (*big.Int, error) { + var out []interface{} + err := _OptimismModuleV2.contract.Call(opts, &out, "getCurrentL1Fee", dataSize) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_OptimismModuleV2 *OptimismModuleV2Session) GetCurrentL1Fee(dataSize *big.Int) (*big.Int, error) { + return _OptimismModuleV2.Contract.GetCurrentL1Fee(&_OptimismModuleV2.CallOpts, dataSize) +} + +func (_OptimismModuleV2 *OptimismModuleV2CallerSession) GetCurrentL1Fee(dataSize *big.Int) (*big.Int, error) { + return _OptimismModuleV2.Contract.GetCurrentL1Fee(&_OptimismModuleV2.CallOpts, dataSize) +} + +func (_OptimismModuleV2 *OptimismModuleV2Caller) GetGasOverhead(opts *bind.CallOpts) (GetGasOverhead, + + error) { + var out []interface{} + err := _OptimismModuleV2.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 (_OptimismModuleV2 *OptimismModuleV2Session) GetGasOverhead() (GetGasOverhead, + + error) { + return _OptimismModuleV2.Contract.GetGasOverhead(&_OptimismModuleV2.CallOpts) +} + +func (_OptimismModuleV2 *OptimismModuleV2CallerSession) GetGasOverhead() (GetGasOverhead, + + error) { + return _OptimismModuleV2.Contract.GetGasOverhead(&_OptimismModuleV2.CallOpts) +} + +func (_OptimismModuleV2 *OptimismModuleV2Caller) GetL1FeeCoefficient(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _OptimismModuleV2.contract.Call(opts, &out, "getL1FeeCoefficient") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_OptimismModuleV2 *OptimismModuleV2Session) GetL1FeeCoefficient() (*big.Int, error) { + return _OptimismModuleV2.Contract.GetL1FeeCoefficient(&_OptimismModuleV2.CallOpts) +} + +func (_OptimismModuleV2 *OptimismModuleV2CallerSession) GetL1FeeCoefficient() (*big.Int, error) { + return _OptimismModuleV2.Contract.GetL1FeeCoefficient(&_OptimismModuleV2.CallOpts) +} + +func (_OptimismModuleV2 *OptimismModuleV2Caller) GetMaxL1Fee(opts *bind.CallOpts, dataSize *big.Int) (*big.Int, error) { + var out []interface{} + err := _OptimismModuleV2.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 (_OptimismModuleV2 *OptimismModuleV2Session) GetMaxL1Fee(dataSize *big.Int) (*big.Int, error) { + return _OptimismModuleV2.Contract.GetMaxL1Fee(&_OptimismModuleV2.CallOpts, dataSize) +} + +func (_OptimismModuleV2 *OptimismModuleV2CallerSession) GetMaxL1Fee(dataSize *big.Int) (*big.Int, error) { + return _OptimismModuleV2.Contract.GetMaxL1Fee(&_OptimismModuleV2.CallOpts, dataSize) +} + +func (_OptimismModuleV2 *OptimismModuleV2Caller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _OptimismModuleV2.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_OptimismModuleV2 *OptimismModuleV2Session) Owner() (common.Address, error) { + return _OptimismModuleV2.Contract.Owner(&_OptimismModuleV2.CallOpts) +} + +func (_OptimismModuleV2 *OptimismModuleV2CallerSession) Owner() (common.Address, error) { + return _OptimismModuleV2.Contract.Owner(&_OptimismModuleV2.CallOpts) +} + +func (_OptimismModuleV2 *OptimismModuleV2Transactor) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _OptimismModuleV2.contract.Transact(opts, "acceptOwnership") +} + +func (_OptimismModuleV2 *OptimismModuleV2Session) AcceptOwnership() (*types.Transaction, error) { + return _OptimismModuleV2.Contract.AcceptOwnership(&_OptimismModuleV2.TransactOpts) +} + +func (_OptimismModuleV2 *OptimismModuleV2TransactorSession) AcceptOwnership() (*types.Transaction, error) { + return _OptimismModuleV2.Contract.AcceptOwnership(&_OptimismModuleV2.TransactOpts) +} + +func (_OptimismModuleV2 *OptimismModuleV2Transactor) SetL1FeeCalculation(opts *bind.TransactOpts, coefficient uint8) (*types.Transaction, error) { + return _OptimismModuleV2.contract.Transact(opts, "setL1FeeCalculation", coefficient) +} + +func (_OptimismModuleV2 *OptimismModuleV2Session) SetL1FeeCalculation(coefficient uint8) (*types.Transaction, error) { + return _OptimismModuleV2.Contract.SetL1FeeCalculation(&_OptimismModuleV2.TransactOpts, coefficient) +} + +func (_OptimismModuleV2 *OptimismModuleV2TransactorSession) SetL1FeeCalculation(coefficient uint8) (*types.Transaction, error) { + return _OptimismModuleV2.Contract.SetL1FeeCalculation(&_OptimismModuleV2.TransactOpts, coefficient) +} + +func (_OptimismModuleV2 *OptimismModuleV2Transactor) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { + return _OptimismModuleV2.contract.Transact(opts, "transferOwnership", to) +} + +func (_OptimismModuleV2 *OptimismModuleV2Session) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _OptimismModuleV2.Contract.TransferOwnership(&_OptimismModuleV2.TransactOpts, to) +} + +func (_OptimismModuleV2 *OptimismModuleV2TransactorSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _OptimismModuleV2.Contract.TransferOwnership(&_OptimismModuleV2.TransactOpts, to) +} + +type OptimismModuleV2L1FeeCoefficientSetIterator struct { + Event *OptimismModuleV2L1FeeCoefficientSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *OptimismModuleV2L1FeeCoefficientSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(OptimismModuleV2L1FeeCoefficientSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(OptimismModuleV2L1FeeCoefficientSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *OptimismModuleV2L1FeeCoefficientSetIterator) Error() error { + return it.fail +} + +func (it *OptimismModuleV2L1FeeCoefficientSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type OptimismModuleV2L1FeeCoefficientSet struct { + Coefficient uint8 + Raw types.Log +} + +func (_OptimismModuleV2 *OptimismModuleV2Filterer) FilterL1FeeCoefficientSet(opts *bind.FilterOpts) (*OptimismModuleV2L1FeeCoefficientSetIterator, error) { + + logs, sub, err := _OptimismModuleV2.contract.FilterLogs(opts, "L1FeeCoefficientSet") + if err != nil { + return nil, err + } + return &OptimismModuleV2L1FeeCoefficientSetIterator{contract: _OptimismModuleV2.contract, event: "L1FeeCoefficientSet", logs: logs, sub: sub}, nil +} + +func (_OptimismModuleV2 *OptimismModuleV2Filterer) WatchL1FeeCoefficientSet(opts *bind.WatchOpts, sink chan<- *OptimismModuleV2L1FeeCoefficientSet) (event.Subscription, error) { + + logs, sub, err := _OptimismModuleV2.contract.WatchLogs(opts, "L1FeeCoefficientSet") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(OptimismModuleV2L1FeeCoefficientSet) + if err := _OptimismModuleV2.contract.UnpackLog(event, "L1FeeCoefficientSet", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_OptimismModuleV2 *OptimismModuleV2Filterer) ParseL1FeeCoefficientSet(log types.Log) (*OptimismModuleV2L1FeeCoefficientSet, error) { + event := new(OptimismModuleV2L1FeeCoefficientSet) + if err := _OptimismModuleV2.contract.UnpackLog(event, "L1FeeCoefficientSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type OptimismModuleV2OwnershipTransferRequestedIterator struct { + Event *OptimismModuleV2OwnershipTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *OptimismModuleV2OwnershipTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(OptimismModuleV2OwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(OptimismModuleV2OwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *OptimismModuleV2OwnershipTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *OptimismModuleV2OwnershipTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type OptimismModuleV2OwnershipTransferRequested struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_OptimismModuleV2 *OptimismModuleV2Filterer) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*OptimismModuleV2OwnershipTransferRequestedIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _OptimismModuleV2.contract.FilterLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return &OptimismModuleV2OwnershipTransferRequestedIterator{contract: _OptimismModuleV2.contract, event: "OwnershipTransferRequested", logs: logs, sub: sub}, nil +} + +func (_OptimismModuleV2 *OptimismModuleV2Filterer) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *OptimismModuleV2OwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _OptimismModuleV2.contract.WatchLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(OptimismModuleV2OwnershipTransferRequested) + if err := _OptimismModuleV2.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_OptimismModuleV2 *OptimismModuleV2Filterer) ParseOwnershipTransferRequested(log types.Log) (*OptimismModuleV2OwnershipTransferRequested, error) { + event := new(OptimismModuleV2OwnershipTransferRequested) + if err := _OptimismModuleV2.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type OptimismModuleV2OwnershipTransferredIterator struct { + Event *OptimismModuleV2OwnershipTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *OptimismModuleV2OwnershipTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(OptimismModuleV2OwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(OptimismModuleV2OwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *OptimismModuleV2OwnershipTransferredIterator) Error() error { + return it.fail +} + +func (it *OptimismModuleV2OwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type OptimismModuleV2OwnershipTransferred struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_OptimismModuleV2 *OptimismModuleV2Filterer) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*OptimismModuleV2OwnershipTransferredIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _OptimismModuleV2.contract.FilterLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return &OptimismModuleV2OwnershipTransferredIterator{contract: _OptimismModuleV2.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +func (_OptimismModuleV2 *OptimismModuleV2Filterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *OptimismModuleV2OwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _OptimismModuleV2.contract.WatchLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(OptimismModuleV2OwnershipTransferred) + if err := _OptimismModuleV2.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_OptimismModuleV2 *OptimismModuleV2Filterer) ParseOwnershipTransferred(log types.Log) (*OptimismModuleV2OwnershipTransferred, error) { + event := new(OptimismModuleV2OwnershipTransferred) + if err := _OptimismModuleV2.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type GetGasOverhead struct { + ChainModuleFixedOverhead *big.Int + ChainModulePerByteOverhead *big.Int +} + +func (_OptimismModuleV2 *OptimismModuleV2) ParseLog(log types.Log) (generated.AbigenLog, error) { + switch log.Topics[0] { + case _OptimismModuleV2.abi.Events["L1FeeCoefficientSet"].ID: + return _OptimismModuleV2.ParseL1FeeCoefficientSet(log) + case _OptimismModuleV2.abi.Events["OwnershipTransferRequested"].ID: + return _OptimismModuleV2.ParseOwnershipTransferRequested(log) + case _OptimismModuleV2.abi.Events["OwnershipTransferred"].ID: + return _OptimismModuleV2.ParseOwnershipTransferred(log) + + default: + return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) + } +} + +func (OptimismModuleV2L1FeeCoefficientSet) Topic() common.Hash { + return common.HexToHash("0x29ec9e31de0d3fe0208a7ccb792bbc26a854f123146110daa3a77219cb74a554") +} + +func (OptimismModuleV2OwnershipTransferRequested) Topic() common.Hash { + return common.HexToHash("0xed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278") +} + +func (OptimismModuleV2OwnershipTransferred) Topic() common.Hash { + return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") +} + +func (_OptimismModuleV2 *OptimismModuleV2) Address() common.Address { + return _OptimismModuleV2.address +} + +type OptimismModuleV2Interface interface { + BlockHash(opts *bind.CallOpts, n *big.Int) ([32]byte, error) + + BlockNumber(opts *bind.CallOpts) (*big.Int, error) + + GetCurrentL1Fee(opts *bind.CallOpts, dataSize *big.Int) (*big.Int, error) + + GetGasOverhead(opts *bind.CallOpts) (GetGasOverhead, + + error) + + GetL1FeeCoefficient(opts *bind.CallOpts) (*big.Int, error) + + GetMaxL1Fee(opts *bind.CallOpts, dataSize *big.Int) (*big.Int, error) + + Owner(opts *bind.CallOpts) (common.Address, error) + + AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) + + SetL1FeeCalculation(opts *bind.TransactOpts, coefficient uint8) (*types.Transaction, error) + + TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + + FilterL1FeeCoefficientSet(opts *bind.FilterOpts) (*OptimismModuleV2L1FeeCoefficientSetIterator, error) + + WatchL1FeeCoefficientSet(opts *bind.WatchOpts, sink chan<- *OptimismModuleV2L1FeeCoefficientSet) (event.Subscription, error) + + ParseL1FeeCoefficientSet(log types.Log) (*OptimismModuleV2L1FeeCoefficientSet, error) + + FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*OptimismModuleV2OwnershipTransferRequestedIterator, error) + + WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *OptimismModuleV2OwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferRequested(log types.Log) (*OptimismModuleV2OwnershipTransferRequested, error) + + FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*OptimismModuleV2OwnershipTransferredIterator, error) + + WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *OptimismModuleV2OwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferred(log types.Log) (*OptimismModuleV2OwnershipTransferred, error) + + ParseLog(log types.Log) (generated.AbigenLog, error) + + Address() common.Address +} diff --git a/core/gethwrappers/generated/scroll_module/scroll_module.go b/core/gethwrappers/generated/scroll_module/scroll_module.go index 702cc39a7a..e8f48da0d0 100644 --- a/core/gethwrappers/generated/scroll_module/scroll_module.go +++ b/core/gethwrappers/generated/scroll_module/scroll_module.go @@ -5,6 +5,7 @@ package scroll_module import ( "errors" + "fmt" "math/big" "strings" @@ -14,6 +15,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/event" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" ) var ( @@ -29,8 +31,8 @@ var ( ) 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: "0x608060405234801561001057600080fd5b5061050c806100206000396000f3fe608060405234801561001057600080fd5b50600436106100675760003560e01c806357e871e71161005057806357e871e71461009a57806385df51fd146100a0578063de9ee35e146100b357600080fd5b8063125441401461006c57806318b8f61314610092575b600080fd5b61007f61007a3660046102e8565b6100c9565b6040519081526020015b60405180910390f35b61007f6101ea565b4361007f565b61007f6100ae3660046102e8565b6102bb565b6040805161afc8815260aa602082015201610089565b6000806100d7836004610330565b67ffffffffffffffff8111156100ef576100ef61034d565b6040519080825280601f01601f191660200182016040528015610119576020820181803683370190505b50905073530000000000000000000000000000000000000273ffffffffffffffffffffffffffffffffffffffff166349948e0e826040518060c00160405280608c8152602001610474608c91396040516020016101779291906103a0565b6040516020818303038152906040526040518263ffffffff1660e01b81526004016101a291906103cf565b602060405180830381865afa1580156101bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101e39190610420565b9392505050565b600073530000000000000000000000000000000000000273ffffffffffffffffffffffffffffffffffffffff166349948e0e6000366040518060c00160405280608c8152602001610474608c913960405160200161024a93929190610439565b6040516020818303038152906040526040518263ffffffff1660e01b815260040161027591906103cf565b602060405180830381865afa158015610292573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102b69190610420565b905090565b600043821015806102d657506101006102d48343610460565b115b156102e357506000919050565b504090565b6000602082840312156102fa57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808202811582820484141761034757610347610301565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60005b8381101561039757818101518382015260200161037f565b50506000910152565b600083516103b281846020880161037c565b8351908301906103c681836020880161037c565b01949350505050565b60208152600082518060208401526103ee81604085016020870161037c565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b60006020828403121561043257600080fd5b5051919050565b82848237600083820160008152835161045681836020880161037c565b0195945050505050565b818103818111156103475761034761030156feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa164736f6c6343000813000a", + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"coefficient\",\"type\":\"uint8\"}],\"name\":\"InvalidL1FeeCoefficient\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"coefficient\",\"type\":\"uint8\"}],\"name\":\"L1FeeCoefficientSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"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\":[{\"internalType\":\"uint256\",\"name\":\"dataSize\",\"type\":\"uint256\"}],\"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\":[],\"name\":\"getL1FeeCoefficient\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"coefficient\",\"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\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"coefficient\",\"type\":\"uint8\"}],\"name\":\"setL1FeeCalculation\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60806040526001805460ff60a01b1916601960a21b17905534801561002357600080fd5b50338060008161007a5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b03848116919091179091558116156100aa576100aa816100b2565b50505061015b565b336001600160a01b0382160361010a5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610071565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6109288061016a6000396000f3fe608060405234801561001057600080fd5b50600436106100be5760003560e01c80638da5cb5b11610076578063de9ee35e1161005b578063de9ee35e1461016a578063f22559a014610180578063f2fde38b1461019357600080fd5b80638da5cb5b1461011f578063d10a944e1461014757600080fd5b80637810d12a116100a75780637810d12a146100ef57806379ba50971461010257806385df51fd1461010c57600080fd5b806312544140146100c357806357e871e7146100e9575b600080fd5b6100d66100d136600461069d565b6101a6565b6040519081526020015b60405180910390f35b436100d6565b6100d66100fd36600461069d565b6101b7565b61010a6101f6565b005b6100d661011a36600461069d565b6102f8565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100e0565b60015474010000000000000000000000000000000000000000900460ff166100d6565b6040805161afc8815260aa6020820152016100e0565b61010a61018e3660046106b6565b610325565b61010a6101a13660046106d9565b6103f0565b60006101b182610404565b92915050565b600060646101c483610404565b6001546101ec919074010000000000000000000000000000000000000000900460ff1661073e565b6101b19190610755565b60015473ffffffffffffffffffffffffffffffffffffffff16331461027c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b6000438210158061031357506101006103118343610790565b115b1561032057506000919050565b504090565b61032d610525565b60648160ff161115610370576040517f1a8a06a000000000000000000000000000000000000000000000000000000000815260ff82166004820152602401610273565b600180547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000060ff8416908102919091179091556040519081527f29ec9e31de0d3fe0208a7ccb792bbc26a854f123146110daa3a77219cb74a5549060200160405180910390a150565b6103f8610525565b610401816105a8565b50565b60008061041283600461073e565b67ffffffffffffffff81111561042a5761042a6107a3565b6040519080825280601f01601f191660200182016040528015610454576020820181803683370190505b50905073530000000000000000000000000000000000000273ffffffffffffffffffffffffffffffffffffffff166349948e0e826040518060c00160405280608c8152602001610890608c91396040516020016104b29291906107f6565b6040516020818303038152906040526040518263ffffffff1660e01b81526004016104dd9190610825565b602060405180830381865afa1580156104fa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061051e9190610876565b9392505050565b60005473ffffffffffffffffffffffffffffffffffffffff1633146105a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610273565b565b3373ffffffffffffffffffffffffffffffffffffffff821603610627576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610273565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000602082840312156106af57600080fd5b5035919050565b6000602082840312156106c857600080fd5b813560ff8116811461051e57600080fd5b6000602082840312156106eb57600080fd5b813573ffffffffffffffffffffffffffffffffffffffff8116811461051e57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820281158282048414176101b1576101b161070f565b60008261078b577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b818103818111156101b1576101b161070f565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60005b838110156107ed5781810151838201526020016107d5565b50506000910152565b600083516108088184602088016107d2565b83519083019061081c8183602088016107d2565b01949350505050565b60208152600082518060208401526108448160408501602087016107d2565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b60006020828403121561088857600080fd5b505191905056feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa164736f6c6343000813000a", } var ScrollModuleABI = ScrollModuleMetaData.ABI @@ -213,9 +215,9 @@ func (_ScrollModule *ScrollModuleCallerSession) BlockNumber() (*big.Int, error) return _ScrollModule.Contract.BlockNumber(&_ScrollModule.CallOpts) } -func (_ScrollModule *ScrollModuleCaller) GetCurrentL1Fee(opts *bind.CallOpts) (*big.Int, error) { +func (_ScrollModule *ScrollModuleCaller) GetCurrentL1Fee(opts *bind.CallOpts, dataSize *big.Int) (*big.Int, error) { var out []interface{} - err := _ScrollModule.contract.Call(opts, &out, "getCurrentL1Fee") + err := _ScrollModule.contract.Call(opts, &out, "getCurrentL1Fee", dataSize) if err != nil { return *new(*big.Int), err @@ -227,12 +229,12 @@ func (_ScrollModule *ScrollModuleCaller) GetCurrentL1Fee(opts *bind.CallOpts) (* } -func (_ScrollModule *ScrollModuleSession) GetCurrentL1Fee() (*big.Int, error) { - return _ScrollModule.Contract.GetCurrentL1Fee(&_ScrollModule.CallOpts) +func (_ScrollModule *ScrollModuleSession) GetCurrentL1Fee(dataSize *big.Int) (*big.Int, error) { + return _ScrollModule.Contract.GetCurrentL1Fee(&_ScrollModule.CallOpts, dataSize) } -func (_ScrollModule *ScrollModuleCallerSession) GetCurrentL1Fee() (*big.Int, error) { - return _ScrollModule.Contract.GetCurrentL1Fee(&_ScrollModule.CallOpts) +func (_ScrollModule *ScrollModuleCallerSession) GetCurrentL1Fee(dataSize *big.Int) (*big.Int, error) { + return _ScrollModule.Contract.GetCurrentL1Fee(&_ScrollModule.CallOpts, dataSize) } func (_ScrollModule *ScrollModuleCaller) GetGasOverhead(opts *bind.CallOpts) (GetGasOverhead, @@ -265,6 +267,28 @@ func (_ScrollModule *ScrollModuleCallerSession) GetGasOverhead() (GetGasOverhead return _ScrollModule.Contract.GetGasOverhead(&_ScrollModule.CallOpts) } +func (_ScrollModule *ScrollModuleCaller) GetL1FeeCoefficient(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _ScrollModule.contract.Call(opts, &out, "getL1FeeCoefficient") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_ScrollModule *ScrollModuleSession) GetL1FeeCoefficient() (*big.Int, error) { + return _ScrollModule.Contract.GetL1FeeCoefficient(&_ScrollModule.CallOpts) +} + +func (_ScrollModule *ScrollModuleCallerSession) GetL1FeeCoefficient() (*big.Int, error) { + return _ScrollModule.Contract.GetL1FeeCoefficient(&_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) @@ -287,11 +311,484 @@ func (_ScrollModule *ScrollModuleCallerSession) GetMaxL1Fee(dataSize *big.Int) ( return _ScrollModule.Contract.GetMaxL1Fee(&_ScrollModule.CallOpts, dataSize) } +func (_ScrollModule *ScrollModuleCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _ScrollModule.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_ScrollModule *ScrollModuleSession) Owner() (common.Address, error) { + return _ScrollModule.Contract.Owner(&_ScrollModule.CallOpts) +} + +func (_ScrollModule *ScrollModuleCallerSession) Owner() (common.Address, error) { + return _ScrollModule.Contract.Owner(&_ScrollModule.CallOpts) +} + +func (_ScrollModule *ScrollModuleTransactor) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ScrollModule.contract.Transact(opts, "acceptOwnership") +} + +func (_ScrollModule *ScrollModuleSession) AcceptOwnership() (*types.Transaction, error) { + return _ScrollModule.Contract.AcceptOwnership(&_ScrollModule.TransactOpts) +} + +func (_ScrollModule *ScrollModuleTransactorSession) AcceptOwnership() (*types.Transaction, error) { + return _ScrollModule.Contract.AcceptOwnership(&_ScrollModule.TransactOpts) +} + +func (_ScrollModule *ScrollModuleTransactor) SetL1FeeCalculation(opts *bind.TransactOpts, coefficient uint8) (*types.Transaction, error) { + return _ScrollModule.contract.Transact(opts, "setL1FeeCalculation", coefficient) +} + +func (_ScrollModule *ScrollModuleSession) SetL1FeeCalculation(coefficient uint8) (*types.Transaction, error) { + return _ScrollModule.Contract.SetL1FeeCalculation(&_ScrollModule.TransactOpts, coefficient) +} + +func (_ScrollModule *ScrollModuleTransactorSession) SetL1FeeCalculation(coefficient uint8) (*types.Transaction, error) { + return _ScrollModule.Contract.SetL1FeeCalculation(&_ScrollModule.TransactOpts, coefficient) +} + +func (_ScrollModule *ScrollModuleTransactor) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { + return _ScrollModule.contract.Transact(opts, "transferOwnership", to) +} + +func (_ScrollModule *ScrollModuleSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _ScrollModule.Contract.TransferOwnership(&_ScrollModule.TransactOpts, to) +} + +func (_ScrollModule *ScrollModuleTransactorSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _ScrollModule.Contract.TransferOwnership(&_ScrollModule.TransactOpts, to) +} + +type ScrollModuleL1FeeCoefficientSetIterator struct { + Event *ScrollModuleL1FeeCoefficientSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *ScrollModuleL1FeeCoefficientSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(ScrollModuleL1FeeCoefficientSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(ScrollModuleL1FeeCoefficientSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *ScrollModuleL1FeeCoefficientSetIterator) Error() error { + return it.fail +} + +func (it *ScrollModuleL1FeeCoefficientSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type ScrollModuleL1FeeCoefficientSet struct { + Coefficient uint8 + Raw types.Log +} + +func (_ScrollModule *ScrollModuleFilterer) FilterL1FeeCoefficientSet(opts *bind.FilterOpts) (*ScrollModuleL1FeeCoefficientSetIterator, error) { + + logs, sub, err := _ScrollModule.contract.FilterLogs(opts, "L1FeeCoefficientSet") + if err != nil { + return nil, err + } + return &ScrollModuleL1FeeCoefficientSetIterator{contract: _ScrollModule.contract, event: "L1FeeCoefficientSet", logs: logs, sub: sub}, nil +} + +func (_ScrollModule *ScrollModuleFilterer) WatchL1FeeCoefficientSet(opts *bind.WatchOpts, sink chan<- *ScrollModuleL1FeeCoefficientSet) (event.Subscription, error) { + + logs, sub, err := _ScrollModule.contract.WatchLogs(opts, "L1FeeCoefficientSet") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(ScrollModuleL1FeeCoefficientSet) + if err := _ScrollModule.contract.UnpackLog(event, "L1FeeCoefficientSet", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_ScrollModule *ScrollModuleFilterer) ParseL1FeeCoefficientSet(log types.Log) (*ScrollModuleL1FeeCoefficientSet, error) { + event := new(ScrollModuleL1FeeCoefficientSet) + if err := _ScrollModule.contract.UnpackLog(event, "L1FeeCoefficientSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type ScrollModuleOwnershipTransferRequestedIterator struct { + Event *ScrollModuleOwnershipTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *ScrollModuleOwnershipTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(ScrollModuleOwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(ScrollModuleOwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *ScrollModuleOwnershipTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *ScrollModuleOwnershipTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type ScrollModuleOwnershipTransferRequested struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_ScrollModule *ScrollModuleFilterer) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*ScrollModuleOwnershipTransferRequestedIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _ScrollModule.contract.FilterLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return &ScrollModuleOwnershipTransferRequestedIterator{contract: _ScrollModule.contract, event: "OwnershipTransferRequested", logs: logs, sub: sub}, nil +} + +func (_ScrollModule *ScrollModuleFilterer) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *ScrollModuleOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _ScrollModule.contract.WatchLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(ScrollModuleOwnershipTransferRequested) + if err := _ScrollModule.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_ScrollModule *ScrollModuleFilterer) ParseOwnershipTransferRequested(log types.Log) (*ScrollModuleOwnershipTransferRequested, error) { + event := new(ScrollModuleOwnershipTransferRequested) + if err := _ScrollModule.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type ScrollModuleOwnershipTransferredIterator struct { + Event *ScrollModuleOwnershipTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *ScrollModuleOwnershipTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(ScrollModuleOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(ScrollModuleOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *ScrollModuleOwnershipTransferredIterator) Error() error { + return it.fail +} + +func (it *ScrollModuleOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type ScrollModuleOwnershipTransferred struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_ScrollModule *ScrollModuleFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*ScrollModuleOwnershipTransferredIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _ScrollModule.contract.FilterLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return &ScrollModuleOwnershipTransferredIterator{contract: _ScrollModule.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +func (_ScrollModule *ScrollModuleFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *ScrollModuleOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _ScrollModule.contract.WatchLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(ScrollModuleOwnershipTransferred) + if err := _ScrollModule.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_ScrollModule *ScrollModuleFilterer) ParseOwnershipTransferred(log types.Log) (*ScrollModuleOwnershipTransferred, error) { + event := new(ScrollModuleOwnershipTransferred) + if err := _ScrollModule.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + type GetGasOverhead struct { ChainModuleFixedOverhead *big.Int ChainModulePerByteOverhead *big.Int } +func (_ScrollModule *ScrollModule) ParseLog(log types.Log) (generated.AbigenLog, error) { + switch log.Topics[0] { + case _ScrollModule.abi.Events["L1FeeCoefficientSet"].ID: + return _ScrollModule.ParseL1FeeCoefficientSet(log) + case _ScrollModule.abi.Events["OwnershipTransferRequested"].ID: + return _ScrollModule.ParseOwnershipTransferRequested(log) + case _ScrollModule.abi.Events["OwnershipTransferred"].ID: + return _ScrollModule.ParseOwnershipTransferred(log) + + default: + return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) + } +} + +func (ScrollModuleL1FeeCoefficientSet) Topic() common.Hash { + return common.HexToHash("0x29ec9e31de0d3fe0208a7ccb792bbc26a854f123146110daa3a77219cb74a554") +} + +func (ScrollModuleOwnershipTransferRequested) Topic() common.Hash { + return common.HexToHash("0xed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278") +} + +func (ScrollModuleOwnershipTransferred) Topic() common.Hash { + return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") +} + func (_ScrollModule *ScrollModule) Address() common.Address { return _ScrollModule.address } @@ -301,13 +798,43 @@ type ScrollModuleInterface interface { BlockNumber(opts *bind.CallOpts) (*big.Int, error) - GetCurrentL1Fee(opts *bind.CallOpts) (*big.Int, error) + GetCurrentL1Fee(opts *bind.CallOpts, dataSize *big.Int) (*big.Int, error) GetGasOverhead(opts *bind.CallOpts) (GetGasOverhead, error) + GetL1FeeCoefficient(opts *bind.CallOpts) (*big.Int, error) + GetMaxL1Fee(opts *bind.CallOpts, dataSize *big.Int) (*big.Int, error) + Owner(opts *bind.CallOpts) (common.Address, error) + + AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) + + SetL1FeeCalculation(opts *bind.TransactOpts, coefficient uint8) (*types.Transaction, error) + + TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + + FilterL1FeeCoefficientSet(opts *bind.FilterOpts) (*ScrollModuleL1FeeCoefficientSetIterator, error) + + WatchL1FeeCoefficientSet(opts *bind.WatchOpts, sink chan<- *ScrollModuleL1FeeCoefficientSet) (event.Subscription, error) + + ParseL1FeeCoefficientSet(log types.Log) (*ScrollModuleL1FeeCoefficientSet, error) + + FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*ScrollModuleOwnershipTransferRequestedIterator, error) + + WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *ScrollModuleOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferRequested(log types.Log) (*ScrollModuleOwnershipTransferRequested, error) + + FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*ScrollModuleOwnershipTransferredIterator, error) + + WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *ScrollModuleOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferred(log types.Log) (*ScrollModuleOwnershipTransferred, error) + + ParseLog(log types.Log) (generated.AbigenLog, error) + Address() common.Address } diff --git a/core/gethwrappers/generated/vrf_coordinator_test_v2_5/vrf_coordinator_test_v2_5.go b/core/gethwrappers/generated/vrf_coordinator_test_v2_5/vrf_coordinator_test_v2_5.go new file mode 100644 index 0000000000..6d6d38ef38 --- /dev/null +++ b/core/gethwrappers/generated/vrf_coordinator_test_v2_5/vrf_coordinator_test_v2_5.go @@ -0,0 +1,3994 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package vrf_coordinator_test_v2_5 + +import ( + "errors" + "fmt" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" +) + +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +type VRFOldProof struct { + Pk [2]*big.Int + Gamma [2]*big.Int + C *big.Int + S *big.Int + Seed *big.Int + UWitness common.Address + CGammaWitness [2]*big.Int + SHashWitness [2]*big.Int + ZInv *big.Int +} + +type VRFTypesRequestCommitmentV2Plus struct { + BlockNum uint64 + SubId *big.Int + CallbackGasLimit uint32 + NumWords uint32 + Sender common.Address + ExtraArgs []byte +} + +type VRFV2PlusClientRandomWordsRequest struct { + KeyHash [32]byte + SubId *big.Int + RequestConfirmations uint16 + CallbackGasLimit uint32 + NumWords uint32 + ExtraArgs []byte +} + +var VRFCoordinatorTestV25MetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"blockhashStore\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"internalBalance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"externalBalance\",\"type\":\"uint256\"}],\"name\":\"BalanceInvariantViolated\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNum\",\"type\":\"uint256\"}],\"name\":\"BlockhashNotInStore\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"coordinatorAddress\",\"type\":\"address\"}],\"name\":\"CoordinatorAlreadyRegistered\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"coordinatorAddress\",\"type\":\"address\"}],\"name\":\"CoordinatorNotRegistered\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FailedToSendNative\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FailedToTransferLink\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"have\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"want\",\"type\":\"uint32\"}],\"name\":\"GasLimitTooBig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"gasPrice\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxGas\",\"type\":\"uint256\"}],\"name\":\"GasPriceExceeded\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectCommitment\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IndexOutOfRange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCalldata\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"InvalidConsumer\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidExtraArgsTag\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"linkWei\",\"type\":\"int256\"}],\"name\":\"InvalidLinkWeiPrice\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"premiumPercentage\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"max\",\"type\":\"uint8\"}],\"name\":\"InvalidPremiumPercentage\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"have\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"min\",\"type\":\"uint16\"},{\"internalType\":\"uint16\",\"name\":\"max\",\"type\":\"uint16\"}],\"name\":\"InvalidRequestConfirmations\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSubscription\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LinkAlreadySet\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"flatFeeLinkDiscountPPM\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"flatFeeNativePPM\",\"type\":\"uint32\"}],\"name\":\"LinkDiscountTooHigh\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LinkNotSet\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"have\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"max\",\"type\":\"uint32\"}],\"name\":\"MsgDataTooBig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"proposedOwner\",\"type\":\"address\"}],\"name\":\"MustBeRequestedOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"MustBeSubOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoCorrespondingRequest\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"}],\"name\":\"NoSuchProvingKey\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"have\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"want\",\"type\":\"uint32\"}],\"name\":\"NumWordsTooBig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableFromLink\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PaymentTooLarge\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PendingRequestExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"}],\"name\":\"ProvingKeyAlreadyRegistered\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Reentrant\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyConsumers\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"maxGasLimit\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"stalenessSeconds\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"gasAfterPaymentCalculation\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"int256\",\"name\":\"fallbackWeiPerUnitLink\",\"type\":\"int256\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeNativePPM\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkDiscountPPM\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"nativePremiumPercentage\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"linkPremiumPercentage\",\"type\":\"uint8\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"coordinatorAddress\",\"type\":\"address\"}],\"name\":\"CoordinatorDeregistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"coordinatorAddress\",\"type\":\"address\"}],\"name\":\"CoordinatorRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"int256\",\"name\":\"fallbackWeiPerUnitLink\",\"type\":\"int256\"}],\"name\":\"FallbackWeiPerUnitLinkUsed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"FundsRecovered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newCoordinator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"MigrationCompleted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"NativeFundsRecovered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"maxGas\",\"type\":\"uint64\"}],\"name\":\"ProvingKeyDeregistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"maxGas\",\"type\":\"uint64\"}],\"name\":\"ProvingKeyRegistered\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"outputSeed\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"payment\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"nativePayment\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"onlyPremium\",\"type\":\"bool\"}],\"name\":\"RandomWordsFulfilled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"preSeed\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RandomWordsRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amountLink\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amountNative\",\"type\":\"uint256\"}],\"name\":\"SubscriptionCanceled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"SubscriptionConsumerAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"SubscriptionConsumerRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"SubscriptionCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newBalance\",\"type\":\"uint256\"}],\"name\":\"SubscriptionFunded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldNativeBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newNativeBalance\",\"type\":\"uint256\"}],\"name\":\"SubscriptionFundedWithNative\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"SubscriptionOwnerTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"SubscriptionOwnerTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"BLOCKHASH_STORE\",\"outputs\":[{\"internalType\":\"contractBlockhashStoreInterface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"LINK\",\"outputs\":[{\"internalType\":\"contractLinkTokenInterface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"LINK_NATIVE_FEED\",\"outputs\":[{\"internalType\":\"contractAggregatorV3Interface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_CONSUMERS\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_NUM_WORDS\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_REQUEST_CONFIRMATIONS\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"acceptSubscriptionOwnerTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"addConsumer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"cancelSubscription\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"createSubscription\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"}],\"name\":\"deregisterMigratableCoordinator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[2]\",\"name\":\"publicProvingKey\",\"type\":\"uint256[2]\"}],\"name\":\"deregisterProvingKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256[2]\",\"name\":\"pk\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256[2]\",\"name\":\"gamma\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256\",\"name\":\"c\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"s\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"seed\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"uWitness\",\"type\":\"address\"},{\"internalType\":\"uint256[2]\",\"name\":\"cGammaWitness\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256[2]\",\"name\":\"sHashWitness\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint256\",\"name\":\"zInv\",\"type\":\"uint256\"}],\"internalType\":\"structVRFOld.Proof\",\"name\":\"proof\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"blockNum\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"}],\"internalType\":\"structVRFTypes.RequestCommitmentV2Plus\",\"name\":\"rc\",\"type\":\"tuple\"},{\"internalType\":\"bool\",\"name\":\"onlyPremium\",\"type\":\"bool\"}],\"name\":\"fulfillRandomWords\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"payment\",\"type\":\"uint96\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"fundSubscriptionWithNative\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"startIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxCount\",\"type\":\"uint256\"}],\"name\":\"getActiveSubscriptionIds\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"ids\",\"type\":\"uint256[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"getSubscription\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"balance\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"nativeBalance\",\"type\":\"uint96\"},{\"internalType\":\"uint64\",\"name\":\"reqCount\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"subOwner\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"consumers\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[2]\",\"name\":\"publicKey\",\"type\":\"uint256[2]\"}],\"name\":\"hashOfKey\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"newCoordinator\",\"type\":\"address\"}],\"name\":\"migrate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"onTokenTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"ownerCancelSubscription\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"}],\"name\":\"pendingRequestExists\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"recoverFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"addresspayable\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"recoverNativeFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"}],\"name\":\"registerMigratableCoordinator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[2]\",\"name\":\"publicProvingKey\",\"type\":\"uint256[2]\"},{\"internalType\":\"uint64\",\"name\":\"maxGas\",\"type\":\"uint64\"}],\"name\":\"registerProvingKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"consumer\",\"type\":\"address\"}],\"name\":\"removeConsumer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"keyHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"uint16\",\"name\":\"requestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"numWords\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"extraArgs\",\"type\":\"bytes\"}],\"internalType\":\"structVRFV2PlusClient.RandomWordsRequest\",\"name\":\"req\",\"type\":\"tuple\"}],\"name\":\"requestRandomWords\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"requestId\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"subId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"requestSubscriptionOwnerTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_config\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"reentrancyLock\",\"type\":\"bool\"},{\"internalType\":\"uint32\",\"name\":\"stalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasAfterPaymentCalculation\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeNativePPM\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkDiscountPPM\",\"type\":\"uint32\"},{\"internalType\":\"uint8\",\"name\":\"nativePremiumPercentage\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"linkPremiumPercentage\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_currentSubNonce\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_fallbackWeiPerUnitLink\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"s_provingKeyHashes\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"s_provingKeys\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"exists\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"maxGas\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"s_requestCommitments\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_totalBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_totalNativeBalance\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"minimumRequestConfirmations\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"maxGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"stalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasAfterPaymentCalculation\",\"type\":\"uint32\"},{\"internalType\":\"int256\",\"name\":\"fallbackWeiPerUnitLink\",\"type\":\"int256\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeNativePPM\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"fulfillmentFlatFeeLinkDiscountPPM\",\"type\":\"uint32\"},{\"internalType\":\"uint8\",\"name\":\"nativePremiumPercentage\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"linkPremiumPercentage\",\"type\":\"uint8\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"link\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"linkNativeFeed\",\"type\":\"address\"}],\"name\":\"setLINKAndLINKNativeFeed\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"}],\"name\":\"withdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"addresspayable\",\"name\":\"recipient\",\"type\":\"address\"}],\"name\":\"withdrawNative\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60a06040523480156200001157600080fd5b5060405162005e6338038062005e6383398101604081905262000034916200017e565b33806000816200008b5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000be57620000be81620000d3565b5050506001600160a01b0316608052620001b0565b336001600160a01b038216036200012d5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000082565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000602082840312156200019157600080fd5b81516001600160a01b0381168114620001a957600080fd5b9392505050565b608051615c90620001d3600039600081816105d2015261331e0152615c906000f3fe60806040526004361061028c5760003560e01c80638402595e11610164578063b2a7cac5116100c6578063da2f26101161008a578063e72f6e3011610064578063e72f6e3014610904578063ee9d2d3814610924578063f2fde38b1461095157600080fd5b8063da2f261014610854578063dac83d29146108b3578063dc311dd3146108d357600080fd5b8063b2a7cac5146107b4578063bec4c08c146107d4578063caf70c4a146107f4578063cb63179714610814578063d98e620e1461083457600080fd5b80639d40a6fd11610128578063a63e0bfb11610102578063a63e0bfb14610747578063aa433aff14610767578063aefb212f1461078757600080fd5b80639d40a6fd146106da578063a21a23e414610712578063a4c0ed361461072757600080fd5b80638402595e1461064957806386fe91c7146106695780638da5cb5b1461068957806395b55cfc146106a75780639b1c385e146106ba57600080fd5b8063405b84fa1161020d57806364d51a2a116101d157806372e9d565116101ab57806372e9d565146105f457806379ba5097146106145780637a5a2aef1461062957600080fd5b806364d51a2a1461058b57806365982744146105a0578063689c4517146105c057600080fd5b8063405b84fa146104d057806340d6bb82146104f057806341af6c871461051b57806351cff8d91461054b5780635d06b4ab1461056b57600080fd5b806315c48b841161025457806315c48b84146103f157806318e3dd27146104195780631b6b6d23146104585780632f622e6b14610490578063301f42e9146104b057600080fd5b806304104edb14610291578063043bd6ae146102b3578063088070f5146102dc57806308821d58146103b15780630ae09540146103d1575b600080fd5b34801561029d57600080fd5b506102b16102ac366004614f21565b610971565b005b3480156102bf57600080fd5b506102c960105481565b6040519081526020015b60405180910390f35b3480156102e857600080fd5b50600c546103549061ffff81169063ffffffff62010000820481169160ff660100000000000082048116926701000000000000008304811692600160581b8104821692600160781b8204831692600160981b83041691600160b81b8104821691600160c01b9091041689565b6040805161ffff909a168a5263ffffffff98891660208b01529615159689019690965293861660608801529185166080870152841660a08601529290921660c084015260ff91821660e084015216610100820152610120016102d3565b3480156103bd57600080fd5b506102b16103cc366004614f4f565b610aea565b3480156103dd57600080fd5b506102b16103ec366004614f6b565b610ca7565b3480156103fd57600080fd5b5061040660c881565b60405161ffff90911681526020016102d3565b34801561042557600080fd5b50600a5461044090600160601b90046001600160601b031681565b6040516001600160601b0390911681526020016102d3565b34801561046457600080fd5b50600254610478906001600160a01b031681565b6040516001600160a01b0390911681526020016102d3565b34801561049c57600080fd5b506102b16104ab366004614f21565b610cef565b3480156104bc57600080fd5b506104406104cb3660046151cd565b610d95565b3480156104dc57600080fd5b506102b16104eb366004614f6b565b6110ab565b3480156104fc57600080fd5b506105066101f481565b60405163ffffffff90911681526020016102d3565b34801561052757600080fd5b5061053b6105363660046152bb565b61148d565b60405190151581526020016102d3565b34801561055757600080fd5b506102b1610566366004614f21565b611541565b34801561057757600080fd5b506102b1610586366004614f21565b611666565b34801561059757600080fd5b50610406606481565b3480156105ac57600080fd5b506102b16105bb3660046152d4565b611724565b3480156105cc57600080fd5b506104787f000000000000000000000000000000000000000000000000000000000000000081565b34801561060057600080fd5b50600354610478906001600160a01b031681565b34801561062057600080fd5b506102b1611784565b34801561063557600080fd5b506102b1610644366004615302565b611835565b34801561065557600080fd5b506102b1610664366004614f21565b611969565b34801561067557600080fd5b50600a54610440906001600160601b031681565b34801561069557600080fd5b506000546001600160a01b0316610478565b6102b16106b53660046152bb565b611a84565b3480156106c657600080fd5b506102c96106d5366004615336565b611b94565b3480156106e657600080fd5b506007546106fa906001600160401b031681565b6040516001600160401b0390911681526020016102d3565b34801561071e57600080fd5b506102c9611fda565b34801561073357600080fd5b506102b1610742366004615370565b6121c1565b34801561075357600080fd5b506102b161076236600461541b565b612329565b34801561077357600080fd5b506102b16107823660046152bb565b612610565b34801561079357600080fd5b506107a76107a23660046154bc565b612643565b6040516102d39190615519565b3480156107c057600080fd5b506102b16107cf3660046152bb565b612745565b3480156107e057600080fd5b506102b16107ef366004614f6b565b612834565b34801561080057600080fd5b506102c961080f36600461552c565b612927565b34801561082057600080fd5b506102b161082f366004614f6b565b612957565b34801561084057600080fd5b506102c961084f3660046152bb565b612bc5565b34801561086057600080fd5b5061089461086f3660046152bb565b600d6020526000908152604090205460ff81169061010090046001600160401b031682565b6040805192151583526001600160401b039091166020830152016102d3565b3480156108bf57600080fd5b506102b16108ce366004614f6b565b612be6565b3480156108df57600080fd5b506108f36108ee3660046152bb565b612c81565b6040516102d3959493929190615581565b34801561091057600080fd5b506102b161091f366004614f21565b612d5a565b34801561093057600080fd5b506102c961093f3660046152bb565b600f6020526000908152604090205481565b34801561095d57600080fd5b506102b161096c366004614f21565b612f1b565b610979612f2c565b60115460005b81811015610abd57826001600160a01b0316601182815481106109a4576109a46155d6565b6000918252602090912001546001600160a01b031603610aad5760116109cb600184615602565b815481106109db576109db6155d6565b600091825260209091200154601180546001600160a01b039092169183908110610a0757610a076155d6565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506011805480610a4657610a46615615565b6000828152602090819020600019908301810180546001600160a01b03191690559091019091556040516001600160a01b03851681527ff80a1a97fd42251f3c33cda98635e7399253033a6774fe37cd3f650b5282af3791015b60405180910390a1505050565b610ab68161562b565b905061097f565b50604051635428d44960e01b81526001600160a01b03831660048201526024015b60405180910390fd5b50565b610af2612f2c565b604080518082018252600091610b21919084906002908390839080828437600092019190915250612927915050565b6000818152600d602090815260409182902082518084019093525460ff811615158084526101009091046001600160401b03169183019190915291925090610b7f57604051631dfd6e1360e21b815260048101839052602401610ade565b6000828152600d60205260408120805468ffffffffffffffffff19169055600e54905b81811015610c515783600e8281548110610bbe57610bbe6155d6565b906000526020600020015403610c4157600e610bdb600184615602565b81548110610beb57610beb6155d6565b9060005260206000200154600e8281548110610c0957610c096155d6565b600091825260209091200155600e805480610c2657610c26615615565b60019003818190600052602060002001600090559055610c51565b610c4a8161562b565b9050610ba2565b507f9b6868e0eb737bcd72205360baa6bfd0ba4e4819a33ade2db384e8a8025639a5838360200151604051610c999291909182526001600160401b0316602082015260400190565b60405180910390a150505050565b81610cb181612f88565b610cb9612fdd565b610cc28361148d565b15610ce057604051631685ecdd60e31b815260040160405180910390fd5b610cea838361300b565b505050565b610cf7612fdd565b610cff612f2c565b600b54600160601b90046001600160601b0316610d1d8115156130ee565b600b80546bffffffffffffffffffffffff60601b19169055600a8054829190600c90610d5a908490600160601b90046001600160601b0316615644565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550610d9182826001600160601b031661310c565b5050565b6000610d9f612fdd565b60005a9050610324361115610dd157604051630f28961b60e01b81523660048201526103246024820152604401610ade565b6000610ddd8686613180565b90506000610df385836000015160200151613431565b60408301516060888101519293509163ffffffff16806001600160401b03811115610e2057610e20614f9b565b604051908082528060200260200182016040528015610e49578160200160208202803683370190505b50925060005b81811015610eb15760408051602081018590529081018290526060016040516020818303038152906040528051906020012060001c848281518110610e9657610e966155d6565b6020908102919091010152610eaa8161562b565b9050610e4f565b5050602080850180516000908152600f9092526040822082905551610ed7908a8561348c565b60208a8101516000908152600690915260409020805491925090601890610f0d90600160c01b90046001600160401b0316615664565b82546101009290920a6001600160401b0381810219909316918316021790915560808a01516001600160a01b03166000908152600460209081526040808320828e01518452909152902080549091600991610f7091600160481b9091041661568a565b91906101000a8154816001600160401b0302191690836001600160401b0316021790555060008960a0015160018b60a0015151610fad9190615602565b81518110610fbd57610fbd6155d6565b60209101015160f81c60011490506000610fd98887848d613530565b909950905080156110245760208088015160105460408051928352928201527f6ca648a381f22ead7e37773d934e64885dcf861fbfbb26c40354cbf0c4662d1a910160405180910390a15b5061103488828c60200151613568565b6020808b015187820151604080518781526001600160601b038d16948101949094528415159084015284151560608401528b1515608084015290917faeb4b4786571e184246d39587f659abf0e26f41f6a3358692250382c0cdb47b79060a00160405180910390a3505050505050505b9392505050565b6110b3612fdd565b6110bc816136c5565b6110e457604051635428d44960e01b81526001600160a01b0382166004820152602401610ade565b6000806000806110f386612c81565b945094505093509350336001600160a01b0316826001600160a01b03161461115d5760405162461bcd60e51b815260206004820152601660248201527f4e6f7420737562736372697074696f6e206f776e6572000000000000000000006044820152606401610ade565b6111668661148d565b156111b35760405162461bcd60e51b815260206004820152601660248201527f50656e64696e67207265717565737420657869737473000000000000000000006044820152606401610ade565b6040805160c0810182526001815260208082018990526001600160a01b03851682840152606082018490526001600160601b038088166080840152861660a083015291519091600091611208918491016156ad565b604051602081830303815290604052905061122288613730565b505060405163ce3f471960e01b81526001600160a01b0388169063ce3f4719906001600160601b0388169061125b908590600401615772565b6000604051808303818588803b15801561127457600080fd5b505af1158015611288573d6000803e3d6000fd5b50506002546001600160a01b0316158015935091506112b1905057506001600160601b03861615155b156113815760025460405163a9059cbb60e01b81526001600160a01b0389811660048301526001600160601b03891660248301529091169063a9059cbb906044016020604051808303816000875af1158015611311573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113359190615785565b6113815760405162461bcd60e51b815260206004820152601260248201527f696e73756666696369656e742066756e647300000000000000000000000000006044820152606401610ade565b600c805466ff0000000000001916660100000000000017905560005b8351811015611430578381815181106113b8576113b86155d6565b6020908102919091010151604051638ea9811760e01b81526001600160a01b038a8116600483015290911690638ea9811790602401600060405180830381600087803b15801561140757600080fd5b505af115801561141b573d6000803e3d6000fd5b50505050806114299061562b565b905061139d565b50600c805466ff00000000000019169055604080516001600160a01b0389168152602081018a90527fd63ca8cb945956747ee69bfdc3ea754c24a4caf7418db70e46052f7850be4187910160405180910390a15050505050505050565b600081815260056020526040812060020180548083036114b1575060009392505050565b60005b81811015611536576000600460008584815481106114d4576114d46155d6565b60009182526020808320909101546001600160a01b0316835282810193909352604091820181208982529092529020546001600160401b03600160481b90910416111561152657506001949350505050565b61152f8161562b565b90506114b4565b506000949350505050565b611549612fdd565b611551612f2c565b6002546001600160a01b031661157a5760405163c1f0c0a160e01b815260040160405180910390fd5b600b546001600160601b03166115918115156130ee565b600b80546bffffffffffffffffffffffff19169055600a80548291906000906115c49084906001600160601b0316615644565b82546101009290920a6001600160601b0381810219909316918316021790915560025460405163a9059cbb60e01b81526001600160a01b0386811660048301529285166024820152610d91935091169063a9059cbb906044015b6020604051808303816000875af115801561163d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116619190615785565b6130ee565b61166e612f2c565b611677816136c5565b156116a05760405163ac8a27ef60e01b81526001600160a01b0382166004820152602401610ade565b601180546001810182556000919091527f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c680180546001600160a01b0319166001600160a01b0383169081179091556040519081527fb7cabbfc11e66731fc77de0444614282023bcbd41d16781c753a431d0af016259060200160405180910390a150565b61172c612f2c565b6002546001600160a01b03161561175657604051631688c53760e11b815260040160405180910390fd5b600280546001600160a01b039384166001600160a01b03199182161790915560038054929093169116179055565b6001546001600160a01b031633146117de5760405162461bcd60e51b815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610ade565b60008054336001600160a01b0319808316821784556001805490911690556040516001600160a01b0390921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b61183d612f2c565b60408051808201825260009161186c919085906002908390839080828437600092019190915250612927915050565b6000818152600d602052604090205490915060ff16156118a257604051634a0b8fa760e01b815260048101829052602401610ade565b60408051808201825260018082526001600160401b0385811660208085018281526000888152600d835287812096518754925168ffffffffffffffffff1990931690151568ffffffffffffffff00191617610100929095169190910293909317909455600e805493840181559091527fbb7b4a454dc3493923482f07822329ed19e8244eff582cc204f8554c3620c3fd9091018490558251848152918201527f9b911b2c240bfbef3b6a8f7ed6ee321d1258bb2a3fe6becab52ac1cd3210afd39101610aa0565b611971612f2c565b600a544790600160601b90046001600160601b0316818111156119b1576040516354ced18160e11b81526004810182905260248101839052604401610ade565b81811015610cea5760006119c58284615602565b90506000846001600160a01b03168260405160006040518083038185875af1925050503d8060008114611a14576040519150601f19603f3d011682016040523d82523d6000602084013e611a19565b606091505b5050905080611a3b5760405163950b247960e01b815260040160405180910390fd5b604080516001600160a01b0387168152602081018490527f4aed7c8eed0496c8c19ea2681fcca25741c1602342e38b045d9f1e8e905d2e9c910160405180910390a15050505050565b611a8c612fdd565b600081815260056020526040902054611aad906001600160a01b03166138e2565b60008181526006602052604090208054600160601b90046001600160601b0316903490600c611adc83856157a2565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555034600a600c8282829054906101000a90046001600160601b0316611b2491906157a2565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550817f7603b205d03651ee812f803fccde89f1012e545a9c99f0abfea9cedd0fd8e902823484611b7791906157c2565b604080519283526020830191909152015b60405180910390a25050565b6000611b9e612fdd565b602080830135600081815260059092526040909120546001600160a01b0316611bda57604051630fb532db60e11b815260040160405180910390fd5b336000908152600460209081526040808320848452808352928190208151606081018352905460ff811615158083526001600160401b036101008304811695840195909552600160481b9091049093169181019190915290611c58576040516379bfd40160e01b815260048101849052336024820152604401610ade565b600c5461ffff16611c6f60608701604088016157d5565b61ffff161080611c92575060c8611c8c60608701604088016157d5565b61ffff16115b15611cd857611ca760608601604087016157d5565b600c5460405163539c34bb60e11b815261ffff92831660048201529116602482015260c86044820152606401610ade565b600c5462010000900463ffffffff16611cf760808701606088016157f0565b63ffffffff161115611d4757611d1360808601606087016157f0565b600c54604051637aebf00f60e11b815263ffffffff9283166004820152620100009091049091166024820152604401610ade565b6101f4611d5a60a08701608088016157f0565b63ffffffff161115611da057611d7660a08601608087016157f0565b6040516311ce1afb60e21b815263ffffffff90911660048201526101f46024820152604401610ade565b806020018051611daf90615664565b6001600160401b03169052604081018051611dc990615664565b6001600160401b03908116909152602082810151604080518935818501819052338284015260608201899052929094166080808601919091528151808603909101815260a08501825280519084012060c085019290925260e08085018390528151808603909101815261010090940190528251929091019190912060009190955090506000611e6b611e66611e6160a08a018a61580b565b613909565b61398a565b905085611e766139fb565b86611e8760808b0160608c016157f0565b611e9760a08c0160808d016157f0565b3386604051602001611eaf9796959493929190615858565b60405160208183030381529060405280519060200120600f600088815260200190815260200160002081905550336001600160a01b03168588600001357feb0e3652e0f44f417695e6e90f2f42c99b65cd7169074c5a654b16b9748c3a4e89868c6040016020810190611f2291906157d5565b8d6060016020810190611f3591906157f0565b8e6080016020810190611f4891906157f0565b89604051611f5b969594939291906158af565b60405180910390a45050600092835260209182526040928390208151815493830151929094015168ffffffffffffffffff1990931693151568ffffffffffffffff001916939093176101006001600160401b03928316021770ffffffffffffffff0000000000000000001916600160481b91909216021790555b919050565b6000611fe4612fdd565b6007546001600160401b031633611ffc600143615602565b6040516bffffffffffffffffffffffff19606093841b81166020830152914060348201523090921b1660548201526001600160c01b031960c083901b16606882015260700160408051601f19818403018152919052805160209091012091506120668160016158ee565b6007805467ffffffffffffffff19166001600160401b03928316179055604080516000808252608082018352602080830182815283850183815260608086018581528a86526006855287862093518454935191516001600160601b039182166001600160c01b031990951694909417600160601b91909216021777ffffffffffffffffffffffffffffffffffffffffffffffff16600160c01b9290981691909102969096179055835194850184523385528481018281528585018481528884526005835294909220855181546001600160a01b03199081166001600160a01b0392831617835593516001830180549095169116179092559251805192949391926121769260028501920190614e0f565b5061218691506008905084613a7c565b5060405133815283907f1d3015d7ba850fa198dc7b1a3f5d42779313a681035f77c8c03764c61005518d9060200160405180910390a2505090565b6121c9612fdd565b6002546001600160a01b031633146121f4576040516344b0e3c360e01b815260040160405180910390fd5b6020811461221557604051638129bbcd60e01b815260040160405180910390fd5b6000612223828401846152bb565b600081815260056020526040902054909150612247906001600160a01b03166138e2565b600081815260066020526040812080546001600160601b03169186919061226e83856157a2565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555084600a60008282829054906101000a90046001600160601b03166122b691906157a2565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550817f1ced9348ff549fceab2ac57cd3a9de38edaaab274b725ee82c23e8fc8c4eec7a82878461230991906157c2565b6040805192835260208301919091520160405180910390a2505050505050565b612331612f2c565b60c861ffff8a16111561236b5760405163539c34bb60e11b815261ffff8a1660048201819052602482015260c86044820152606401610ade565b6000851361238f576040516321ea67b360e11b815260048101869052602401610ade565b8363ffffffff168363ffffffff1611156123cc576040516313c06e5960e11b815263ffffffff808516600483015285166024820152604401610ade565b609b60ff831611156123fd57604051631d66288d60e11b815260ff83166004820152609b6024820152604401610ade565b609b60ff8216111561242e57604051631d66288d60e11b815260ff82166004820152609b6024820152604401610ade565b604080516101208101825261ffff8b1680825263ffffffff808c16602084018190526000848601528b8216606085018190528b8316608086018190528a841660a08701819052938a1660c0870181905260ff808b1660e08901819052908a16610100909801889052600c8054600160c01b90990260ff60c01b19600160b81b9093029290921661ffff60b81b19600160981b90940263ffffffff60981b19600160781b9099029890981676ffffffffffffffff00000000000000000000000000000019600160581b9096026effffffff000000000000000000000019670100000000000000909802979097166effffffffffffffffff000000000000196201000090990265ffffffffffff19909c16909a179a909a1796909616979097179390931791909116959095179290921793909316929092179190911790556010869055517f2c6b6b12413678366b05b145c5f00745bdd00e739131ab5de82484a50c9d78b6906125fd908b908b908b908b908b908b908b908b908b9061ffff99909916895263ffffffff97881660208a0152958716604089015293861660608801526080870192909252841660a086015290921660c084015260ff91821660e0840152166101008201526101200190565b60405180910390a1505050505050505050565b612618612f2c565b6000818152600560205260409020546001600160a01b0316612639816138e2565b610d91828261300b565b606060006126516008613a88565b905080841061267357604051631390f2a160e01b815260040160405180910390fd5b600061267f84866157c2565b90508181118061268d575083155b6126975780612699565b815b905060006126a78683615602565b9050806001600160401b038111156126c1576126c1614f9b565b6040519080825280602002602001820160405280156126ea578160200160208202803683370190505b50935060005b8181101561273a5761270d61270588836157c2565b600890613a92565b85828151811061271f5761271f6155d6565b60209081029190910101526127338161562b565b90506126f0565b505050505b92915050565b61274d612fdd565b6000818152600560205260409020546001600160a01b031661276e816138e2565b6000828152600560205260409020600101546001600160a01b031633146127c7576000828152600560205260409081902060010154905163d084e97560e01b81526001600160a01b039091166004820152602401610ade565b6000828152600560209081526040918290208054336001600160a01b03199182168117835560019092018054909116905582516001600160a01b03851681529182015283917fd4114ab6e9af9f597c52041f32d62dc57c5c4e4c0d4427006069635e216c93869101611b88565b8161283e81612f88565b612846612fdd565b6001600160a01b03821660009081526004602090815260408083208684529091529020805460ff16156128795750505050565b60008481526005602052604090206002018054606319016128ad576040516305a48e0f60e01b815260040160405180910390fd5b8154600160ff199091168117835581549081018255600082815260209081902090910180546001600160a01b0319166001600160a01b03871690811790915560405190815286917f1e980d04aa7648e205713e5e8ea3808672ac163d10936d36f91b2c88ac1575e191015b60405180910390a25050505050565b60008160405160200161293a9190615931565b604051602081830303815290604052805190602001209050919050565b8161296181612f88565b612969612fdd565b6129728361148d565b1561299057604051631685ecdd60e31b815260040160405180910390fd5b6001600160a01b038216600090815260046020908152604080832086845290915290205460ff166129e6576040516379bfd40160e01b8152600481018490526001600160a01b0383166024820152604401610ade565b600083815260056020908152604080832060020180548251818502810185019093528083529192909190830182828015612a4957602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612a2b575b50505050509050600060018251612a609190615602565b905060005b8251811015612b6957846001600160a01b0316838281518110612a8a57612a8a6155d6565b60200260200101516001600160a01b031603612b59576000838381518110612ab457612ab46155d6565b6020026020010151905080600560008981526020019081526020016000206002018381548110612ae657612ae66155d6565b600091825260208083209190910180546001600160a01b0319166001600160a01b039490941693909317909255888152600590915260409020600201805480612b3157612b31615615565b600082815260209020810160001990810180546001600160a01b031916905501905550612b69565b612b628161562b565b9050612a65565b506001600160a01b0384166000818152600460209081526040808320898452825291829020805460ff19169055905191825286917f32158c6058347c1601b2d12bc696ac6901d8a9a9aa3ba10c27ab0a983e8425a79101612918565b600e8181548110612bd557600080fd5b600091825260209091200154905081565b81612bf081612f88565b612bf8612fdd565b600083815260056020526040902060018101546001600160a01b03848116911614612c7b576001810180546001600160a01b0319166001600160a01b03851690811790915560408051338152602081019290925285917f21a4dad170a6bf476c31bbcf4a16628295b0e450672eec25d7c93308e05344a191015b60405180910390a25b50505050565b600081815260056020526040812054819081906001600160a01b03166060612ca8826138e2565b600086815260066020908152604080832054600583529281902060020180548251818502810185019093528083526001600160601b0380861695600160601b810490911694600160c01b9091046001600160401b0316938893929091839190830182828015612d4057602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612d22575b505050505090509450945094509450945091939590929450565b612d62612f2c565b6002546001600160a01b0316612d8b5760405163c1f0c0a160e01b815260040160405180910390fd5b6002546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa158015612dd4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612df8919061593f565b600a549091506001600160601b031681811115612e32576040516354ced18160e11b81526004810182905260248101839052604401610ade565b81811015610cea576000612e468284615602565b60025460405163a9059cbb60e01b81526001600160a01b0387811660048301526024820184905292935091169063a9059cbb906044016020604051808303816000875af1158015612e9b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ebf9190615785565b612edc57604051631f01ff1360e21b815260040160405180910390fd5b604080516001600160a01b0386168152602081018390527f59bfc682b673f8cbf945f1e454df9334834abf7dfe7f92237ca29ecb9b4366009101610c99565b612f23612f2c565b610ae781613a9e565b6000546001600160a01b03163314612f865760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610ade565b565b6000818152600560205260409020546001600160a01b0316612fa9816138e2565b336001600160a01b03821614610d9157604051636c51fda960e11b81526001600160a01b0382166004820152602401610ade565b600c546601000000000000900460ff1615612f865760405163769dd35360e11b815260040160405180910390fd5b60008061301784613730565b60025491935091506001600160a01b03161580159061303e57506001600160601b03821615155b156130865760025460405163a9059cbb60e01b81526001600160a01b0385811660048301526001600160601b038516602483015261308692169063a9059cbb9060440161161e565b61309983826001600160601b031661310c565b604080516001600160a01b03851681526001600160601b03808516602083015283169181019190915284907f8c74ce8b8cf87f5eb001275c8be27eb34ea2b62bfab6814fcc62192bb63e81c490606001612c72565b80610ae757604051631e9acf1760e31b815260040160405180910390fd5b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114613159576040519150601f19603f3d011682016040523d82523d6000602084013e61315e565b606091505b5050905080610cea5760405163950b247960e01b815260040160405180910390fd5b6040805160a081018252600060608201818152608083018290528252602082018190529181019190915260006131b98460000151612927565b6000818152600d602090815260409182902082518084019093525460ff811615158084526101009091046001600160401b0316918301919091529192509061321757604051631dfd6e1360e21b815260048101839052602401610ade565b6000828660800151604051602001613239929190918252602082015260400190565b60408051601f1981840301815291815281516020928301206000818152600f909352908220549092509081900361328357604051631b44092560e11b815260040160405180910390fd5b85516020808801516040808a015160608b015160808c015160a08d015193516132b2978a979096959101615958565b6040516020818303038152906040528051906020012081146132e75760405163354a450b60e21b815260040160405180910390fd5b60006132f68760000151613b47565b9050806133bf578651604051631d2827a760e31b81526001600160401b0390911660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063e9413d3890602401602060405180830381865afa15801561336d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613391919061593f565b9050806133bf57865160405163175dadad60e01b81526001600160401b039091166004820152602401610ade565b60008860800151826040516020016133e1929190918252602082015260400190565b6040516020818303038152906040528051906020012060001c905060006134088a83613c1a565b604080516060810182529788526020880196909652948601949094525092979650505050505050565b6000816001600160401b03163a111561348457821561345a57506001600160401b03811661273f565b60405163435e532d60e11b81523a60048201526001600160401b0383166024820152604401610ade565b503a92915050565b6000806000631fe543e360e01b86856040516024016134ac9291906159ab565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252600c805466ff000000000000191666010000000000001790559086015160808701519192506135169163ffffffff9091169083613c85565b600c805466ff000000000000191690559695505050505050565b600080831561354f57613544868685613cd1565b60009150915061355f565b61355a868685613de2565b915091505b94509492505050565b6000818152600660205260409020821561362c5780546001600160601b03600160601b90910481169085168110156135b357604051631e9acf1760e31b815260040160405180910390fd5b6135bd8582615644565b82546bffffffffffffffffffffffff60601b1916600160601b6001600160601b039283168102919091178455600b805488939192600c926136029286929004166157a2565b92506101000a8154816001600160601b0302191690836001600160601b0316021790555050612c7b565b80546001600160601b0390811690851681101561365c57604051631e9acf1760e31b815260040160405180910390fd5b6136668582615644565b82546bffffffffffffffffffffffff19166001600160601b03918216178355600b8054879260009161369a918591166157a2565b92506101000a8154816001600160601b0302191690836001600160601b031602179055505050505050565b601154600090815b8181101561372657836001600160a01b0316601182815481106136f2576136f26155d6565b6000918252602090912001546001600160a01b031603613716575060019392505050565b61371f8161562b565b90506136cd565b5060009392505050565b60008181526005602090815260408083206006909252822054600290910180546001600160601b0380841694600160601b90940416925b818110156137dc5760046000848381548110613785576137856155d6565b60009182526020808320909101546001600160a01b0316835282810193909352604091820181208982529092529020805470ffffffffffffffffffffffffffffffffff191690556137d58161562b565b9050613767565b50600085815260056020526040812080546001600160a01b031990811682556001820180549091169055906138146002830182614e74565b5050600085815260066020526040812055613830600886613fd4565b506001600160601b0384161561388357600a805485919060009061385e9084906001600160601b0316615644565b92506101000a8154816001600160601b0302191690836001600160601b031602179055505b6001600160601b038316156138db5782600a600c8282829054906101000a90046001600160601b03166138b69190615644565b92506101000a8154816001600160601b0302191690836001600160601b031602179055505b5050915091565b6001600160a01b038116610ae757604051630fb532db60e11b815260040160405180910390fd5b6040805160208101909152600081526000829003613936575060408051602081019091526000815261273f565b63125fa26760e31b61394883856159cc565b6001600160e01b0319161461397057604051632923fee760e11b815260040160405180910390fd5b61397d82600481866159fc565b8101906110a49190615a26565b60607f92fd13387c7fe7befbc38d303d6468778fb9731bc4583f17d92989c6fcfdeaaa826040516024016139c391511515815260200190565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915292915050565b600046613a0781613fe0565b15613a755760646001600160a01b031663a3b1b31d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613a4b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a6f919061593f565b91505090565b4391505090565b60006110a48383614003565b600061273f825490565b60006110a48383614052565b336001600160a01b03821603613af65760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610ade565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600046613b5381613fe0565b15613c0b57610100836001600160401b0316613b6d6139fb565b613b779190615602565b1180613b935750613b866139fb565b836001600160401b031610155b15613ba15750600092915050565b6040516315a03d4160e11b81526001600160401b0384166004820152606490632b407a82906024015b602060405180830381865afa158015613be7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110a4919061593f565b50506001600160401b03164090565b6000613c4e8360000151846020015185604001518660600151868860a001518960c001518a60e001518b610100015161407c565b60038360200151604051602001613c66929190615a71565b60408051601f1981840301815291905280516020909101209392505050565b60005a611388811015613c9757600080fd5b611388810390508460408204820311613caf57600080fd5b50823b613cbb57600080fd5b60008083516020850160008789f1949350505050565b600080613d146000368080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506142a792505050565b905060005a600c54613d34908890600160581b900463ffffffff166157c2565b613d3e9190615602565b613d489086615a85565b600c54909150600090613d6d90600160781b900463ffffffff1664e8d4a51000615a85565b90508415613db957600c548190606490600160b81b900460ff16613d9185876157c2565b613d9b9190615a85565b613da59190615ab2565b613daf91906157c2565b93505050506110a4565b600c548190606490613dd590600160b81b900460ff1682615ac6565b60ff16613d9185876157c2565b600080600080613df0614387565b9150915060008213613e18576040516321ea67b360e11b815260048101839052602401610ade565b6000613e5a6000368080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506142a792505050565b9050600083825a600c54613e7c908d90600160581b900463ffffffff166157c2565b613e869190615602565b613e90908b615a85565b613e9a91906157c2565b613eac90670de0b6b3a7640000615a85565b613eb69190615ab2565b600c54909150600090613edf9063ffffffff600160981b8204811691600160781b900416615adf565b613ef49063ffffffff1664e8d4a51000615a85565b9050600085613f0b83670de0b6b3a7640000615a85565b613f159190615ab2565b905060008915613f5657600c548290606490613f3b90600160c01b900460ff1687615a85565b613f459190615ab2565b613f4f91906157c2565b9050613f96565b600c548290606490613f7290600160c01b900460ff1682615ac6565b613f7f9060ff1687615a85565b613f899190615ab2565b613f9391906157c2565b90505b6b033b2e3c9fd0803ce8000000811115613fc35760405163e80fa38160e01b815260040160405180910390fd5b9b949a509398505050505050505050565b60006110a48383614452565b600061a4b1821480613ff4575062066eed82145b8061273f57505062066eee1490565b600081815260018301602052604081205461404a5750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915561273f565b50600061273f565b6000826000018281548110614069576140696155d6565b9060005260206000200154905092915050565b6140858961454c565b6140d15760405162461bcd60e51b815260206004820152601a60248201527f7075626c6963206b6579206973206e6f74206f6e2063757276650000000000006044820152606401610ade565b6140da8861454c565b6141265760405162461bcd60e51b815260206004820152601560248201527f67616d6d61206973206e6f74206f6e20637572766500000000000000000000006044820152606401610ade565b61412f8361454c565b61417b5760405162461bcd60e51b815260206004820152601d60248201527f6347616d6d615769746e657373206973206e6f74206f6e2063757276650000006044820152606401610ade565b6141848261454c565b6141d05760405162461bcd60e51b815260206004820152601c60248201527f73486173685769746e657373206973206e6f74206f6e206375727665000000006044820152606401610ade565b6141dc878a8887614625565b6142285760405162461bcd60e51b815260206004820152601960248201527f6164647228632a706b2b732a6729213d5f755769746e657373000000000000006044820152606401610ade565b60006142348a87614748565b90506000614247898b878b8689896147ac565b90506000614258838d8d8a866148d8565b9050808a146142995760405162461bcd60e51b815260206004820152600d60248201526c34b73b30b634b210383937b7b360991b6044820152606401610ade565b505050505050505050505050565b6000466142b381613fe0565b156142f757606c6001600160a01b031663c6f7de0e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613be7573d6000803e3d6000fd5b61430081614918565b1561437e5773420000000000000000000000000000000000000f6001600160a01b03166349948e0e84604051806080016040528060488152602001615c3c60489139604051602001614353929190615afc565b6040516020818303038152906040526040518263ffffffff1660e01b8152600401613bca9190615772565b50600092915050565b600c5460035460408051633fabe5a360e21b81529051600093849367010000000000000090910463ffffffff169284926001600160a01b039092169163feaf968c9160048082019260a0929091908290030181865afa1580156143ee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144129190615b45565b50919650909250505063ffffffff82161580159061443e57506144358142615602565b8263ffffffff16105b9250821561444c5760105493505b50509091565b6000818152600183016020526040812054801561453b576000614476600183615602565b855490915060009061448a90600190615602565b90508181146144ef5760008660000182815481106144aa576144aa6155d6565b90600052602060002001549050808760000184815481106144cd576144cd6155d6565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061450057614500615615565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505061273f565b600091505061273f565b5092915050565b80516000906401000003d019116145a55760405162461bcd60e51b815260206004820152601260248201527f696e76616c696420782d6f7264696e61746500000000000000000000000000006044820152606401610ade565b60208201516401000003d019116145fe5760405162461bcd60e51b815260206004820152601260248201527f696e76616c696420792d6f7264696e61746500000000000000000000000000006044820152606401610ade565b60208201516401000003d01990800961461e8360005b602002015161495f565b1492915050565b60006001600160a01b03821661466b5760405162461bcd60e51b815260206004820152600b60248201526a626164207769746e65737360a81b6044820152606401610ade565b60208401516000906001161561468257601c614685565b601b5b9050600070014551231950b75fc4402da1732fc9bebe1985876000602002015109865170014551231950b75fc4402da1732fc9bebe19918203925060009190890987516040805160008082526020820180845287905260ff88169282019290925260608101929092526080820183905291925060019060a0016020604051602081039080840390855afa158015614720573d6000803e3d6000fd5b5050604051601f1901516001600160a01b039081169088161495505050505050949350505050565b614750614e92565b61477d6001848460405160200161476993929190615b95565b604051602081830303815290604052614983565b90505b6147898161454c565b61273f5780516040805160208101929092526147a59101614769565b9050614780565b6147b4614e92565b825186516401000003d01991829006919006036148135760405162461bcd60e51b815260206004820152601e60248201527f706f696e747320696e2073756d206d7573742062652064697374696e637400006044820152606401610ade565b61481e8789886149d0565b61486a5760405162461bcd60e51b815260206004820152601660248201527f4669727374206d756c20636865636b206661696c6564000000000000000000006044820152606401610ade565b6148758486856149d0565b6148c15760405162461bcd60e51b815260206004820152601760248201527f5365636f6e64206d756c20636865636b206661696c65640000000000000000006044820152606401610ade565b6148cc868484614afb565b98975050505050505050565b6000600286868685876040516020016148f696959493929190615bb6565b60408051601f1981840301815291905280516020909101209695505050505050565b6000600a82148061492a57506101a482145b80614937575062aa37dc82145b80614943575061210582145b80614950575062014a3382145b8061273f57505062014a341490565b6000806401000003d01980848509840990506401000003d019600782089392505050565b61498b614e92565b61499482614bc2565b81526149a96149a4826000614614565b614bfd565b6020820181905260029006600103611fd5576020810180516401000003d019039052919050565b600082600003614a105760405162461bcd60e51b815260206004820152600b60248201526a3d32b9379039b1b0b630b960a91b6044820152606401610ade565b83516020850151600090614a2690600290615c15565b15614a3257601c614a35565b601b5b9050600070014551231950b75fc4402da1732fc9bebe198387096040805160008082526020820180845281905260ff86169282019290925260608101869052608081018390529192509060019060a0016020604051602081039080840390855afa158015614aa7573d6000803e3d6000fd5b505050602060405103519050600086604051602001614ac69190615c29565b60408051601f1981840301815291905280516020909101206001600160a01b0392831692169190911498975050505050505050565b614b03614e92565b835160208086015185519186015160009384938493614b2493909190614c1d565b919450925090506401000003d019858209600114614b845760405162461bcd60e51b815260206004820152601960248201527f696e765a206d75737420626520696e7665727365206f66207a000000000000006044820152606401610ade565b60405180604001604052806401000003d01980614ba357614ba3615a9c565b87860981526020016401000003d0198785099052979650505050505050565b805160208201205b6401000003d0198110611fd557604080516020808201939093528151808203840181529082019091528051910120614bca565b600061273f826002614c166401000003d01960016157c2565b901c614cfd565b60008080600180826401000003d019896401000003d019038808905060006401000003d0198b6401000003d019038a0890506000614c5d83838585614da2565b9098509050614c6e88828e88614dc6565b9098509050614c7f88828c87614dc6565b90985090506000614c928d878b85614dc6565b9098509050614ca388828686614da2565b9098509050614cb488828e89614dc6565b9098509050818114614ce9576401000003d019818a0998506401000003d01982890997506401000003d0198183099650614ced565b8196505b5050505050509450945094915050565b600080614d08614eb0565b6020808252818101819052604082015260608101859052608081018490526401000003d01960a0820152614d3a614ece565b60208160c0846005600019fa925082600003614d985760405162461bcd60e51b815260206004820152601260248201527f6269674d6f64457870206661696c7572652100000000000000000000000000006044820152606401610ade565b5195945050505050565b6000806401000003d0198487096401000003d0198487099097909650945050505050565b600080806401000003d019878509905060006401000003d01987876401000003d019030990506401000003d0198183086401000003d01986890990999098509650505050505050565b828054828255906000526020600020908101928215614e64579160200282015b82811115614e6457825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190614e2f565b50614e70929150614eec565b5090565b5080546000825590600052602060002090810190610ae79190614eec565b60405180604001604052806002906020820280368337509192915050565b6040518060c001604052806006906020820280368337509192915050565b60405180602001604052806001906020820280368337509192915050565b5b80821115614e705760008155600101614eed565b6001600160a01b0381168114610ae757600080fd5b8035611fd581614f01565b600060208284031215614f3357600080fd5b81356110a481614f01565b806040810183101561273f57600080fd5b600060408284031215614f6157600080fd5b6110a48383614f3e565b60008060408385031215614f7e57600080fd5b823591506020830135614f9081614f01565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b60405160c081016001600160401b0381118282101715614fd357614fd3614f9b565b60405290565b60405161012081016001600160401b0381118282101715614fd357614fd3614f9b565b604051601f8201601f191681016001600160401b038111828210171561502457615024614f9b565b604052919050565b600082601f83011261503d57600080fd5b604051604081018181106001600160401b038211171561505f5761505f614f9b565b806040525080604084018581111561507657600080fd5b845b81811015615090578035835260209283019201615078565b509195945050505050565b80356001600160401b0381168114611fd557600080fd5b803563ffffffff81168114611fd557600080fd5b600060c082840312156150d857600080fd5b6150e0614fb1565b90506150eb8261509b565b815260208083013581830152615103604084016150b2565b6040830152615114606084016150b2565b6060830152608083013561512781614f01565b608083015260a08301356001600160401b038082111561514657600080fd5b818501915085601f83011261515a57600080fd5b81358181111561516c5761516c614f9b565b61517e601f8201601f19168501614ffc565b9150808252868482850101111561519457600080fd5b80848401858401376000848284010152508060a085015250505092915050565b8015158114610ae757600080fd5b8035611fd5816151b4565b60008060008385036101e08112156151e457600080fd5b6101a0808212156151f457600080fd5b6151fc614fd9565b9150615208878761502c565b8252615217876040880161502c565b60208301526080860135604083015260a0860135606083015260c0860135608083015261524660e08701614f16565b60a083015261010061525a8882890161502c565b60c084015261526d88610140890161502c565b60e0840152610180870135908301529093508401356001600160401b0381111561529657600080fd5b6152a2868287016150c6565b9250506152b26101c085016151c2565b90509250925092565b6000602082840312156152cd57600080fd5b5035919050565b600080604083850312156152e757600080fd5b82356152f281614f01565b91506020830135614f9081614f01565b6000806060838503121561531557600080fd5b61531f8484614f3e565b915061532d6040840161509b565b90509250929050565b60006020828403121561534857600080fd5b81356001600160401b0381111561535e57600080fd5b820160c081850312156110a457600080fd5b6000806000806060858703121561538657600080fd5b843561539181614f01565b93506020850135925060408501356001600160401b03808211156153b457600080fd5b818701915087601f8301126153c857600080fd5b8135818111156153d757600080fd5b8860208285010111156153e957600080fd5b95989497505060200194505050565b803561ffff81168114611fd557600080fd5b803560ff81168114611fd557600080fd5b60008060008060008060008060006101208a8c03121561543a57600080fd5b6154438a6153f8565b985061545160208b016150b2565b975061545f60408b016150b2565b965061546d60608b016150b2565b955060808a0135945061548260a08b016150b2565b935061549060c08b016150b2565b925061549e60e08b0161540a565b91506154ad6101008b0161540a565b90509295985092959850929598565b600080604083850312156154cf57600080fd5b50508035926020909101359150565b600081518084526020808501945080840160005b8381101561550e578151875295820195908201906001016154f2565b509495945050505050565b6020815260006110a460208301846154de565b60006040828403121561553e57600080fd5b6110a4838361502c565b600081518084526020808501945080840160005b8381101561550e5781516001600160a01b03168752958201959082019060010161555c565b60006001600160601b0380881683528087166020840152506001600160401b03851660408301526001600160a01b038416606083015260a060808301526155cb60a0830184615548565b979650505050505050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b8181038181111561273f5761273f6155ec565b634e487b7160e01b600052603160045260246000fd5b60006001820161563d5761563d6155ec565b5060010190565b6001600160601b03828116828216039080821115614545576145456155ec565b60006001600160401b03808316818103615680576156806155ec565b6001019392505050565b60006001600160401b038216806156a3576156a36155ec565b6000190192915050565b6020815260ff8251166020820152602082015160408201526001600160a01b0360408301511660608201526000606083015160c060808401526156f360e0840182615548565b905060808401516001600160601b0380821660a08601528060a08701511660c086015250508091505092915050565b60005b8381101561573d578181015183820152602001615725565b50506000910152565b6000815180845261575e816020860160208601615722565b601f01601f19169290920160200192915050565b6020815260006110a46020830184615746565b60006020828403121561579757600080fd5b81516110a4816151b4565b6001600160601b03818116838216019080821115614545576145456155ec565b8082018082111561273f5761273f6155ec565b6000602082840312156157e757600080fd5b6110a4826153f8565b60006020828403121561580257600080fd5b6110a4826150b2565b6000808335601e1984360301811261582257600080fd5b8301803591506001600160401b0382111561583c57600080fd5b60200191503681900382131561585157600080fd5b9250929050565b878152866020820152856040820152600063ffffffff80871660608401528086166080840152506001600160a01b03841660a083015260e060c08301526158a260e0830184615746565b9998505050505050505050565b86815285602082015261ffff85166040820152600063ffffffff808616606084015280851660808401525060c060a08301526148cc60c0830184615746565b6001600160401b03818116838216019080821115614545576145456155ec565b8060005b6002811015612c7b578151845260209384019390910190600101615912565b6040810161273f828461590e565b60006020828403121561595157600080fd5b5051919050565b8781526001600160401b0387166020820152856040820152600063ffffffff80871660608401528086166080840152506001600160a01b03841660a083015260e060c08301526158a260e0830184615746565b8281526040602082015260006159c460408301846154de565b949350505050565b6001600160e01b031981358181169160048510156159f45780818660040360031b1b83161692505b505092915050565b60008085851115615a0c57600080fd5b83861115615a1957600080fd5b5050820193919092039150565b600060208284031215615a3857600080fd5b604051602081018181106001600160401b0382111715615a5a57615a5a614f9b565b6040528235615a68816151b4565b81529392505050565b828152606081016110a4602083018461590e565b808202811582820484141761273f5761273f6155ec565b634e487b7160e01b600052601260045260246000fd5b600082615ac157615ac1615a9c565b500490565b60ff818116838216019081111561273f5761273f6155ec565b63ffffffff828116828216039080821115614545576145456155ec565b60008351615b0e818460208801615722565b835190830190615b22818360208801615722565b01949350505050565b805169ffffffffffffffffffff81168114611fd557600080fd5b600080600080600060a08688031215615b5d57600080fd5b615b6686615b2b565b9450602086015193506040860151925060608601519150615b8960808701615b2b565b90509295509295909350565b838152615ba5602082018461590e565b606081019190915260800192915050565b868152615bc6602082018761590e565b615bd3606082018661590e565b615be060a082018561590e565b615bed60e082018461590e565b60609190911b6bffffffffffffffffffffffff19166101208201526101340195945050505050565b600082615c2457615c24615a9c565b500690565b615c33818361590e565b60400191905056fe307866666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666a164736f6c6343000813000a", +} + +var VRFCoordinatorTestV25ABI = VRFCoordinatorTestV25MetaData.ABI + +var VRFCoordinatorTestV25Bin = VRFCoordinatorTestV25MetaData.Bin + +func DeployVRFCoordinatorTestV25(auth *bind.TransactOpts, backend bind.ContractBackend, blockhashStore common.Address) (common.Address, *types.Transaction, *VRFCoordinatorTestV25, error) { + parsed, err := VRFCoordinatorTestV25MetaData.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(VRFCoordinatorTestV25Bin), backend, blockhashStore) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &VRFCoordinatorTestV25{address: address, abi: *parsed, VRFCoordinatorTestV25Caller: VRFCoordinatorTestV25Caller{contract: contract}, VRFCoordinatorTestV25Transactor: VRFCoordinatorTestV25Transactor{contract: contract}, VRFCoordinatorTestV25Filterer: VRFCoordinatorTestV25Filterer{contract: contract}}, nil +} + +type VRFCoordinatorTestV25 struct { + address common.Address + abi abi.ABI + VRFCoordinatorTestV25Caller + VRFCoordinatorTestV25Transactor + VRFCoordinatorTestV25Filterer +} + +type VRFCoordinatorTestV25Caller struct { + contract *bind.BoundContract +} + +type VRFCoordinatorTestV25Transactor struct { + contract *bind.BoundContract +} + +type VRFCoordinatorTestV25Filterer struct { + contract *bind.BoundContract +} + +type VRFCoordinatorTestV25Session struct { + Contract *VRFCoordinatorTestV25 + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type VRFCoordinatorTestV25CallerSession struct { + Contract *VRFCoordinatorTestV25Caller + CallOpts bind.CallOpts +} + +type VRFCoordinatorTestV25TransactorSession struct { + Contract *VRFCoordinatorTestV25Transactor + TransactOpts bind.TransactOpts +} + +type VRFCoordinatorTestV25Raw struct { + Contract *VRFCoordinatorTestV25 +} + +type VRFCoordinatorTestV25CallerRaw struct { + Contract *VRFCoordinatorTestV25Caller +} + +type VRFCoordinatorTestV25TransactorRaw struct { + Contract *VRFCoordinatorTestV25Transactor +} + +func NewVRFCoordinatorTestV25(address common.Address, backend bind.ContractBackend) (*VRFCoordinatorTestV25, error) { + abi, err := abi.JSON(strings.NewReader(VRFCoordinatorTestV25ABI)) + if err != nil { + return nil, err + } + contract, err := bindVRFCoordinatorTestV25(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV25{address: address, abi: abi, VRFCoordinatorTestV25Caller: VRFCoordinatorTestV25Caller{contract: contract}, VRFCoordinatorTestV25Transactor: VRFCoordinatorTestV25Transactor{contract: contract}, VRFCoordinatorTestV25Filterer: VRFCoordinatorTestV25Filterer{contract: contract}}, nil +} + +func NewVRFCoordinatorTestV25Caller(address common.Address, caller bind.ContractCaller) (*VRFCoordinatorTestV25Caller, error) { + contract, err := bindVRFCoordinatorTestV25(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV25Caller{contract: contract}, nil +} + +func NewVRFCoordinatorTestV25Transactor(address common.Address, transactor bind.ContractTransactor) (*VRFCoordinatorTestV25Transactor, error) { + contract, err := bindVRFCoordinatorTestV25(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV25Transactor{contract: contract}, nil +} + +func NewVRFCoordinatorTestV25Filterer(address common.Address, filterer bind.ContractFilterer) (*VRFCoordinatorTestV25Filterer, error) { + contract, err := bindVRFCoordinatorTestV25(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV25Filterer{contract: contract}, nil +} + +func bindVRFCoordinatorTestV25(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := VRFCoordinatorTestV25MetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Raw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _VRFCoordinatorTestV25.Contract.VRFCoordinatorTestV25Caller.contract.Call(opts, result, method, params...) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Raw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.VRFCoordinatorTestV25Transactor.contract.Transfer(opts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Raw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.VRFCoordinatorTestV25Transactor.contract.Transact(opts, method, params...) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25CallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _VRFCoordinatorTestV25.Contract.contract.Call(opts, result, method, params...) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25TransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.contract.Transfer(opts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25TransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.contract.Transact(opts, method, params...) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Caller) BLOCKHASHSTORE(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _VRFCoordinatorTestV25.contract.Call(opts, &out, "BLOCKHASH_STORE") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) BLOCKHASHSTORE() (common.Address, error) { + return _VRFCoordinatorTestV25.Contract.BLOCKHASHSTORE(&_VRFCoordinatorTestV25.CallOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25CallerSession) BLOCKHASHSTORE() (common.Address, error) { + return _VRFCoordinatorTestV25.Contract.BLOCKHASHSTORE(&_VRFCoordinatorTestV25.CallOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Caller) LINK(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _VRFCoordinatorTestV25.contract.Call(opts, &out, "LINK") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) LINK() (common.Address, error) { + return _VRFCoordinatorTestV25.Contract.LINK(&_VRFCoordinatorTestV25.CallOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25CallerSession) LINK() (common.Address, error) { + return _VRFCoordinatorTestV25.Contract.LINK(&_VRFCoordinatorTestV25.CallOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Caller) LINKNATIVEFEED(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _VRFCoordinatorTestV25.contract.Call(opts, &out, "LINK_NATIVE_FEED") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) LINKNATIVEFEED() (common.Address, error) { + return _VRFCoordinatorTestV25.Contract.LINKNATIVEFEED(&_VRFCoordinatorTestV25.CallOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25CallerSession) LINKNATIVEFEED() (common.Address, error) { + return _VRFCoordinatorTestV25.Contract.LINKNATIVEFEED(&_VRFCoordinatorTestV25.CallOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Caller) MAXCONSUMERS(opts *bind.CallOpts) (uint16, error) { + var out []interface{} + err := _VRFCoordinatorTestV25.contract.Call(opts, &out, "MAX_CONSUMERS") + + if err != nil { + return *new(uint16), err + } + + out0 := *abi.ConvertType(out[0], new(uint16)).(*uint16) + + return out0, err + +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) MAXCONSUMERS() (uint16, error) { + return _VRFCoordinatorTestV25.Contract.MAXCONSUMERS(&_VRFCoordinatorTestV25.CallOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25CallerSession) MAXCONSUMERS() (uint16, error) { + return _VRFCoordinatorTestV25.Contract.MAXCONSUMERS(&_VRFCoordinatorTestV25.CallOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Caller) MAXNUMWORDS(opts *bind.CallOpts) (uint32, error) { + var out []interface{} + err := _VRFCoordinatorTestV25.contract.Call(opts, &out, "MAX_NUM_WORDS") + + if err != nil { + return *new(uint32), err + } + + out0 := *abi.ConvertType(out[0], new(uint32)).(*uint32) + + return out0, err + +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) MAXNUMWORDS() (uint32, error) { + return _VRFCoordinatorTestV25.Contract.MAXNUMWORDS(&_VRFCoordinatorTestV25.CallOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25CallerSession) MAXNUMWORDS() (uint32, error) { + return _VRFCoordinatorTestV25.Contract.MAXNUMWORDS(&_VRFCoordinatorTestV25.CallOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Caller) MAXREQUESTCONFIRMATIONS(opts *bind.CallOpts) (uint16, error) { + var out []interface{} + err := _VRFCoordinatorTestV25.contract.Call(opts, &out, "MAX_REQUEST_CONFIRMATIONS") + + if err != nil { + return *new(uint16), err + } + + out0 := *abi.ConvertType(out[0], new(uint16)).(*uint16) + + return out0, err + +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) MAXREQUESTCONFIRMATIONS() (uint16, error) { + return _VRFCoordinatorTestV25.Contract.MAXREQUESTCONFIRMATIONS(&_VRFCoordinatorTestV25.CallOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25CallerSession) MAXREQUESTCONFIRMATIONS() (uint16, error) { + return _VRFCoordinatorTestV25.Contract.MAXREQUESTCONFIRMATIONS(&_VRFCoordinatorTestV25.CallOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Caller) GetActiveSubscriptionIds(opts *bind.CallOpts, startIndex *big.Int, maxCount *big.Int) ([]*big.Int, error) { + var out []interface{} + err := _VRFCoordinatorTestV25.contract.Call(opts, &out, "getActiveSubscriptionIds", startIndex, maxCount) + + if err != nil { + return *new([]*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new([]*big.Int)).(*[]*big.Int) + + return out0, err + +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) GetActiveSubscriptionIds(startIndex *big.Int, maxCount *big.Int) ([]*big.Int, error) { + return _VRFCoordinatorTestV25.Contract.GetActiveSubscriptionIds(&_VRFCoordinatorTestV25.CallOpts, startIndex, maxCount) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25CallerSession) GetActiveSubscriptionIds(startIndex *big.Int, maxCount *big.Int) ([]*big.Int, error) { + return _VRFCoordinatorTestV25.Contract.GetActiveSubscriptionIds(&_VRFCoordinatorTestV25.CallOpts, startIndex, maxCount) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Caller) GetSubscription(opts *bind.CallOpts, subId *big.Int) (GetSubscription, + + error) { + var out []interface{} + err := _VRFCoordinatorTestV25.contract.Call(opts, &out, "getSubscription", subId) + + outstruct := new(GetSubscription) + if err != nil { + return *outstruct, err + } + + outstruct.Balance = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + outstruct.NativeBalance = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int) + outstruct.ReqCount = *abi.ConvertType(out[2], new(uint64)).(*uint64) + outstruct.SubOwner = *abi.ConvertType(out[3], new(common.Address)).(*common.Address) + outstruct.Consumers = *abi.ConvertType(out[4], new([]common.Address)).(*[]common.Address) + + return *outstruct, err + +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) GetSubscription(subId *big.Int) (GetSubscription, + + error) { + return _VRFCoordinatorTestV25.Contract.GetSubscription(&_VRFCoordinatorTestV25.CallOpts, subId) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25CallerSession) GetSubscription(subId *big.Int) (GetSubscription, + + error) { + return _VRFCoordinatorTestV25.Contract.GetSubscription(&_VRFCoordinatorTestV25.CallOpts, subId) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Caller) HashOfKey(opts *bind.CallOpts, publicKey [2]*big.Int) ([32]byte, error) { + var out []interface{} + err := _VRFCoordinatorTestV25.contract.Call(opts, &out, "hashOfKey", publicKey) + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) HashOfKey(publicKey [2]*big.Int) ([32]byte, error) { + return _VRFCoordinatorTestV25.Contract.HashOfKey(&_VRFCoordinatorTestV25.CallOpts, publicKey) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25CallerSession) HashOfKey(publicKey [2]*big.Int) ([32]byte, error) { + return _VRFCoordinatorTestV25.Contract.HashOfKey(&_VRFCoordinatorTestV25.CallOpts, publicKey) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Caller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _VRFCoordinatorTestV25.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) Owner() (common.Address, error) { + return _VRFCoordinatorTestV25.Contract.Owner(&_VRFCoordinatorTestV25.CallOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25CallerSession) Owner() (common.Address, error) { + return _VRFCoordinatorTestV25.Contract.Owner(&_VRFCoordinatorTestV25.CallOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Caller) PendingRequestExists(opts *bind.CallOpts, subId *big.Int) (bool, error) { + var out []interface{} + err := _VRFCoordinatorTestV25.contract.Call(opts, &out, "pendingRequestExists", subId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) PendingRequestExists(subId *big.Int) (bool, error) { + return _VRFCoordinatorTestV25.Contract.PendingRequestExists(&_VRFCoordinatorTestV25.CallOpts, subId) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25CallerSession) PendingRequestExists(subId *big.Int) (bool, error) { + return _VRFCoordinatorTestV25.Contract.PendingRequestExists(&_VRFCoordinatorTestV25.CallOpts, subId) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Caller) SConfig(opts *bind.CallOpts) (SConfig, + + error) { + var out []interface{} + err := _VRFCoordinatorTestV25.contract.Call(opts, &out, "s_config") + + outstruct := new(SConfig) + if err != nil { + return *outstruct, err + } + + outstruct.MinimumRequestConfirmations = *abi.ConvertType(out[0], new(uint16)).(*uint16) + outstruct.MaxGasLimit = *abi.ConvertType(out[1], new(uint32)).(*uint32) + outstruct.ReentrancyLock = *abi.ConvertType(out[2], new(bool)).(*bool) + outstruct.StalenessSeconds = *abi.ConvertType(out[3], new(uint32)).(*uint32) + outstruct.GasAfterPaymentCalculation = *abi.ConvertType(out[4], new(uint32)).(*uint32) + outstruct.FulfillmentFlatFeeNativePPM = *abi.ConvertType(out[5], new(uint32)).(*uint32) + outstruct.FulfillmentFlatFeeLinkDiscountPPM = *abi.ConvertType(out[6], new(uint32)).(*uint32) + outstruct.NativePremiumPercentage = *abi.ConvertType(out[7], new(uint8)).(*uint8) + outstruct.LinkPremiumPercentage = *abi.ConvertType(out[8], new(uint8)).(*uint8) + + return *outstruct, err + +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) SConfig() (SConfig, + + error) { + return _VRFCoordinatorTestV25.Contract.SConfig(&_VRFCoordinatorTestV25.CallOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25CallerSession) SConfig() (SConfig, + + error) { + return _VRFCoordinatorTestV25.Contract.SConfig(&_VRFCoordinatorTestV25.CallOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Caller) SCurrentSubNonce(opts *bind.CallOpts) (uint64, error) { + var out []interface{} + err := _VRFCoordinatorTestV25.contract.Call(opts, &out, "s_currentSubNonce") + + if err != nil { + return *new(uint64), err + } + + out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) + + return out0, err + +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) SCurrentSubNonce() (uint64, error) { + return _VRFCoordinatorTestV25.Contract.SCurrentSubNonce(&_VRFCoordinatorTestV25.CallOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25CallerSession) SCurrentSubNonce() (uint64, error) { + return _VRFCoordinatorTestV25.Contract.SCurrentSubNonce(&_VRFCoordinatorTestV25.CallOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Caller) SFallbackWeiPerUnitLink(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _VRFCoordinatorTestV25.contract.Call(opts, &out, "s_fallbackWeiPerUnitLink") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) SFallbackWeiPerUnitLink() (*big.Int, error) { + return _VRFCoordinatorTestV25.Contract.SFallbackWeiPerUnitLink(&_VRFCoordinatorTestV25.CallOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25CallerSession) SFallbackWeiPerUnitLink() (*big.Int, error) { + return _VRFCoordinatorTestV25.Contract.SFallbackWeiPerUnitLink(&_VRFCoordinatorTestV25.CallOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Caller) SProvingKeyHashes(opts *bind.CallOpts, arg0 *big.Int) ([32]byte, error) { + var out []interface{} + err := _VRFCoordinatorTestV25.contract.Call(opts, &out, "s_provingKeyHashes", arg0) + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) SProvingKeyHashes(arg0 *big.Int) ([32]byte, error) { + return _VRFCoordinatorTestV25.Contract.SProvingKeyHashes(&_VRFCoordinatorTestV25.CallOpts, arg0) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25CallerSession) SProvingKeyHashes(arg0 *big.Int) ([32]byte, error) { + return _VRFCoordinatorTestV25.Contract.SProvingKeyHashes(&_VRFCoordinatorTestV25.CallOpts, arg0) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Caller) SProvingKeys(opts *bind.CallOpts, arg0 [32]byte) (SProvingKeys, + + error) { + var out []interface{} + err := _VRFCoordinatorTestV25.contract.Call(opts, &out, "s_provingKeys", arg0) + + outstruct := new(SProvingKeys) + if err != nil { + return *outstruct, err + } + + outstruct.Exists = *abi.ConvertType(out[0], new(bool)).(*bool) + outstruct.MaxGas = *abi.ConvertType(out[1], new(uint64)).(*uint64) + + return *outstruct, err + +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) SProvingKeys(arg0 [32]byte) (SProvingKeys, + + error) { + return _VRFCoordinatorTestV25.Contract.SProvingKeys(&_VRFCoordinatorTestV25.CallOpts, arg0) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25CallerSession) SProvingKeys(arg0 [32]byte) (SProvingKeys, + + error) { + return _VRFCoordinatorTestV25.Contract.SProvingKeys(&_VRFCoordinatorTestV25.CallOpts, arg0) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Caller) SRequestCommitments(opts *bind.CallOpts, arg0 *big.Int) ([32]byte, error) { + var out []interface{} + err := _VRFCoordinatorTestV25.contract.Call(opts, &out, "s_requestCommitments", arg0) + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) SRequestCommitments(arg0 *big.Int) ([32]byte, error) { + return _VRFCoordinatorTestV25.Contract.SRequestCommitments(&_VRFCoordinatorTestV25.CallOpts, arg0) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25CallerSession) SRequestCommitments(arg0 *big.Int) ([32]byte, error) { + return _VRFCoordinatorTestV25.Contract.SRequestCommitments(&_VRFCoordinatorTestV25.CallOpts, arg0) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Caller) STotalBalance(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _VRFCoordinatorTestV25.contract.Call(opts, &out, "s_totalBalance") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) STotalBalance() (*big.Int, error) { + return _VRFCoordinatorTestV25.Contract.STotalBalance(&_VRFCoordinatorTestV25.CallOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25CallerSession) STotalBalance() (*big.Int, error) { + return _VRFCoordinatorTestV25.Contract.STotalBalance(&_VRFCoordinatorTestV25.CallOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Caller) STotalNativeBalance(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _VRFCoordinatorTestV25.contract.Call(opts, &out, "s_totalNativeBalance") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) STotalNativeBalance() (*big.Int, error) { + return _VRFCoordinatorTestV25.Contract.STotalNativeBalance(&_VRFCoordinatorTestV25.CallOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25CallerSession) STotalNativeBalance() (*big.Int, error) { + return _VRFCoordinatorTestV25.Contract.STotalNativeBalance(&_VRFCoordinatorTestV25.CallOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Transactor) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.contract.Transact(opts, "acceptOwnership") +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) AcceptOwnership() (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.AcceptOwnership(&_VRFCoordinatorTestV25.TransactOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25TransactorSession) AcceptOwnership() (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.AcceptOwnership(&_VRFCoordinatorTestV25.TransactOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Transactor) AcceptSubscriptionOwnerTransfer(opts *bind.TransactOpts, subId *big.Int) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.contract.Transact(opts, "acceptSubscriptionOwnerTransfer", subId) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) AcceptSubscriptionOwnerTransfer(subId *big.Int) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.AcceptSubscriptionOwnerTransfer(&_VRFCoordinatorTestV25.TransactOpts, subId) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25TransactorSession) AcceptSubscriptionOwnerTransfer(subId *big.Int) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.AcceptSubscriptionOwnerTransfer(&_VRFCoordinatorTestV25.TransactOpts, subId) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Transactor) AddConsumer(opts *bind.TransactOpts, subId *big.Int, consumer common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.contract.Transact(opts, "addConsumer", subId, consumer) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) AddConsumer(subId *big.Int, consumer common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.AddConsumer(&_VRFCoordinatorTestV25.TransactOpts, subId, consumer) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25TransactorSession) AddConsumer(subId *big.Int, consumer common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.AddConsumer(&_VRFCoordinatorTestV25.TransactOpts, subId, consumer) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Transactor) CancelSubscription(opts *bind.TransactOpts, subId *big.Int, to common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.contract.Transact(opts, "cancelSubscription", subId, to) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) CancelSubscription(subId *big.Int, to common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.CancelSubscription(&_VRFCoordinatorTestV25.TransactOpts, subId, to) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25TransactorSession) CancelSubscription(subId *big.Int, to common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.CancelSubscription(&_VRFCoordinatorTestV25.TransactOpts, subId, to) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Transactor) CreateSubscription(opts *bind.TransactOpts) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.contract.Transact(opts, "createSubscription") +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) CreateSubscription() (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.CreateSubscription(&_VRFCoordinatorTestV25.TransactOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25TransactorSession) CreateSubscription() (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.CreateSubscription(&_VRFCoordinatorTestV25.TransactOpts) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Transactor) DeregisterMigratableCoordinator(opts *bind.TransactOpts, target common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.contract.Transact(opts, "deregisterMigratableCoordinator", target) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) DeregisterMigratableCoordinator(target common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.DeregisterMigratableCoordinator(&_VRFCoordinatorTestV25.TransactOpts, target) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25TransactorSession) DeregisterMigratableCoordinator(target common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.DeregisterMigratableCoordinator(&_VRFCoordinatorTestV25.TransactOpts, target) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Transactor) DeregisterProvingKey(opts *bind.TransactOpts, publicProvingKey [2]*big.Int) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.contract.Transact(opts, "deregisterProvingKey", publicProvingKey) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) DeregisterProvingKey(publicProvingKey [2]*big.Int) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.DeregisterProvingKey(&_VRFCoordinatorTestV25.TransactOpts, publicProvingKey) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25TransactorSession) DeregisterProvingKey(publicProvingKey [2]*big.Int) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.DeregisterProvingKey(&_VRFCoordinatorTestV25.TransactOpts, publicProvingKey) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Transactor) FulfillRandomWords(opts *bind.TransactOpts, proof VRFOldProof, rc VRFTypesRequestCommitmentV2Plus, onlyPremium bool) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.contract.Transact(opts, "fulfillRandomWords", proof, rc, onlyPremium) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) FulfillRandomWords(proof VRFOldProof, rc VRFTypesRequestCommitmentV2Plus, onlyPremium bool) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.FulfillRandomWords(&_VRFCoordinatorTestV25.TransactOpts, proof, rc, onlyPremium) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25TransactorSession) FulfillRandomWords(proof VRFOldProof, rc VRFTypesRequestCommitmentV2Plus, onlyPremium bool) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.FulfillRandomWords(&_VRFCoordinatorTestV25.TransactOpts, proof, rc, onlyPremium) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Transactor) FundSubscriptionWithNative(opts *bind.TransactOpts, subId *big.Int) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.contract.Transact(opts, "fundSubscriptionWithNative", subId) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) FundSubscriptionWithNative(subId *big.Int) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.FundSubscriptionWithNative(&_VRFCoordinatorTestV25.TransactOpts, subId) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25TransactorSession) FundSubscriptionWithNative(subId *big.Int) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.FundSubscriptionWithNative(&_VRFCoordinatorTestV25.TransactOpts, subId) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Transactor) Migrate(opts *bind.TransactOpts, subId *big.Int, newCoordinator common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.contract.Transact(opts, "migrate", subId, newCoordinator) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) Migrate(subId *big.Int, newCoordinator common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.Migrate(&_VRFCoordinatorTestV25.TransactOpts, subId, newCoordinator) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25TransactorSession) Migrate(subId *big.Int, newCoordinator common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.Migrate(&_VRFCoordinatorTestV25.TransactOpts, subId, newCoordinator) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Transactor) OnTokenTransfer(opts *bind.TransactOpts, arg0 common.Address, amount *big.Int, data []byte) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.contract.Transact(opts, "onTokenTransfer", arg0, amount, data) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) OnTokenTransfer(arg0 common.Address, amount *big.Int, data []byte) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.OnTokenTransfer(&_VRFCoordinatorTestV25.TransactOpts, arg0, amount, data) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25TransactorSession) OnTokenTransfer(arg0 common.Address, amount *big.Int, data []byte) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.OnTokenTransfer(&_VRFCoordinatorTestV25.TransactOpts, arg0, amount, data) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Transactor) OwnerCancelSubscription(opts *bind.TransactOpts, subId *big.Int) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.contract.Transact(opts, "ownerCancelSubscription", subId) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) OwnerCancelSubscription(subId *big.Int) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.OwnerCancelSubscription(&_VRFCoordinatorTestV25.TransactOpts, subId) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25TransactorSession) OwnerCancelSubscription(subId *big.Int) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.OwnerCancelSubscription(&_VRFCoordinatorTestV25.TransactOpts, subId) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Transactor) RecoverFunds(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.contract.Transact(opts, "recoverFunds", to) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) RecoverFunds(to common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.RecoverFunds(&_VRFCoordinatorTestV25.TransactOpts, to) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25TransactorSession) RecoverFunds(to common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.RecoverFunds(&_VRFCoordinatorTestV25.TransactOpts, to) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Transactor) RecoverNativeFunds(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.contract.Transact(opts, "recoverNativeFunds", to) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) RecoverNativeFunds(to common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.RecoverNativeFunds(&_VRFCoordinatorTestV25.TransactOpts, to) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25TransactorSession) RecoverNativeFunds(to common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.RecoverNativeFunds(&_VRFCoordinatorTestV25.TransactOpts, to) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Transactor) RegisterMigratableCoordinator(opts *bind.TransactOpts, target common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.contract.Transact(opts, "registerMigratableCoordinator", target) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) RegisterMigratableCoordinator(target common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.RegisterMigratableCoordinator(&_VRFCoordinatorTestV25.TransactOpts, target) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25TransactorSession) RegisterMigratableCoordinator(target common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.RegisterMigratableCoordinator(&_VRFCoordinatorTestV25.TransactOpts, target) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Transactor) RegisterProvingKey(opts *bind.TransactOpts, publicProvingKey [2]*big.Int, maxGas uint64) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.contract.Transact(opts, "registerProvingKey", publicProvingKey, maxGas) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) RegisterProvingKey(publicProvingKey [2]*big.Int, maxGas uint64) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.RegisterProvingKey(&_VRFCoordinatorTestV25.TransactOpts, publicProvingKey, maxGas) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25TransactorSession) RegisterProvingKey(publicProvingKey [2]*big.Int, maxGas uint64) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.RegisterProvingKey(&_VRFCoordinatorTestV25.TransactOpts, publicProvingKey, maxGas) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Transactor) RemoveConsumer(opts *bind.TransactOpts, subId *big.Int, consumer common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.contract.Transact(opts, "removeConsumer", subId, consumer) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) RemoveConsumer(subId *big.Int, consumer common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.RemoveConsumer(&_VRFCoordinatorTestV25.TransactOpts, subId, consumer) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25TransactorSession) RemoveConsumer(subId *big.Int, consumer common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.RemoveConsumer(&_VRFCoordinatorTestV25.TransactOpts, subId, consumer) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Transactor) RequestRandomWords(opts *bind.TransactOpts, req VRFV2PlusClientRandomWordsRequest) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.contract.Transact(opts, "requestRandomWords", req) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) RequestRandomWords(req VRFV2PlusClientRandomWordsRequest) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.RequestRandomWords(&_VRFCoordinatorTestV25.TransactOpts, req) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25TransactorSession) RequestRandomWords(req VRFV2PlusClientRandomWordsRequest) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.RequestRandomWords(&_VRFCoordinatorTestV25.TransactOpts, req) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Transactor) RequestSubscriptionOwnerTransfer(opts *bind.TransactOpts, subId *big.Int, newOwner common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.contract.Transact(opts, "requestSubscriptionOwnerTransfer", subId, newOwner) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) RequestSubscriptionOwnerTransfer(subId *big.Int, newOwner common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.RequestSubscriptionOwnerTransfer(&_VRFCoordinatorTestV25.TransactOpts, subId, newOwner) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25TransactorSession) RequestSubscriptionOwnerTransfer(subId *big.Int, newOwner common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.RequestSubscriptionOwnerTransfer(&_VRFCoordinatorTestV25.TransactOpts, subId, newOwner) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Transactor) SetConfig(opts *bind.TransactOpts, minimumRequestConfirmations uint16, maxGasLimit uint32, stalenessSeconds uint32, gasAfterPaymentCalculation uint32, fallbackWeiPerUnitLink *big.Int, fulfillmentFlatFeeNativePPM uint32, fulfillmentFlatFeeLinkDiscountPPM uint32, nativePremiumPercentage uint8, linkPremiumPercentage uint8) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.contract.Transact(opts, "setConfig", minimumRequestConfirmations, maxGasLimit, stalenessSeconds, gasAfterPaymentCalculation, fallbackWeiPerUnitLink, fulfillmentFlatFeeNativePPM, fulfillmentFlatFeeLinkDiscountPPM, nativePremiumPercentage, linkPremiumPercentage) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) SetConfig(minimumRequestConfirmations uint16, maxGasLimit uint32, stalenessSeconds uint32, gasAfterPaymentCalculation uint32, fallbackWeiPerUnitLink *big.Int, fulfillmentFlatFeeNativePPM uint32, fulfillmentFlatFeeLinkDiscountPPM uint32, nativePremiumPercentage uint8, linkPremiumPercentage uint8) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.SetConfig(&_VRFCoordinatorTestV25.TransactOpts, minimumRequestConfirmations, maxGasLimit, stalenessSeconds, gasAfterPaymentCalculation, fallbackWeiPerUnitLink, fulfillmentFlatFeeNativePPM, fulfillmentFlatFeeLinkDiscountPPM, nativePremiumPercentage, linkPremiumPercentage) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25TransactorSession) SetConfig(minimumRequestConfirmations uint16, maxGasLimit uint32, stalenessSeconds uint32, gasAfterPaymentCalculation uint32, fallbackWeiPerUnitLink *big.Int, fulfillmentFlatFeeNativePPM uint32, fulfillmentFlatFeeLinkDiscountPPM uint32, nativePremiumPercentage uint8, linkPremiumPercentage uint8) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.SetConfig(&_VRFCoordinatorTestV25.TransactOpts, minimumRequestConfirmations, maxGasLimit, stalenessSeconds, gasAfterPaymentCalculation, fallbackWeiPerUnitLink, fulfillmentFlatFeeNativePPM, fulfillmentFlatFeeLinkDiscountPPM, nativePremiumPercentage, linkPremiumPercentage) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Transactor) SetLINKAndLINKNativeFeed(opts *bind.TransactOpts, link common.Address, linkNativeFeed common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.contract.Transact(opts, "setLINKAndLINKNativeFeed", link, linkNativeFeed) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) SetLINKAndLINKNativeFeed(link common.Address, linkNativeFeed common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.SetLINKAndLINKNativeFeed(&_VRFCoordinatorTestV25.TransactOpts, link, linkNativeFeed) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25TransactorSession) SetLINKAndLINKNativeFeed(link common.Address, linkNativeFeed common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.SetLINKAndLINKNativeFeed(&_VRFCoordinatorTestV25.TransactOpts, link, linkNativeFeed) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Transactor) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.contract.Transact(opts, "transferOwnership", to) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.TransferOwnership(&_VRFCoordinatorTestV25.TransactOpts, to) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25TransactorSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.TransferOwnership(&_VRFCoordinatorTestV25.TransactOpts, to) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Transactor) Withdraw(opts *bind.TransactOpts, recipient common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.contract.Transact(opts, "withdraw", recipient) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) Withdraw(recipient common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.Withdraw(&_VRFCoordinatorTestV25.TransactOpts, recipient) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25TransactorSession) Withdraw(recipient common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.Withdraw(&_VRFCoordinatorTestV25.TransactOpts, recipient) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Transactor) WithdrawNative(opts *bind.TransactOpts, recipient common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.contract.Transact(opts, "withdrawNative", recipient) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Session) WithdrawNative(recipient common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.WithdrawNative(&_VRFCoordinatorTestV25.TransactOpts, recipient) +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25TransactorSession) WithdrawNative(recipient common.Address) (*types.Transaction, error) { + return _VRFCoordinatorTestV25.Contract.WithdrawNative(&_VRFCoordinatorTestV25.TransactOpts, recipient) +} + +type VRFCoordinatorTestV25ConfigSetIterator struct { + Event *VRFCoordinatorTestV25ConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV25ConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25ConfigSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25ConfigSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *VRFCoordinatorTestV25ConfigSetIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV25ConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV25ConfigSet struct { + MinimumRequestConfirmations uint16 + MaxGasLimit uint32 + StalenessSeconds uint32 + GasAfterPaymentCalculation uint32 + FallbackWeiPerUnitLink *big.Int + FulfillmentFlatFeeNativePPM uint32 + FulfillmentFlatFeeLinkDiscountPPM uint32 + NativePremiumPercentage uint8 + LinkPremiumPercentage uint8 + Raw types.Log +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) FilterConfigSet(opts *bind.FilterOpts) (*VRFCoordinatorTestV25ConfigSetIterator, error) { + + logs, sub, err := _VRFCoordinatorTestV25.contract.FilterLogs(opts, "ConfigSet") + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV25ConfigSetIterator{contract: _VRFCoordinatorTestV25.contract, event: "ConfigSet", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) WatchConfigSet(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25ConfigSet) (event.Subscription, error) { + + logs, sub, err := _VRFCoordinatorTestV25.contract.WatchLogs(opts, "ConfigSet") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV25ConfigSet) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "ConfigSet", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) ParseConfigSet(log types.Log) (*VRFCoordinatorTestV25ConfigSet, error) { + event := new(VRFCoordinatorTestV25ConfigSet) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "ConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV25CoordinatorDeregisteredIterator struct { + Event *VRFCoordinatorTestV25CoordinatorDeregistered + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV25CoordinatorDeregisteredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25CoordinatorDeregistered) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25CoordinatorDeregistered) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *VRFCoordinatorTestV25CoordinatorDeregisteredIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV25CoordinatorDeregisteredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV25CoordinatorDeregistered struct { + CoordinatorAddress common.Address + Raw types.Log +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) FilterCoordinatorDeregistered(opts *bind.FilterOpts) (*VRFCoordinatorTestV25CoordinatorDeregisteredIterator, error) { + + logs, sub, err := _VRFCoordinatorTestV25.contract.FilterLogs(opts, "CoordinatorDeregistered") + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV25CoordinatorDeregisteredIterator{contract: _VRFCoordinatorTestV25.contract, event: "CoordinatorDeregistered", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) WatchCoordinatorDeregistered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25CoordinatorDeregistered) (event.Subscription, error) { + + logs, sub, err := _VRFCoordinatorTestV25.contract.WatchLogs(opts, "CoordinatorDeregistered") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV25CoordinatorDeregistered) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "CoordinatorDeregistered", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) ParseCoordinatorDeregistered(log types.Log) (*VRFCoordinatorTestV25CoordinatorDeregistered, error) { + event := new(VRFCoordinatorTestV25CoordinatorDeregistered) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "CoordinatorDeregistered", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV25CoordinatorRegisteredIterator struct { + Event *VRFCoordinatorTestV25CoordinatorRegistered + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV25CoordinatorRegisteredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25CoordinatorRegistered) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25CoordinatorRegistered) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *VRFCoordinatorTestV25CoordinatorRegisteredIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV25CoordinatorRegisteredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV25CoordinatorRegistered struct { + CoordinatorAddress common.Address + Raw types.Log +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) FilterCoordinatorRegistered(opts *bind.FilterOpts) (*VRFCoordinatorTestV25CoordinatorRegisteredIterator, error) { + + logs, sub, err := _VRFCoordinatorTestV25.contract.FilterLogs(opts, "CoordinatorRegistered") + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV25CoordinatorRegisteredIterator{contract: _VRFCoordinatorTestV25.contract, event: "CoordinatorRegistered", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) WatchCoordinatorRegistered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25CoordinatorRegistered) (event.Subscription, error) { + + logs, sub, err := _VRFCoordinatorTestV25.contract.WatchLogs(opts, "CoordinatorRegistered") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV25CoordinatorRegistered) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "CoordinatorRegistered", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) ParseCoordinatorRegistered(log types.Log) (*VRFCoordinatorTestV25CoordinatorRegistered, error) { + event := new(VRFCoordinatorTestV25CoordinatorRegistered) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "CoordinatorRegistered", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV25FallbackWeiPerUnitLinkUsedIterator struct { + Event *VRFCoordinatorTestV25FallbackWeiPerUnitLinkUsed + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV25FallbackWeiPerUnitLinkUsedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25FallbackWeiPerUnitLinkUsed) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25FallbackWeiPerUnitLinkUsed) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *VRFCoordinatorTestV25FallbackWeiPerUnitLinkUsedIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV25FallbackWeiPerUnitLinkUsedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV25FallbackWeiPerUnitLinkUsed struct { + RequestId *big.Int + FallbackWeiPerUnitLink *big.Int + Raw types.Log +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) FilterFallbackWeiPerUnitLinkUsed(opts *bind.FilterOpts) (*VRFCoordinatorTestV25FallbackWeiPerUnitLinkUsedIterator, error) { + + logs, sub, err := _VRFCoordinatorTestV25.contract.FilterLogs(opts, "FallbackWeiPerUnitLinkUsed") + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV25FallbackWeiPerUnitLinkUsedIterator{contract: _VRFCoordinatorTestV25.contract, event: "FallbackWeiPerUnitLinkUsed", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) WatchFallbackWeiPerUnitLinkUsed(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25FallbackWeiPerUnitLinkUsed) (event.Subscription, error) { + + logs, sub, err := _VRFCoordinatorTestV25.contract.WatchLogs(opts, "FallbackWeiPerUnitLinkUsed") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV25FallbackWeiPerUnitLinkUsed) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "FallbackWeiPerUnitLinkUsed", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) ParseFallbackWeiPerUnitLinkUsed(log types.Log) (*VRFCoordinatorTestV25FallbackWeiPerUnitLinkUsed, error) { + event := new(VRFCoordinatorTestV25FallbackWeiPerUnitLinkUsed) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "FallbackWeiPerUnitLinkUsed", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV25FundsRecoveredIterator struct { + Event *VRFCoordinatorTestV25FundsRecovered + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV25FundsRecoveredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25FundsRecovered) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25FundsRecovered) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *VRFCoordinatorTestV25FundsRecoveredIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV25FundsRecoveredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV25FundsRecovered struct { + To common.Address + Amount *big.Int + Raw types.Log +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) FilterFundsRecovered(opts *bind.FilterOpts) (*VRFCoordinatorTestV25FundsRecoveredIterator, error) { + + logs, sub, err := _VRFCoordinatorTestV25.contract.FilterLogs(opts, "FundsRecovered") + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV25FundsRecoveredIterator{contract: _VRFCoordinatorTestV25.contract, event: "FundsRecovered", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) WatchFundsRecovered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25FundsRecovered) (event.Subscription, error) { + + logs, sub, err := _VRFCoordinatorTestV25.contract.WatchLogs(opts, "FundsRecovered") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV25FundsRecovered) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "FundsRecovered", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) ParseFundsRecovered(log types.Log) (*VRFCoordinatorTestV25FundsRecovered, error) { + event := new(VRFCoordinatorTestV25FundsRecovered) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "FundsRecovered", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV25MigrationCompletedIterator struct { + Event *VRFCoordinatorTestV25MigrationCompleted + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV25MigrationCompletedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25MigrationCompleted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25MigrationCompleted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *VRFCoordinatorTestV25MigrationCompletedIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV25MigrationCompletedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV25MigrationCompleted struct { + NewCoordinator common.Address + SubId *big.Int + Raw types.Log +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) FilterMigrationCompleted(opts *bind.FilterOpts) (*VRFCoordinatorTestV25MigrationCompletedIterator, error) { + + logs, sub, err := _VRFCoordinatorTestV25.contract.FilterLogs(opts, "MigrationCompleted") + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV25MigrationCompletedIterator{contract: _VRFCoordinatorTestV25.contract, event: "MigrationCompleted", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) WatchMigrationCompleted(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25MigrationCompleted) (event.Subscription, error) { + + logs, sub, err := _VRFCoordinatorTestV25.contract.WatchLogs(opts, "MigrationCompleted") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV25MigrationCompleted) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "MigrationCompleted", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) ParseMigrationCompleted(log types.Log) (*VRFCoordinatorTestV25MigrationCompleted, error) { + event := new(VRFCoordinatorTestV25MigrationCompleted) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "MigrationCompleted", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV25NativeFundsRecoveredIterator struct { + Event *VRFCoordinatorTestV25NativeFundsRecovered + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV25NativeFundsRecoveredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25NativeFundsRecovered) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25NativeFundsRecovered) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *VRFCoordinatorTestV25NativeFundsRecoveredIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV25NativeFundsRecoveredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV25NativeFundsRecovered struct { + To common.Address + Amount *big.Int + Raw types.Log +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) FilterNativeFundsRecovered(opts *bind.FilterOpts) (*VRFCoordinatorTestV25NativeFundsRecoveredIterator, error) { + + logs, sub, err := _VRFCoordinatorTestV25.contract.FilterLogs(opts, "NativeFundsRecovered") + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV25NativeFundsRecoveredIterator{contract: _VRFCoordinatorTestV25.contract, event: "NativeFundsRecovered", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) WatchNativeFundsRecovered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25NativeFundsRecovered) (event.Subscription, error) { + + logs, sub, err := _VRFCoordinatorTestV25.contract.WatchLogs(opts, "NativeFundsRecovered") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV25NativeFundsRecovered) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "NativeFundsRecovered", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) ParseNativeFundsRecovered(log types.Log) (*VRFCoordinatorTestV25NativeFundsRecovered, error) { + event := new(VRFCoordinatorTestV25NativeFundsRecovered) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "NativeFundsRecovered", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV25OwnershipTransferRequestedIterator struct { + Event *VRFCoordinatorTestV25OwnershipTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV25OwnershipTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25OwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25OwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *VRFCoordinatorTestV25OwnershipTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV25OwnershipTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV25OwnershipTransferRequested struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*VRFCoordinatorTestV25OwnershipTransferRequestedIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _VRFCoordinatorTestV25.contract.FilterLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV25OwnershipTransferRequestedIterator{contract: _VRFCoordinatorTestV25.contract, event: "OwnershipTransferRequested", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25OwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _VRFCoordinatorTestV25.contract.WatchLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV25OwnershipTransferRequested) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) ParseOwnershipTransferRequested(log types.Log) (*VRFCoordinatorTestV25OwnershipTransferRequested, error) { + event := new(VRFCoordinatorTestV25OwnershipTransferRequested) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV25OwnershipTransferredIterator struct { + Event *VRFCoordinatorTestV25OwnershipTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV25OwnershipTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25OwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25OwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *VRFCoordinatorTestV25OwnershipTransferredIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV25OwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV25OwnershipTransferred struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*VRFCoordinatorTestV25OwnershipTransferredIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _VRFCoordinatorTestV25.contract.FilterLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV25OwnershipTransferredIterator{contract: _VRFCoordinatorTestV25.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25OwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _VRFCoordinatorTestV25.contract.WatchLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV25OwnershipTransferred) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) ParseOwnershipTransferred(log types.Log) (*VRFCoordinatorTestV25OwnershipTransferred, error) { + event := new(VRFCoordinatorTestV25OwnershipTransferred) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV25ProvingKeyDeregisteredIterator struct { + Event *VRFCoordinatorTestV25ProvingKeyDeregistered + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV25ProvingKeyDeregisteredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25ProvingKeyDeregistered) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25ProvingKeyDeregistered) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *VRFCoordinatorTestV25ProvingKeyDeregisteredIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV25ProvingKeyDeregisteredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV25ProvingKeyDeregistered struct { + KeyHash [32]byte + MaxGas uint64 + Raw types.Log +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) FilterProvingKeyDeregistered(opts *bind.FilterOpts) (*VRFCoordinatorTestV25ProvingKeyDeregisteredIterator, error) { + + logs, sub, err := _VRFCoordinatorTestV25.contract.FilterLogs(opts, "ProvingKeyDeregistered") + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV25ProvingKeyDeregisteredIterator{contract: _VRFCoordinatorTestV25.contract, event: "ProvingKeyDeregistered", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) WatchProvingKeyDeregistered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25ProvingKeyDeregistered) (event.Subscription, error) { + + logs, sub, err := _VRFCoordinatorTestV25.contract.WatchLogs(opts, "ProvingKeyDeregistered") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV25ProvingKeyDeregistered) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "ProvingKeyDeregistered", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) ParseProvingKeyDeregistered(log types.Log) (*VRFCoordinatorTestV25ProvingKeyDeregistered, error) { + event := new(VRFCoordinatorTestV25ProvingKeyDeregistered) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "ProvingKeyDeregistered", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV25ProvingKeyRegisteredIterator struct { + Event *VRFCoordinatorTestV25ProvingKeyRegistered + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV25ProvingKeyRegisteredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25ProvingKeyRegistered) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25ProvingKeyRegistered) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *VRFCoordinatorTestV25ProvingKeyRegisteredIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV25ProvingKeyRegisteredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV25ProvingKeyRegistered struct { + KeyHash [32]byte + MaxGas uint64 + Raw types.Log +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) FilterProvingKeyRegistered(opts *bind.FilterOpts) (*VRFCoordinatorTestV25ProvingKeyRegisteredIterator, error) { + + logs, sub, err := _VRFCoordinatorTestV25.contract.FilterLogs(opts, "ProvingKeyRegistered") + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV25ProvingKeyRegisteredIterator{contract: _VRFCoordinatorTestV25.contract, event: "ProvingKeyRegistered", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) WatchProvingKeyRegistered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25ProvingKeyRegistered) (event.Subscription, error) { + + logs, sub, err := _VRFCoordinatorTestV25.contract.WatchLogs(opts, "ProvingKeyRegistered") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV25ProvingKeyRegistered) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "ProvingKeyRegistered", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) ParseProvingKeyRegistered(log types.Log) (*VRFCoordinatorTestV25ProvingKeyRegistered, error) { + event := new(VRFCoordinatorTestV25ProvingKeyRegistered) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "ProvingKeyRegistered", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV25RandomWordsFulfilledIterator struct { + Event *VRFCoordinatorTestV25RandomWordsFulfilled + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV25RandomWordsFulfilledIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25RandomWordsFulfilled) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25RandomWordsFulfilled) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *VRFCoordinatorTestV25RandomWordsFulfilledIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV25RandomWordsFulfilledIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV25RandomWordsFulfilled struct { + RequestId *big.Int + OutputSeed *big.Int + SubId *big.Int + Payment *big.Int + NativePayment bool + Success bool + OnlyPremium bool + Raw types.Log +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) FilterRandomWordsFulfilled(opts *bind.FilterOpts, requestId []*big.Int, subId []*big.Int) (*VRFCoordinatorTestV25RandomWordsFulfilledIterator, error) { + + var requestIdRule []interface{} + for _, requestIdItem := range requestId { + requestIdRule = append(requestIdRule, requestIdItem) + } + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV25.contract.FilterLogs(opts, "RandomWordsFulfilled", requestIdRule, subIdRule) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV25RandomWordsFulfilledIterator{contract: _VRFCoordinatorTestV25.contract, event: "RandomWordsFulfilled", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) WatchRandomWordsFulfilled(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25RandomWordsFulfilled, requestId []*big.Int, subId []*big.Int) (event.Subscription, error) { + + var requestIdRule []interface{} + for _, requestIdItem := range requestId { + requestIdRule = append(requestIdRule, requestIdItem) + } + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV25.contract.WatchLogs(opts, "RandomWordsFulfilled", requestIdRule, subIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV25RandomWordsFulfilled) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "RandomWordsFulfilled", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) ParseRandomWordsFulfilled(log types.Log) (*VRFCoordinatorTestV25RandomWordsFulfilled, error) { + event := new(VRFCoordinatorTestV25RandomWordsFulfilled) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "RandomWordsFulfilled", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV25RandomWordsRequestedIterator struct { + Event *VRFCoordinatorTestV25RandomWordsRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV25RandomWordsRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25RandomWordsRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25RandomWordsRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *VRFCoordinatorTestV25RandomWordsRequestedIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV25RandomWordsRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV25RandomWordsRequested struct { + KeyHash [32]byte + RequestId *big.Int + PreSeed *big.Int + SubId *big.Int + MinimumRequestConfirmations uint16 + CallbackGasLimit uint32 + NumWords uint32 + ExtraArgs []byte + Sender common.Address + Raw types.Log +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) FilterRandomWordsRequested(opts *bind.FilterOpts, keyHash [][32]byte, subId []*big.Int, sender []common.Address) (*VRFCoordinatorTestV25RandomWordsRequestedIterator, error) { + + var keyHashRule []interface{} + for _, keyHashItem := range keyHash { + keyHashRule = append(keyHashRule, keyHashItem) + } + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _VRFCoordinatorTestV25.contract.FilterLogs(opts, "RandomWordsRequested", keyHashRule, subIdRule, senderRule) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV25RandomWordsRequestedIterator{contract: _VRFCoordinatorTestV25.contract, event: "RandomWordsRequested", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) WatchRandomWordsRequested(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25RandomWordsRequested, keyHash [][32]byte, subId []*big.Int, sender []common.Address) (event.Subscription, error) { + + var keyHashRule []interface{} + for _, keyHashItem := range keyHash { + keyHashRule = append(keyHashRule, keyHashItem) + } + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _VRFCoordinatorTestV25.contract.WatchLogs(opts, "RandomWordsRequested", keyHashRule, subIdRule, senderRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV25RandomWordsRequested) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "RandomWordsRequested", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) ParseRandomWordsRequested(log types.Log) (*VRFCoordinatorTestV25RandomWordsRequested, error) { + event := new(VRFCoordinatorTestV25RandomWordsRequested) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "RandomWordsRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV25SubscriptionCanceledIterator struct { + Event *VRFCoordinatorTestV25SubscriptionCanceled + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV25SubscriptionCanceledIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25SubscriptionCanceled) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25SubscriptionCanceled) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *VRFCoordinatorTestV25SubscriptionCanceledIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV25SubscriptionCanceledIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV25SubscriptionCanceled struct { + SubId *big.Int + To common.Address + AmountLink *big.Int + AmountNative *big.Int + Raw types.Log +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) FilterSubscriptionCanceled(opts *bind.FilterOpts, subId []*big.Int) (*VRFCoordinatorTestV25SubscriptionCanceledIterator, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV25.contract.FilterLogs(opts, "SubscriptionCanceled", subIdRule) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV25SubscriptionCanceledIterator{contract: _VRFCoordinatorTestV25.contract, event: "SubscriptionCanceled", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) WatchSubscriptionCanceled(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25SubscriptionCanceled, subId []*big.Int) (event.Subscription, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV25.contract.WatchLogs(opts, "SubscriptionCanceled", subIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV25SubscriptionCanceled) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "SubscriptionCanceled", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) ParseSubscriptionCanceled(log types.Log) (*VRFCoordinatorTestV25SubscriptionCanceled, error) { + event := new(VRFCoordinatorTestV25SubscriptionCanceled) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "SubscriptionCanceled", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV25SubscriptionConsumerAddedIterator struct { + Event *VRFCoordinatorTestV25SubscriptionConsumerAdded + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV25SubscriptionConsumerAddedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25SubscriptionConsumerAdded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25SubscriptionConsumerAdded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *VRFCoordinatorTestV25SubscriptionConsumerAddedIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV25SubscriptionConsumerAddedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV25SubscriptionConsumerAdded struct { + SubId *big.Int + Consumer common.Address + Raw types.Log +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) FilterSubscriptionConsumerAdded(opts *bind.FilterOpts, subId []*big.Int) (*VRFCoordinatorTestV25SubscriptionConsumerAddedIterator, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV25.contract.FilterLogs(opts, "SubscriptionConsumerAdded", subIdRule) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV25SubscriptionConsumerAddedIterator{contract: _VRFCoordinatorTestV25.contract, event: "SubscriptionConsumerAdded", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) WatchSubscriptionConsumerAdded(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25SubscriptionConsumerAdded, subId []*big.Int) (event.Subscription, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV25.contract.WatchLogs(opts, "SubscriptionConsumerAdded", subIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV25SubscriptionConsumerAdded) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "SubscriptionConsumerAdded", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) ParseSubscriptionConsumerAdded(log types.Log) (*VRFCoordinatorTestV25SubscriptionConsumerAdded, error) { + event := new(VRFCoordinatorTestV25SubscriptionConsumerAdded) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "SubscriptionConsumerAdded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV25SubscriptionConsumerRemovedIterator struct { + Event *VRFCoordinatorTestV25SubscriptionConsumerRemoved + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV25SubscriptionConsumerRemovedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25SubscriptionConsumerRemoved) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25SubscriptionConsumerRemoved) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *VRFCoordinatorTestV25SubscriptionConsumerRemovedIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV25SubscriptionConsumerRemovedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV25SubscriptionConsumerRemoved struct { + SubId *big.Int + Consumer common.Address + Raw types.Log +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) FilterSubscriptionConsumerRemoved(opts *bind.FilterOpts, subId []*big.Int) (*VRFCoordinatorTestV25SubscriptionConsumerRemovedIterator, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV25.contract.FilterLogs(opts, "SubscriptionConsumerRemoved", subIdRule) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV25SubscriptionConsumerRemovedIterator{contract: _VRFCoordinatorTestV25.contract, event: "SubscriptionConsumerRemoved", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) WatchSubscriptionConsumerRemoved(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25SubscriptionConsumerRemoved, subId []*big.Int) (event.Subscription, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV25.contract.WatchLogs(opts, "SubscriptionConsumerRemoved", subIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV25SubscriptionConsumerRemoved) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "SubscriptionConsumerRemoved", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) ParseSubscriptionConsumerRemoved(log types.Log) (*VRFCoordinatorTestV25SubscriptionConsumerRemoved, error) { + event := new(VRFCoordinatorTestV25SubscriptionConsumerRemoved) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "SubscriptionConsumerRemoved", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV25SubscriptionCreatedIterator struct { + Event *VRFCoordinatorTestV25SubscriptionCreated + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV25SubscriptionCreatedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25SubscriptionCreated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25SubscriptionCreated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *VRFCoordinatorTestV25SubscriptionCreatedIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV25SubscriptionCreatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV25SubscriptionCreated struct { + SubId *big.Int + Owner common.Address + Raw types.Log +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) FilterSubscriptionCreated(opts *bind.FilterOpts, subId []*big.Int) (*VRFCoordinatorTestV25SubscriptionCreatedIterator, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV25.contract.FilterLogs(opts, "SubscriptionCreated", subIdRule) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV25SubscriptionCreatedIterator{contract: _VRFCoordinatorTestV25.contract, event: "SubscriptionCreated", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) WatchSubscriptionCreated(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25SubscriptionCreated, subId []*big.Int) (event.Subscription, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV25.contract.WatchLogs(opts, "SubscriptionCreated", subIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV25SubscriptionCreated) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "SubscriptionCreated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) ParseSubscriptionCreated(log types.Log) (*VRFCoordinatorTestV25SubscriptionCreated, error) { + event := new(VRFCoordinatorTestV25SubscriptionCreated) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "SubscriptionCreated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV25SubscriptionFundedIterator struct { + Event *VRFCoordinatorTestV25SubscriptionFunded + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV25SubscriptionFundedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25SubscriptionFunded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25SubscriptionFunded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *VRFCoordinatorTestV25SubscriptionFundedIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV25SubscriptionFundedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV25SubscriptionFunded struct { + SubId *big.Int + OldBalance *big.Int + NewBalance *big.Int + Raw types.Log +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) FilterSubscriptionFunded(opts *bind.FilterOpts, subId []*big.Int) (*VRFCoordinatorTestV25SubscriptionFundedIterator, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV25.contract.FilterLogs(opts, "SubscriptionFunded", subIdRule) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV25SubscriptionFundedIterator{contract: _VRFCoordinatorTestV25.contract, event: "SubscriptionFunded", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) WatchSubscriptionFunded(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25SubscriptionFunded, subId []*big.Int) (event.Subscription, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV25.contract.WatchLogs(opts, "SubscriptionFunded", subIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV25SubscriptionFunded) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "SubscriptionFunded", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) ParseSubscriptionFunded(log types.Log) (*VRFCoordinatorTestV25SubscriptionFunded, error) { + event := new(VRFCoordinatorTestV25SubscriptionFunded) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "SubscriptionFunded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV25SubscriptionFundedWithNativeIterator struct { + Event *VRFCoordinatorTestV25SubscriptionFundedWithNative + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV25SubscriptionFundedWithNativeIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25SubscriptionFundedWithNative) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25SubscriptionFundedWithNative) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *VRFCoordinatorTestV25SubscriptionFundedWithNativeIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV25SubscriptionFundedWithNativeIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV25SubscriptionFundedWithNative struct { + SubId *big.Int + OldNativeBalance *big.Int + NewNativeBalance *big.Int + Raw types.Log +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) FilterSubscriptionFundedWithNative(opts *bind.FilterOpts, subId []*big.Int) (*VRFCoordinatorTestV25SubscriptionFundedWithNativeIterator, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV25.contract.FilterLogs(opts, "SubscriptionFundedWithNative", subIdRule) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV25SubscriptionFundedWithNativeIterator{contract: _VRFCoordinatorTestV25.contract, event: "SubscriptionFundedWithNative", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) WatchSubscriptionFundedWithNative(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25SubscriptionFundedWithNative, subId []*big.Int) (event.Subscription, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV25.contract.WatchLogs(opts, "SubscriptionFundedWithNative", subIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV25SubscriptionFundedWithNative) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "SubscriptionFundedWithNative", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) ParseSubscriptionFundedWithNative(log types.Log) (*VRFCoordinatorTestV25SubscriptionFundedWithNative, error) { + event := new(VRFCoordinatorTestV25SubscriptionFundedWithNative) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "SubscriptionFundedWithNative", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV25SubscriptionOwnerTransferRequestedIterator struct { + Event *VRFCoordinatorTestV25SubscriptionOwnerTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV25SubscriptionOwnerTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25SubscriptionOwnerTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25SubscriptionOwnerTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *VRFCoordinatorTestV25SubscriptionOwnerTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV25SubscriptionOwnerTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV25SubscriptionOwnerTransferRequested struct { + SubId *big.Int + From common.Address + To common.Address + Raw types.Log +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) FilterSubscriptionOwnerTransferRequested(opts *bind.FilterOpts, subId []*big.Int) (*VRFCoordinatorTestV25SubscriptionOwnerTransferRequestedIterator, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV25.contract.FilterLogs(opts, "SubscriptionOwnerTransferRequested", subIdRule) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV25SubscriptionOwnerTransferRequestedIterator{contract: _VRFCoordinatorTestV25.contract, event: "SubscriptionOwnerTransferRequested", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) WatchSubscriptionOwnerTransferRequested(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25SubscriptionOwnerTransferRequested, subId []*big.Int) (event.Subscription, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV25.contract.WatchLogs(opts, "SubscriptionOwnerTransferRequested", subIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV25SubscriptionOwnerTransferRequested) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "SubscriptionOwnerTransferRequested", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) ParseSubscriptionOwnerTransferRequested(log types.Log) (*VRFCoordinatorTestV25SubscriptionOwnerTransferRequested, error) { + event := new(VRFCoordinatorTestV25SubscriptionOwnerTransferRequested) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "SubscriptionOwnerTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type VRFCoordinatorTestV25SubscriptionOwnerTransferredIterator struct { + Event *VRFCoordinatorTestV25SubscriptionOwnerTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *VRFCoordinatorTestV25SubscriptionOwnerTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25SubscriptionOwnerTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(VRFCoordinatorTestV25SubscriptionOwnerTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *VRFCoordinatorTestV25SubscriptionOwnerTransferredIterator) Error() error { + return it.fail +} + +func (it *VRFCoordinatorTestV25SubscriptionOwnerTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type VRFCoordinatorTestV25SubscriptionOwnerTransferred struct { + SubId *big.Int + From common.Address + To common.Address + Raw types.Log +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) FilterSubscriptionOwnerTransferred(opts *bind.FilterOpts, subId []*big.Int) (*VRFCoordinatorTestV25SubscriptionOwnerTransferredIterator, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV25.contract.FilterLogs(opts, "SubscriptionOwnerTransferred", subIdRule) + if err != nil { + return nil, err + } + return &VRFCoordinatorTestV25SubscriptionOwnerTransferredIterator{contract: _VRFCoordinatorTestV25.contract, event: "SubscriptionOwnerTransferred", logs: logs, sub: sub}, nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) WatchSubscriptionOwnerTransferred(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25SubscriptionOwnerTransferred, subId []*big.Int) (event.Subscription, error) { + + var subIdRule []interface{} + for _, subIdItem := range subId { + subIdRule = append(subIdRule, subIdItem) + } + + logs, sub, err := _VRFCoordinatorTestV25.contract.WatchLogs(opts, "SubscriptionOwnerTransferred", subIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(VRFCoordinatorTestV25SubscriptionOwnerTransferred) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "SubscriptionOwnerTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25Filterer) ParseSubscriptionOwnerTransferred(log types.Log) (*VRFCoordinatorTestV25SubscriptionOwnerTransferred, error) { + event := new(VRFCoordinatorTestV25SubscriptionOwnerTransferred) + if err := _VRFCoordinatorTestV25.contract.UnpackLog(event, "SubscriptionOwnerTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type GetSubscription struct { + Balance *big.Int + NativeBalance *big.Int + ReqCount uint64 + SubOwner common.Address + Consumers []common.Address +} +type SConfig struct { + MinimumRequestConfirmations uint16 + MaxGasLimit uint32 + ReentrancyLock bool + StalenessSeconds uint32 + GasAfterPaymentCalculation uint32 + FulfillmentFlatFeeNativePPM uint32 + FulfillmentFlatFeeLinkDiscountPPM uint32 + NativePremiumPercentage uint8 + LinkPremiumPercentage uint8 +} +type SProvingKeys struct { + Exists bool + MaxGas uint64 +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25) ParseLog(log types.Log) (generated.AbigenLog, error) { + switch log.Topics[0] { + case _VRFCoordinatorTestV25.abi.Events["ConfigSet"].ID: + return _VRFCoordinatorTestV25.ParseConfigSet(log) + case _VRFCoordinatorTestV25.abi.Events["CoordinatorDeregistered"].ID: + return _VRFCoordinatorTestV25.ParseCoordinatorDeregistered(log) + case _VRFCoordinatorTestV25.abi.Events["CoordinatorRegistered"].ID: + return _VRFCoordinatorTestV25.ParseCoordinatorRegistered(log) + case _VRFCoordinatorTestV25.abi.Events["FallbackWeiPerUnitLinkUsed"].ID: + return _VRFCoordinatorTestV25.ParseFallbackWeiPerUnitLinkUsed(log) + case _VRFCoordinatorTestV25.abi.Events["FundsRecovered"].ID: + return _VRFCoordinatorTestV25.ParseFundsRecovered(log) + case _VRFCoordinatorTestV25.abi.Events["MigrationCompleted"].ID: + return _VRFCoordinatorTestV25.ParseMigrationCompleted(log) + case _VRFCoordinatorTestV25.abi.Events["NativeFundsRecovered"].ID: + return _VRFCoordinatorTestV25.ParseNativeFundsRecovered(log) + case _VRFCoordinatorTestV25.abi.Events["OwnershipTransferRequested"].ID: + return _VRFCoordinatorTestV25.ParseOwnershipTransferRequested(log) + case _VRFCoordinatorTestV25.abi.Events["OwnershipTransferred"].ID: + return _VRFCoordinatorTestV25.ParseOwnershipTransferred(log) + case _VRFCoordinatorTestV25.abi.Events["ProvingKeyDeregistered"].ID: + return _VRFCoordinatorTestV25.ParseProvingKeyDeregistered(log) + case _VRFCoordinatorTestV25.abi.Events["ProvingKeyRegistered"].ID: + return _VRFCoordinatorTestV25.ParseProvingKeyRegistered(log) + case _VRFCoordinatorTestV25.abi.Events["RandomWordsFulfilled"].ID: + return _VRFCoordinatorTestV25.ParseRandomWordsFulfilled(log) + case _VRFCoordinatorTestV25.abi.Events["RandomWordsRequested"].ID: + return _VRFCoordinatorTestV25.ParseRandomWordsRequested(log) + case _VRFCoordinatorTestV25.abi.Events["SubscriptionCanceled"].ID: + return _VRFCoordinatorTestV25.ParseSubscriptionCanceled(log) + case _VRFCoordinatorTestV25.abi.Events["SubscriptionConsumerAdded"].ID: + return _VRFCoordinatorTestV25.ParseSubscriptionConsumerAdded(log) + case _VRFCoordinatorTestV25.abi.Events["SubscriptionConsumerRemoved"].ID: + return _VRFCoordinatorTestV25.ParseSubscriptionConsumerRemoved(log) + case _VRFCoordinatorTestV25.abi.Events["SubscriptionCreated"].ID: + return _VRFCoordinatorTestV25.ParseSubscriptionCreated(log) + case _VRFCoordinatorTestV25.abi.Events["SubscriptionFunded"].ID: + return _VRFCoordinatorTestV25.ParseSubscriptionFunded(log) + case _VRFCoordinatorTestV25.abi.Events["SubscriptionFundedWithNative"].ID: + return _VRFCoordinatorTestV25.ParseSubscriptionFundedWithNative(log) + case _VRFCoordinatorTestV25.abi.Events["SubscriptionOwnerTransferRequested"].ID: + return _VRFCoordinatorTestV25.ParseSubscriptionOwnerTransferRequested(log) + case _VRFCoordinatorTestV25.abi.Events["SubscriptionOwnerTransferred"].ID: + return _VRFCoordinatorTestV25.ParseSubscriptionOwnerTransferred(log) + + default: + return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) + } +} + +func (VRFCoordinatorTestV25ConfigSet) Topic() common.Hash { + return common.HexToHash("0x2c6b6b12413678366b05b145c5f00745bdd00e739131ab5de82484a50c9d78b6") +} + +func (VRFCoordinatorTestV25CoordinatorDeregistered) Topic() common.Hash { + return common.HexToHash("0xf80a1a97fd42251f3c33cda98635e7399253033a6774fe37cd3f650b5282af37") +} + +func (VRFCoordinatorTestV25CoordinatorRegistered) Topic() common.Hash { + return common.HexToHash("0xb7cabbfc11e66731fc77de0444614282023bcbd41d16781c753a431d0af01625") +} + +func (VRFCoordinatorTestV25FallbackWeiPerUnitLinkUsed) Topic() common.Hash { + return common.HexToHash("0x6ca648a381f22ead7e37773d934e64885dcf861fbfbb26c40354cbf0c4662d1a") +} + +func (VRFCoordinatorTestV25FundsRecovered) Topic() common.Hash { + return common.HexToHash("0x59bfc682b673f8cbf945f1e454df9334834abf7dfe7f92237ca29ecb9b436600") +} + +func (VRFCoordinatorTestV25MigrationCompleted) Topic() common.Hash { + return common.HexToHash("0xd63ca8cb945956747ee69bfdc3ea754c24a4caf7418db70e46052f7850be4187") +} + +func (VRFCoordinatorTestV25NativeFundsRecovered) Topic() common.Hash { + return common.HexToHash("0x4aed7c8eed0496c8c19ea2681fcca25741c1602342e38b045d9f1e8e905d2e9c") +} + +func (VRFCoordinatorTestV25OwnershipTransferRequested) Topic() common.Hash { + return common.HexToHash("0xed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278") +} + +func (VRFCoordinatorTestV25OwnershipTransferred) Topic() common.Hash { + return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") +} + +func (VRFCoordinatorTestV25ProvingKeyDeregistered) Topic() common.Hash { + return common.HexToHash("0x9b6868e0eb737bcd72205360baa6bfd0ba4e4819a33ade2db384e8a8025639a5") +} + +func (VRFCoordinatorTestV25ProvingKeyRegistered) Topic() common.Hash { + return common.HexToHash("0x9b911b2c240bfbef3b6a8f7ed6ee321d1258bb2a3fe6becab52ac1cd3210afd3") +} + +func (VRFCoordinatorTestV25RandomWordsFulfilled) Topic() common.Hash { + return common.HexToHash("0xaeb4b4786571e184246d39587f659abf0e26f41f6a3358692250382c0cdb47b7") +} + +func (VRFCoordinatorTestV25RandomWordsRequested) Topic() common.Hash { + return common.HexToHash("0xeb0e3652e0f44f417695e6e90f2f42c99b65cd7169074c5a654b16b9748c3a4e") +} + +func (VRFCoordinatorTestV25SubscriptionCanceled) Topic() common.Hash { + return common.HexToHash("0x8c74ce8b8cf87f5eb001275c8be27eb34ea2b62bfab6814fcc62192bb63e81c4") +} + +func (VRFCoordinatorTestV25SubscriptionConsumerAdded) Topic() common.Hash { + return common.HexToHash("0x1e980d04aa7648e205713e5e8ea3808672ac163d10936d36f91b2c88ac1575e1") +} + +func (VRFCoordinatorTestV25SubscriptionConsumerRemoved) Topic() common.Hash { + return common.HexToHash("0x32158c6058347c1601b2d12bc696ac6901d8a9a9aa3ba10c27ab0a983e8425a7") +} + +func (VRFCoordinatorTestV25SubscriptionCreated) Topic() common.Hash { + return common.HexToHash("0x1d3015d7ba850fa198dc7b1a3f5d42779313a681035f77c8c03764c61005518d") +} + +func (VRFCoordinatorTestV25SubscriptionFunded) Topic() common.Hash { + return common.HexToHash("0x1ced9348ff549fceab2ac57cd3a9de38edaaab274b725ee82c23e8fc8c4eec7a") +} + +func (VRFCoordinatorTestV25SubscriptionFundedWithNative) Topic() common.Hash { + return common.HexToHash("0x7603b205d03651ee812f803fccde89f1012e545a9c99f0abfea9cedd0fd8e902") +} + +func (VRFCoordinatorTestV25SubscriptionOwnerTransferRequested) Topic() common.Hash { + return common.HexToHash("0x21a4dad170a6bf476c31bbcf4a16628295b0e450672eec25d7c93308e05344a1") +} + +func (VRFCoordinatorTestV25SubscriptionOwnerTransferred) Topic() common.Hash { + return common.HexToHash("0xd4114ab6e9af9f597c52041f32d62dc57c5c4e4c0d4427006069635e216c9386") +} + +func (_VRFCoordinatorTestV25 *VRFCoordinatorTestV25) Address() common.Address { + return _VRFCoordinatorTestV25.address +} + +type VRFCoordinatorTestV25Interface interface { + BLOCKHASHSTORE(opts *bind.CallOpts) (common.Address, error) + + LINK(opts *bind.CallOpts) (common.Address, error) + + LINKNATIVEFEED(opts *bind.CallOpts) (common.Address, error) + + MAXCONSUMERS(opts *bind.CallOpts) (uint16, error) + + MAXNUMWORDS(opts *bind.CallOpts) (uint32, error) + + MAXREQUESTCONFIRMATIONS(opts *bind.CallOpts) (uint16, error) + + GetActiveSubscriptionIds(opts *bind.CallOpts, startIndex *big.Int, maxCount *big.Int) ([]*big.Int, error) + + GetSubscription(opts *bind.CallOpts, subId *big.Int) (GetSubscription, + + error) + + HashOfKey(opts *bind.CallOpts, publicKey [2]*big.Int) ([32]byte, error) + + Owner(opts *bind.CallOpts) (common.Address, error) + + PendingRequestExists(opts *bind.CallOpts, subId *big.Int) (bool, error) + + SConfig(opts *bind.CallOpts) (SConfig, + + error) + + SCurrentSubNonce(opts *bind.CallOpts) (uint64, error) + + SFallbackWeiPerUnitLink(opts *bind.CallOpts) (*big.Int, error) + + SProvingKeyHashes(opts *bind.CallOpts, arg0 *big.Int) ([32]byte, error) + + SProvingKeys(opts *bind.CallOpts, arg0 [32]byte) (SProvingKeys, + + error) + + SRequestCommitments(opts *bind.CallOpts, arg0 *big.Int) ([32]byte, error) + + STotalBalance(opts *bind.CallOpts) (*big.Int, error) + + STotalNativeBalance(opts *bind.CallOpts) (*big.Int, error) + + AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) + + AcceptSubscriptionOwnerTransfer(opts *bind.TransactOpts, subId *big.Int) (*types.Transaction, error) + + AddConsumer(opts *bind.TransactOpts, subId *big.Int, consumer common.Address) (*types.Transaction, error) + + CancelSubscription(opts *bind.TransactOpts, subId *big.Int, to common.Address) (*types.Transaction, error) + + CreateSubscription(opts *bind.TransactOpts) (*types.Transaction, error) + + DeregisterMigratableCoordinator(opts *bind.TransactOpts, target common.Address) (*types.Transaction, error) + + DeregisterProvingKey(opts *bind.TransactOpts, publicProvingKey [2]*big.Int) (*types.Transaction, error) + + FulfillRandomWords(opts *bind.TransactOpts, proof VRFOldProof, rc VRFTypesRequestCommitmentV2Plus, onlyPremium bool) (*types.Transaction, error) + + FundSubscriptionWithNative(opts *bind.TransactOpts, subId *big.Int) (*types.Transaction, error) + + Migrate(opts *bind.TransactOpts, subId *big.Int, newCoordinator common.Address) (*types.Transaction, error) + + OnTokenTransfer(opts *bind.TransactOpts, arg0 common.Address, amount *big.Int, data []byte) (*types.Transaction, error) + + OwnerCancelSubscription(opts *bind.TransactOpts, subId *big.Int) (*types.Transaction, error) + + RecoverFunds(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + + RecoverNativeFunds(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + + RegisterMigratableCoordinator(opts *bind.TransactOpts, target common.Address) (*types.Transaction, error) + + RegisterProvingKey(opts *bind.TransactOpts, publicProvingKey [2]*big.Int, maxGas uint64) (*types.Transaction, error) + + RemoveConsumer(opts *bind.TransactOpts, subId *big.Int, consumer common.Address) (*types.Transaction, error) + + RequestRandomWords(opts *bind.TransactOpts, req VRFV2PlusClientRandomWordsRequest) (*types.Transaction, error) + + RequestSubscriptionOwnerTransfer(opts *bind.TransactOpts, subId *big.Int, newOwner common.Address) (*types.Transaction, error) + + SetConfig(opts *bind.TransactOpts, minimumRequestConfirmations uint16, maxGasLimit uint32, stalenessSeconds uint32, gasAfterPaymentCalculation uint32, fallbackWeiPerUnitLink *big.Int, fulfillmentFlatFeeNativePPM uint32, fulfillmentFlatFeeLinkDiscountPPM uint32, nativePremiumPercentage uint8, linkPremiumPercentage uint8) (*types.Transaction, error) + + SetLINKAndLINKNativeFeed(opts *bind.TransactOpts, link common.Address, linkNativeFeed common.Address) (*types.Transaction, error) + + TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + + Withdraw(opts *bind.TransactOpts, recipient common.Address) (*types.Transaction, error) + + WithdrawNative(opts *bind.TransactOpts, recipient common.Address) (*types.Transaction, error) + + FilterConfigSet(opts *bind.FilterOpts) (*VRFCoordinatorTestV25ConfigSetIterator, error) + + WatchConfigSet(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25ConfigSet) (event.Subscription, error) + + ParseConfigSet(log types.Log) (*VRFCoordinatorTestV25ConfigSet, error) + + FilterCoordinatorDeregistered(opts *bind.FilterOpts) (*VRFCoordinatorTestV25CoordinatorDeregisteredIterator, error) + + WatchCoordinatorDeregistered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25CoordinatorDeregistered) (event.Subscription, error) + + ParseCoordinatorDeregistered(log types.Log) (*VRFCoordinatorTestV25CoordinatorDeregistered, error) + + FilterCoordinatorRegistered(opts *bind.FilterOpts) (*VRFCoordinatorTestV25CoordinatorRegisteredIterator, error) + + WatchCoordinatorRegistered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25CoordinatorRegistered) (event.Subscription, error) + + ParseCoordinatorRegistered(log types.Log) (*VRFCoordinatorTestV25CoordinatorRegistered, error) + + FilterFallbackWeiPerUnitLinkUsed(opts *bind.FilterOpts) (*VRFCoordinatorTestV25FallbackWeiPerUnitLinkUsedIterator, error) + + WatchFallbackWeiPerUnitLinkUsed(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25FallbackWeiPerUnitLinkUsed) (event.Subscription, error) + + ParseFallbackWeiPerUnitLinkUsed(log types.Log) (*VRFCoordinatorTestV25FallbackWeiPerUnitLinkUsed, error) + + FilterFundsRecovered(opts *bind.FilterOpts) (*VRFCoordinatorTestV25FundsRecoveredIterator, error) + + WatchFundsRecovered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25FundsRecovered) (event.Subscription, error) + + ParseFundsRecovered(log types.Log) (*VRFCoordinatorTestV25FundsRecovered, error) + + FilterMigrationCompleted(opts *bind.FilterOpts) (*VRFCoordinatorTestV25MigrationCompletedIterator, error) + + WatchMigrationCompleted(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25MigrationCompleted) (event.Subscription, error) + + ParseMigrationCompleted(log types.Log) (*VRFCoordinatorTestV25MigrationCompleted, error) + + FilterNativeFundsRecovered(opts *bind.FilterOpts) (*VRFCoordinatorTestV25NativeFundsRecoveredIterator, error) + + WatchNativeFundsRecovered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25NativeFundsRecovered) (event.Subscription, error) + + ParseNativeFundsRecovered(log types.Log) (*VRFCoordinatorTestV25NativeFundsRecovered, error) + + FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*VRFCoordinatorTestV25OwnershipTransferRequestedIterator, error) + + WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25OwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferRequested(log types.Log) (*VRFCoordinatorTestV25OwnershipTransferRequested, error) + + FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*VRFCoordinatorTestV25OwnershipTransferredIterator, error) + + WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25OwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferred(log types.Log) (*VRFCoordinatorTestV25OwnershipTransferred, error) + + FilterProvingKeyDeregistered(opts *bind.FilterOpts) (*VRFCoordinatorTestV25ProvingKeyDeregisteredIterator, error) + + WatchProvingKeyDeregistered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25ProvingKeyDeregistered) (event.Subscription, error) + + ParseProvingKeyDeregistered(log types.Log) (*VRFCoordinatorTestV25ProvingKeyDeregistered, error) + + FilterProvingKeyRegistered(opts *bind.FilterOpts) (*VRFCoordinatorTestV25ProvingKeyRegisteredIterator, error) + + WatchProvingKeyRegistered(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25ProvingKeyRegistered) (event.Subscription, error) + + ParseProvingKeyRegistered(log types.Log) (*VRFCoordinatorTestV25ProvingKeyRegistered, error) + + FilterRandomWordsFulfilled(opts *bind.FilterOpts, requestId []*big.Int, subId []*big.Int) (*VRFCoordinatorTestV25RandomWordsFulfilledIterator, error) + + WatchRandomWordsFulfilled(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25RandomWordsFulfilled, requestId []*big.Int, subId []*big.Int) (event.Subscription, error) + + ParseRandomWordsFulfilled(log types.Log) (*VRFCoordinatorTestV25RandomWordsFulfilled, error) + + FilterRandomWordsRequested(opts *bind.FilterOpts, keyHash [][32]byte, subId []*big.Int, sender []common.Address) (*VRFCoordinatorTestV25RandomWordsRequestedIterator, error) + + WatchRandomWordsRequested(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25RandomWordsRequested, keyHash [][32]byte, subId []*big.Int, sender []common.Address) (event.Subscription, error) + + ParseRandomWordsRequested(log types.Log) (*VRFCoordinatorTestV25RandomWordsRequested, error) + + FilterSubscriptionCanceled(opts *bind.FilterOpts, subId []*big.Int) (*VRFCoordinatorTestV25SubscriptionCanceledIterator, error) + + WatchSubscriptionCanceled(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25SubscriptionCanceled, subId []*big.Int) (event.Subscription, error) + + ParseSubscriptionCanceled(log types.Log) (*VRFCoordinatorTestV25SubscriptionCanceled, error) + + FilterSubscriptionConsumerAdded(opts *bind.FilterOpts, subId []*big.Int) (*VRFCoordinatorTestV25SubscriptionConsumerAddedIterator, error) + + WatchSubscriptionConsumerAdded(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25SubscriptionConsumerAdded, subId []*big.Int) (event.Subscription, error) + + ParseSubscriptionConsumerAdded(log types.Log) (*VRFCoordinatorTestV25SubscriptionConsumerAdded, error) + + FilterSubscriptionConsumerRemoved(opts *bind.FilterOpts, subId []*big.Int) (*VRFCoordinatorTestV25SubscriptionConsumerRemovedIterator, error) + + WatchSubscriptionConsumerRemoved(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25SubscriptionConsumerRemoved, subId []*big.Int) (event.Subscription, error) + + ParseSubscriptionConsumerRemoved(log types.Log) (*VRFCoordinatorTestV25SubscriptionConsumerRemoved, error) + + FilterSubscriptionCreated(opts *bind.FilterOpts, subId []*big.Int) (*VRFCoordinatorTestV25SubscriptionCreatedIterator, error) + + WatchSubscriptionCreated(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25SubscriptionCreated, subId []*big.Int) (event.Subscription, error) + + ParseSubscriptionCreated(log types.Log) (*VRFCoordinatorTestV25SubscriptionCreated, error) + + FilterSubscriptionFunded(opts *bind.FilterOpts, subId []*big.Int) (*VRFCoordinatorTestV25SubscriptionFundedIterator, error) + + WatchSubscriptionFunded(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25SubscriptionFunded, subId []*big.Int) (event.Subscription, error) + + ParseSubscriptionFunded(log types.Log) (*VRFCoordinatorTestV25SubscriptionFunded, error) + + FilterSubscriptionFundedWithNative(opts *bind.FilterOpts, subId []*big.Int) (*VRFCoordinatorTestV25SubscriptionFundedWithNativeIterator, error) + + WatchSubscriptionFundedWithNative(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25SubscriptionFundedWithNative, subId []*big.Int) (event.Subscription, error) + + ParseSubscriptionFundedWithNative(log types.Log) (*VRFCoordinatorTestV25SubscriptionFundedWithNative, error) + + FilterSubscriptionOwnerTransferRequested(opts *bind.FilterOpts, subId []*big.Int) (*VRFCoordinatorTestV25SubscriptionOwnerTransferRequestedIterator, error) + + WatchSubscriptionOwnerTransferRequested(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25SubscriptionOwnerTransferRequested, subId []*big.Int) (event.Subscription, error) + + ParseSubscriptionOwnerTransferRequested(log types.Log) (*VRFCoordinatorTestV25SubscriptionOwnerTransferRequested, error) + + FilterSubscriptionOwnerTransferred(opts *bind.FilterOpts, subId []*big.Int) (*VRFCoordinatorTestV25SubscriptionOwnerTransferredIterator, error) + + WatchSubscriptionOwnerTransferred(opts *bind.WatchOpts, sink chan<- *VRFCoordinatorTestV25SubscriptionOwnerTransferred, subId []*big.Int) (event.Subscription, error) + + ParseSubscriptionOwnerTransferred(log types.Log) (*VRFCoordinatorTestV25SubscriptionOwnerTransferred, error) + + ParseLog(log types.Log) (generated.AbigenLog, error) + + Address() common.Address +} diff --git a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 41c270d61c..c363892b9d 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,7 +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 b76cf77e3e8200c5f292e93af3f620f68f207f83634aacaaee43d682701dfea3 +arbitrum_module: ../../contracts/solc/v0.8.19/ArbitrumModule/ArbitrumModule.abi ../../contracts/solc/v0.8.19/ArbitrumModule/ArbitrumModule.bin 12a7bad1f887d832d101a73ae279a91a90c93fd72befea9983e85eff493f62f4 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_compatible_utils: ../../contracts/solc/v0.8.19/AutomationCompatibleUtils/AutomationCompatibleUtils.abi ../../contracts/solc/v0.8.19/AutomationCompatibleUtils/AutomationCompatibleUtils.bin dfe88f4f40d124b8cb5f36a7e9f9328008ca57f7ec5d07a28d949d569d5f2834 @@ -12,10 +12,10 @@ automation_registrar_wrapper2_3: ../../contracts/solc/v0.8.19/AutomationRegistra automation_registry_logic_a_wrapper_2_2: ../../contracts/solc/v0.8.19/AutomationRegistryLogicA2_2/AutomationRegistryLogicA2_2.abi ../../contracts/solc/v0.8.19/AutomationRegistryLogicA2_2/AutomationRegistryLogicA2_2.bin 2f267fb8467a15c587ce4586ac56069f7229344ad3936430d7c7624c0528a171 automation_registry_logic_a_wrapper_2_3: ../../contracts/solc/v0.8.19/AutomationRegistryLogicA2_3/AutomationRegistryLogicA2_3.abi ../../contracts/solc/v0.8.19/AutomationRegistryLogicA2_3/AutomationRegistryLogicA2_3.bin 73b5cc3ece642abbf6f2a4c9188335b71404f4dd0ad10b761390b6397af6f1c8 automation_registry_logic_b_wrapper_2_2: ../../contracts/solc/v0.8.19/AutomationRegistryLogicB2_2/AutomationRegistryLogicB2_2.abi ../../contracts/solc/v0.8.19/AutomationRegistryLogicB2_2/AutomationRegistryLogicB2_2.bin a6d33dfbbfb0ff253eb59a51f4f6d6d4c22ea5ec95aae52d25d49a312b37a22f -automation_registry_logic_b_wrapper_2_3: ../../contracts/solc/v0.8.19/AutomationRegistryLogicB2_3/AutomationRegistryLogicB2_3.abi ../../contracts/solc/v0.8.19/AutomationRegistryLogicB2_3/AutomationRegistryLogicB2_3.bin fbf6f6cf4e6858855ff5da847c3baa4859dd997cfae51f2fa0651e4fa15b92c9 -automation_registry_logic_c_wrapper_2_3: ../../contracts/solc/v0.8.19/AutomationRegistryLogicC2_3/AutomationRegistryLogicC2_3.abi ../../contracts/solc/v0.8.19/AutomationRegistryLogicC2_3/AutomationRegistryLogicC2_3.bin 6bfe0f54fa7a587a83b6981ffdef28b3cb5e24cae1c95becdf59eed21147d289 -automation_registry_wrapper_2_2: ../../contracts/solc/v0.8.19/AutomationRegistry2_2/AutomationRegistry2_2.abi ../../contracts/solc/v0.8.19/AutomationRegistry2_2/AutomationRegistry2_2.bin de60f69878e9b32a291a001c91fc8636544c2cfbd9b507c8c1a4873b602bfb62 -automation_registry_wrapper_2_3: ../../contracts/solc/v0.8.19/AutomationRegistry2_3/AutomationRegistry2_3.abi ../../contracts/solc/v0.8.19/AutomationRegistry2_3/AutomationRegistry2_3.bin f8f920a225fdb1e36948dd95bae3aa46ecc2b01fd113480e111960b5e5f95624 +automation_registry_logic_b_wrapper_2_3: ../../contracts/solc/v0.8.19/AutomationRegistryLogicB2_3/AutomationRegistryLogicB2_3.abi ../../contracts/solc/v0.8.19/AutomationRegistryLogicB2_3/AutomationRegistryLogicB2_3.bin e628b4ba1ca8bf45c2b08c6b80f0b14efbd2dff13b85e5a9ebf643df32335ed2 +automation_registry_logic_c_wrapper_2_3: ../../contracts/solc/v0.8.19/AutomationRegistryLogicC2_3/AutomationRegistryLogicC2_3.abi ../../contracts/solc/v0.8.19/AutomationRegistryLogicC2_3/AutomationRegistryLogicC2_3.bin 19d59318e42f28777756eff60d5c5e52563a2fffb8e3f0f0b07b6d36d82b2c55 +automation_registry_wrapper_2_2: ../../contracts/solc/v0.8.19/AutomationRegistry2_2/AutomationRegistry2_2.abi ../../contracts/solc/v0.8.19/AutomationRegistry2_2/AutomationRegistry2_2.bin 7072ba90159d84572f427ec816e78aa032cf907b39bf228185e0c446842f7c11 +automation_registry_wrapper_2_3: ../../contracts/solc/v0.8.19/AutomationRegistry2_3/AutomationRegistry2_3.abi ../../contracts/solc/v0.8.19/AutomationRegistry2_3/AutomationRegistry2_3.bin b6163402434b84e3b66bc078f6efac121c1e1240dca0e8ea89c43db46b4e308b automation_utils_2_1: ../../contracts/solc/v0.8.16/AutomationUtils2_1/AutomationUtils2_1.abi ../../contracts/solc/v0.8.16/AutomationUtils2_1/AutomationUtils2_1.bin 815b17b63f15d26a0274b962eefad98cdee4ec897ead58688bbb8e2470e585f5 automation_utils_2_2: ../../contracts/solc/v0.8.19/AutomationUtils2_2/AutomationUtils2_2.abi ../../contracts/solc/v0.8.19/AutomationUtils2_2/AutomationUtils2_2.bin 8743f6231aaefa3f2a0b2d484258070d506e2d0860690e66890dccc3949edb2e automation_utils_2_3: ../../contracts/solc/v0.8.19/AutomationUtils2_3/AutomationUtils2_3.abi ../../contracts/solc/v0.8.19/AutomationUtils2_3/AutomationUtils2_3.bin 11e2b481dc9a4d936e3443345d45d2cc571164459d214917b42a8054b295393b @@ -23,8 +23,8 @@ batch_blockhash_store: ../../contracts/solc/v0.8.19/BatchBlockhashStore/BatchBlo batch_vrf_coordinator_v2: ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2/BatchVRFCoordinatorV2.abi ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2/BatchVRFCoordinatorV2.bin 4512f4313bc5c078215c9241a69045a2a3cfecd6adfcef2f13037183a2d71483 batch_vrf_coordinator_v2plus: ../../contracts/solc/v0.8.19/BatchVRFCoordinatorV2Plus/BatchVRFCoordinatorV2Plus.abi ../../contracts/solc/v0.8.19/BatchVRFCoordinatorV2Plus/BatchVRFCoordinatorV2Plus.bin f13715b38b5b9084b08bffa571fb1c8ef686001535902e1255052f074b31ad4e blockhash_store: ../../contracts/solc/v0.8.19/BlockhashStore/BlockhashStore.abi ../../contracts/solc/v0.8.19/BlockhashStore/BlockhashStore.bin 31b118f9577240c8834c35f8b5a1440e82a6ca8aea702970de2601824b6ab0e1 -chain_module_base: ../../contracts/solc/v0.8.19/ChainModuleBase/ChainModuleBase.abi ../../contracts/solc/v0.8.19/ChainModuleBase/ChainModuleBase.bin 39dfce79330e921e5c169051b11c6e5ea15cd4db5a7b09c06aabbe9658148915 -chain_reader_tester: ../../contracts/solc/v0.8.19/ChainReaderTester/ChainReaderTester.abi ../../contracts/solc/v0.8.19/ChainReaderTester/ChainReaderTester.bin b3718dad488f54de97d124221d96b867c81e11210084a1fad379cb8385d37ffe +chain_module_base: ../../contracts/solc/v0.8.19/ChainModuleBase/ChainModuleBase.abi ../../contracts/solc/v0.8.19/ChainModuleBase/ChainModuleBase.bin 7a82cc28014761090185c2650239ad01a0901181f1b2b899b42ca293bcda3741 +chain_reader_tester: ../../contracts/solc/v0.8.19/ChainReaderTester/ChainReaderTester.abi ../../contracts/solc/v0.8.19/ChainReaderTester/ChainReaderTester.bin b207f9e6bf71e445a2664a602677011b87b80bf95c6352fd7869f1a9ddb08a5b chain_specific_util_helper: ../../contracts/solc/v0.8.6/ChainSpecificUtilHelper/ChainSpecificUtilHelper.abi ../../contracts/solc/v0.8.6/ChainSpecificUtilHelper/ChainSpecificUtilHelper.bin 66eb30b0717fefe05672df5ec863c0b9a5a654623c4757307a2726d8f31e26b1 counter: ../../contracts/solc/v0.8.6/Counter/Counter.abi ../../contracts/solc/v0.8.6/Counter/Counter.bin 6ca06e000e8423573ffa0bdfda749d88236ab3da2a4cbb4a868c706da90488c9 cron_upkeep_factory_wrapper: ../../contracts/solc/v0.8.6/CronUpkeepFactory/CronUpkeepFactory.abi - dacb0f8cdf54ae9d2781c5e720fc314b32ed5e58eddccff512c75d6067292cd7 @@ -35,7 +35,7 @@ gas_wrapper_mock: ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageW i_automation_registry_master_wrapper_2_2: ../../contracts/solc/v0.8.19/IAutomationRegistryMaster/IAutomationRegistryMaster.abi ../../contracts/solc/v0.8.19/IAutomationRegistryMaster/IAutomationRegistryMaster.bin 9ff7087179f89f9b05964ebc3e71332fce11f1b8e85058f7b16b3bc0dd6fb96b i_automation_registry_master_wrapper_2_3: ../../contracts/solc/v0.8.19/IAutomationRegistryMaster2_3/IAutomationRegistryMaster2_3.abi ../../contracts/solc/v0.8.19/IAutomationRegistryMaster2_3/IAutomationRegistryMaster2_3.bin 06cc87c122452f63fbe84f65329978f30281613be0caa261e53503d94763e921 i_automation_v21_plus_common: ../../contracts/solc/v0.8.19/IAutomationV21PlusCommon/IAutomationV21PlusCommon.abi ../../contracts/solc/v0.8.19/IAutomationV21PlusCommon/IAutomationV21PlusCommon.bin e8a601ec382c0a2e83c49759de13b0622b5e04e6b95901e96a1e9504329e594c -i_chain_module: ../../contracts/solc/v0.8.19/IChainModule/IChainModule.abi ../../contracts/solc/v0.8.19/IChainModule/IChainModule.bin 383611981c86c70522f41b8750719faacc7d7933a22849d5004799ebef3371fa +i_chain_module: ../../contracts/solc/v0.8.19/IChainModule/IChainModule.abi ../../contracts/solc/v0.8.19/IChainModule/IChainModule.bin 8ccb8fcfd1ae331a46b4469e1567c380e2a6d2bf21a9976d6c4c655a716aaa42 i_keeper_registry_master_wrapper_2_1: ../../contracts/solc/v0.8.16/IKeeperRegistryMaster/IKeeperRegistryMaster.abi ../../contracts/solc/v0.8.16/IKeeperRegistryMaster/IKeeperRegistryMaster.bin ee0f150b3afbab2df3d24ff3f4c87851efa635da30db04cd1f70cb4e185a1781 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 @@ -60,9 +60,9 @@ mock_ethusd_aggregator_wrapper: ../../contracts/solc/v0.8.19/MockETHUSDAggregato 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 88e6baa5d9b255eea02616fbcb2cbe21a25ab46adeb6395f6289d169dec949ae operator_wrapper: ../../contracts/solc/v0.8.19/Operator/Operator.abi ../../contracts/solc/v0.8.19/Operator/Operator.bin 23c3888eaa7259e6adf2153d09abae8f4b1987dc44200363faab1e65483f32d5 -optimism_module: ../../contracts/solc/v0.8.19/OptimismModule/OptimismModule.abi ../../contracts/solc/v0.8.19/OptimismModule/OptimismModule.bin a1f8ee97e12b1b2311db03b94dc52b91f3c2e9a2f8d554031a9c7b41e4432280 +optimism_module_v2: ../../contracts/solc/v0.8.19/OptimismModuleV2/OptimismModuleV2.abi ../../contracts/solc/v0.8.19/OptimismModuleV2/OptimismModuleV2.bin 6bc8f93d3a49b3fdecc169214565e6fe5690427860ca4f674818c611dd719502 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 8de157cb7e5bc78146548212803d60926c8483aca7e912d802b7c66dc5d2ab11 +scroll_module: ../../contracts/solc/v0.8.19/ScrollModule/ScrollModule.abi ../../contracts/solc/v0.8.19/ScrollModule/ScrollModule.bin 0b99b89ff0c8d95a2ab273c93355e572b9e052ce2a9507498a06e0915b541a86 simple_log_upkeep_counter_wrapper: ../../contracts/solc/v0.8.6/SimpleLogUpkeepCounter/SimpleLogUpkeepCounter.abi ../../contracts/solc/v0.8.6/SimpleLogUpkeepCounter/SimpleLogUpkeepCounter.bin 7557d117a066cd8cf35f635bc085ee11795442073c18f8610ede9037b74fd814 solidity_vrf_consumer_interface_v08: ../../contracts/solc/v0.8.6/VRFConsumer/VRFConsumer.abi ../../contracts/solc/v0.8.6/VRFConsumer/VRFConsumer.bin b14f9136b15e3dc9d6154d5700f3ed4cf88ddc4f70f20c3bb57fc46050904c8f solidity_vrf_request_id_v08: ../../contracts/solc/v0.8.6/VRFRequestIDBaseTestHelper/VRFRequestIDBaseTestHelper.abi ../../contracts/solc/v0.8.6/VRFRequestIDBaseTestHelper/VRFRequestIDBaseTestHelper.bin f2559015d6f3e5d285c57b011be9b2300632e93dd6c4524e58202d6200f09edc @@ -82,6 +82,7 @@ vrf_consumer_v2_plus_upgradeable_example: ../../contracts/solc/v0.8.19/VRFConsum vrf_consumer_v2_upgradeable_example: ../../contracts/solc/v0.8.6/VRFConsumerV2UpgradeableExample/VRFConsumerV2UpgradeableExample.abi ../../contracts/solc/v0.8.6/VRFConsumerV2UpgradeableExample/VRFConsumerV2UpgradeableExample.bin f1790a9a2f2a04c730593e483459709cb89e897f8a19d7a3ac0cfe6a97265e6e vrf_coordinator_mock: ../../contracts/solc/v0.8.6/VRFCoordinatorMock/VRFCoordinatorMock.abi ../../contracts/solc/v0.8.6/VRFCoordinatorMock/VRFCoordinatorMock.bin 5c495cf8df1f46d8736b9150cdf174cce358cb8352f60f0d5bb9581e23920501 vrf_coordinator_test_v2: ../../contracts/solc/v0.8.6/VRFCoordinatorTestV2/VRFCoordinatorTestV2.abi ../../contracts/solc/v0.8.6/VRFCoordinatorTestV2/VRFCoordinatorTestV2.bin ff6c0056c6181ea75f667beed21ff4610f417dd50ceabf2dec8fa42e84851f50 +vrf_coordinator_test_v2_5: ../../contracts/solc/v0.8.19/VRFCoordinatorTestV2_5/VRFCoordinatorTestV2_5.abi ../../contracts/solc/v0.8.19/VRFCoordinatorTestV2_5/VRFCoordinatorTestV2_5.bin ab793e7d72b2d10d5c80b5358ca98caf5ff8a8686700735b198ed811272d7910 vrf_coordinator_v2: ../../contracts/solc/v0.8.6/VRFCoordinatorV2/VRFCoordinatorV2.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2/VRFCoordinatorV2.bin 156fbbc19489383901087c2076648ccd343bcd9a332f1ad25974da834c5be961 vrf_coordinator_v2_5: ../../contracts/solc/v0.8.19/VRFCoordinatorV2_5/VRFCoordinatorV2_5.abi ../../contracts/solc/v0.8.19/VRFCoordinatorV2_5/VRFCoordinatorV2_5.bin 3c766dbdefcc895ad475de96c65b6c48c868b8dc889ee750bba6711b1e5ec41d vrf_coordinator_v2_5_arbitrum: ../../contracts/solc/v0.8.19/VRFCoordinatorV2_5_Arbitrum/VRFCoordinatorV2_5_Arbitrum.abi ../../contracts/solc/v0.8.19/VRFCoordinatorV2_5_Arbitrum/VRFCoordinatorV2_5_Arbitrum.bin 1a2431ee76e307b45f683c439d08b9096a08f08aaf9ca132ea5b36b409962abe diff --git a/core/gethwrappers/go_generate.go b/core/gethwrappers/go_generate.go index 9d1a5194e8..1fadd651cc 100644 --- a/core/gethwrappers/go_generate.go +++ b/core/gethwrappers/go_generate.go @@ -53,7 +53,7 @@ package gethwrappers //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/AutomationUtils2_3/AutomationUtils2_3.abi ../../contracts/solc/v0.8.19/AutomationUtils2_3/AutomationUtils2_3.bin AutomationUtils automation_utils_2_3 //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/OptimismModuleV2/OptimismModuleV2.abi ../../contracts/solc/v0.8.19/OptimismModuleV2/OptimismModuleV2.bin OptimismModuleV2 optimism_module_v2 //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.19/IAutomationV21PlusCommon/IAutomationV21PlusCommon.abi ../../contracts/solc/v0.8.19/IAutomationV21PlusCommon/IAutomationV21PlusCommon.bin IAutomationV21PlusCommon i_automation_v21_plus_common @@ -143,6 +143,7 @@ package gethwrappers //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/VRFCoordinatorV2_5_Optimism/VRFCoordinatorV2_5_Optimism.abi ../../contracts/solc/v0.8.19/VRFCoordinatorV2_5_Optimism/VRFCoordinatorV2_5_Optimism.bin VRFCoordinatorV2_5_Optimism vrf_coordinator_v2_5_optimism //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/VRFV2PlusWrapper_Arbitrum/VRFV2PlusWrapper_Arbitrum.abi ../../contracts/solc/v0.8.19/VRFV2PlusWrapper_Arbitrum/VRFV2PlusWrapper_Arbitrum.bin VRFV2PlusWrapper_Arbitrum vrfv2plus_wrapper_arbitrum //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/VRFV2PlusWrapper_Optimism/VRFV2PlusWrapper_Optimism.abi ../../contracts/solc/v0.8.19/VRFV2PlusWrapper_Optimism/VRFV2PlusWrapper_Optimism.bin VRFV2PlusWrapper_Optimism vrfv2plus_wrapper_optimism +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/VRFCoordinatorTestV2_5/VRFCoordinatorTestV2_5.abi ../../contracts/solc/v0.8.19/VRFCoordinatorTestV2_5/VRFCoordinatorTestV2_5.bin VRFCoordinatorTestV2_5 vrf_coordinator_test_v2_5 // Aggregators //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/AggregatorV2V3Interface/AggregatorV2V3Interface.abi ../../contracts/solc/v0.8.6/AggregatorV2V3Interface/AggregatorV2V3Interface.bin AggregatorV2V3Interface aggregator_v2v3_interface @@ -154,20 +155,14 @@ package gethwrappers // ChainReader test contract //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/ChainReaderTester/ChainReaderTester.abi ../../contracts/solc/v0.8.19/ChainReaderTester/ChainReaderTester.bin ChainReaderTester chain_reader_tester -// Chainlink Functions //go:generate go generate ./functions - -// Chainlink Keystone //go:generate go generate ./keystone - -// Mercury //go:generate go generate ./llo-feeds - -// Operator Forwarder //go:generate go generate ./operatorforwarder - -// Shared //go:generate go generate ./shared +//go:generate go generate ./transmission +//go:generate go generate ./ccip +//go:generate go generate ./liquiditymanager // CCIP //go:generate go generate ./ccip @@ -180,6 +175,3 @@ package gethwrappers // 3. Compile events mock contracts. ./generation/compile_event_mock_contract.sh calls contracts/scripts/native_solc_compile_all_events_mock to compile events mock contracts. // 4. Generate wrappers for events mock contracts. //go:generate ./generation/compile_event_mock_contract.sh - -// Transmission -//go:generate go generate ./transmission diff --git a/core/gethwrappers/keystone/generated/capabilities_registry/capabilities_registry.go b/core/gethwrappers/keystone/generated/capabilities_registry/capabilities_registry.go index c345a86569..e5ae52c71c 100644 --- a/core/gethwrappers/keystone/generated/capabilities_registry/capabilities_registry.go +++ b/core/gethwrappers/keystone/generated/capabilities_registry/capabilities_registry.go @@ -86,8 +86,8 @@ type CapabilitiesRegistryNodeParams struct { } var CapabilitiesRegistryMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AccessForbidden\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityIsDeprecated\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"CapabilityRequiredByDON\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"DONDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONCapability\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONNode\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"proposedConfigurationContract\",\"type\":\"address\"}],\"name\":\"InvalidCapabilityConfigurationContractInterface\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"nodeCount\",\"type\":\"uint256\"}],\"name\":\"InvalidFaultTolerance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"name\":\"InvalidNodeCapabilities\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeOperatorAdmin\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"InvalidNodeP2PId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"lengthOne\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lengthTwo\",\"type\":\"uint256\"}],\"name\":\"LengthMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"NodeAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"NodeDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"NodeDoesNotSupportCapability\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"}],\"name\":\"NodeOperatorDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"NodePartOfCapabilitiesDON\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"NodePartOfWorkflowDON\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityConfigured\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDeprecated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"}],\"name\":\"NodeAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"}],\"name\":\"NodeOperatorRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"NodeRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"}],\"name\":\"NodeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"labelledName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"version\",\"type\":\"string\"},{\"internalType\":\"enumCapabilitiesRegistry.CapabilityType\",\"name\":\"capabilityType\",\"type\":\"uint8\"},{\"internalType\":\"enumCapabilitiesRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilitiesRegistry.Capability[]\",\"name\":\"capabilities\",\"type\":\"tuple[]\"}],\"name\":\"addCapabilities\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"nodes\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilitiesRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"acceptsWorkflows\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"}],\"name\":\"addDON\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilitiesRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"addNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilitiesRegistry.NodeParams[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"addNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"name\":\"deprecateCapabilities\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCapabilities\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"hashedId\",\"type\":\"bytes32\"},{\"internalType\":\"string\",\"name\":\"labelledName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"version\",\"type\":\"string\"},{\"internalType\":\"enumCapabilitiesRegistry.CapabilityType\",\"name\":\"capabilityType\",\"type\":\"uint8\"},{\"internalType\":\"enumCapabilitiesRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"isDeprecated\",\"type\":\"bool\"}],\"internalType\":\"structCapabilitiesRegistry.CapabilityInfo[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedId\",\"type\":\"bytes32\"}],\"name\":\"getCapability\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"hashedId\",\"type\":\"bytes32\"},{\"internalType\":\"string\",\"name\":\"labelledName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"version\",\"type\":\"string\"},{\"internalType\":\"enumCapabilitiesRegistry.CapabilityType\",\"name\":\"capabilityType\",\"type\":\"uint8\"},{\"internalType\":\"enumCapabilitiesRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"isDeprecated\",\"type\":\"bool\"}],\"internalType\":\"structCapabilitiesRegistry.CapabilityInfo\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"getCapabilityConfigs\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"getDON\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"id\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"acceptsWorkflows\",\"type\":\"bool\"},{\"internalType\":\"bytes32[]\",\"name\":\"nodeP2PIds\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilitiesRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"}],\"internalType\":\"structCapabilitiesRegistry.DONInfo\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDONs\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"id\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"acceptsWorkflows\",\"type\":\"bool\"},{\"internalType\":\"bytes32[]\",\"name\":\"nodeP2PIds\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilitiesRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"}],\"internalType\":\"structCapabilitiesRegistry.DONInfo[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"labelledName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"version\",\"type\":\"string\"}],\"name\":\"getHashedCapabilityId\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"getNode\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"workflowDONId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256[]\",\"name\":\"capabilitiesDONIds\",\"type\":\"uint256[]\"}],\"internalType\":\"structCapabilitiesRegistry.NodeInfo\",\"name\":\"nodeInfo\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"}],\"name\":\"getNodeOperator\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilitiesRegistry.NodeOperator\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getNodeOperators\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilitiesRegistry.NodeOperator[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getNodes\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"workflowDONId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256[]\",\"name\":\"capabilitiesDONIds\",\"type\":\"uint256[]\"}],\"internalType\":\"structCapabilitiesRegistry.NodeInfo[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"isCapabilityDeprecated\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32[]\",\"name\":\"donIds\",\"type\":\"uint32[]\"}],\"name\":\"removeDONs\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint32[]\"}],\"name\":\"removeNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"removedNodeP2PIds\",\"type\":\"bytes32[]\"}],\"name\":\"removeNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32[]\",\"name\":\"nodes\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilitiesRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"acceptsWorkflows\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"}],\"name\":\"updateDON\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint32[]\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilitiesRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"updateNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilitiesRegistry.NodeParams[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"updateNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x6080604052600e80546001600160401b0319166401000000011790553480156200002857600080fd5b503380600081620000805760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000b357620000b381620000bc565b50505062000167565b336001600160a01b03821603620001165760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000077565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6150f680620001776000396000f3fe608060405234801561001057600080fd5b50600436106101ae5760003560e01c80635e65e309116100ee5780638da5cb5b11610097578063d8bc7b6811610071578063d8bc7b68146103f6578063ddbe4f8214610409578063e29581aa1461041e578063f2fde38b1461043357600080fd5b80638da5cb5b1461039b5780639cb7c5f4146103c3578063d59a79f6146103e357600080fd5b806373ac22b4116100c857806373ac22b41461036d57806379ba50971461038057806386fa42461461038857600080fd5b80635e65e3091461033257806366acaa3314610345578063715f52951461035a57600080fd5b8063235374051161015b578063398f377311610135578063398f3773146102cb5780633f2a13c9146102de57806350c946fe146102ff5780635d83d9671461031f57600080fd5b80632353740514610285578063275459f2146102a55780632c01a1e8146102b857600080fd5b80631d05394c1161018c5780631d05394c1461023b578063214502431461025057806322bdbcbc1461026557600080fd5b80630fe5800a146101b357806312570011146101d9578063181f5a77146101fc575b600080fd5b6101c66101c1366004613e8b565b610446565b6040519081526020015b60405180910390f35b6101ec6101e7366004613eef565b61047a565b60405190151581526020016101d0565b604080518082018252601a81527f4361706162696c6974696573526567697374727920312e302e30000000000000602082015290516101d09190613f76565b61024e610249366004613fce565b610487565b005b61025861069c565b6040516101d09190614150565b6102786102733660046141eb565b6107f9565b6040516101d09190614243565b6102986102933660046141eb565b6108e6565b6040516101d09190614256565b61024e6102b3366004613fce565b61092a565b61024e6102c6366004613fce565b610a01565b61024e6102d9366004613fce565b610c9d565b6102f16102ec366004614269565b610e5c565b6040516101d0929190614293565b61031261030d366004613eef565b611048565b6040516101d09190614358565b61024e61032d366004613fce565b611122565b61024e610340366004613fce565b611217565b61034d61193f565b6040516101d0919061436b565b61024e610368366004613fce565b611b22565b61024e61037b366004613fce565b611bd4565b61024e6120a2565b61024e6103963660046143e0565b61219f565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101d0565b6103d66103d1366004613eef565b6124df565b6040516101d0919061452f565b61024e6103f1366004614561565b61271a565b61024e610404366004614616565b6127e3565b6104116128ad565b6040516101d091906146bb565b6104266129a1565b6040516101d09190614730565b61024e6104413660046147c9565b612aaa565b6000828260405160200161045b929190614293565b6040516020818303038152906040528051906020012090505b92915050565b6000610474600583612abe565b61048f612ad9565b60005b818110156106975760008383838181106104ae576104ae6147e4565b90506020020160208101906104c391906141eb565b63ffffffff8181166000908152600d60209081526040808320805464010000000081049095168085526001820190935290832094955093909290916a010000000000000000000090910460ff16905b61051b83612b5c565b8110156105bb57811561057157600c60006105368584612b66565b8152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffff1690556105b3565b6105b18663ffffffff16600c60006105928588612b6690919063ffffffff16565b8152602001908152602001600020600401612b7290919063ffffffff16565b505b600101610512565b508354640100000000900463ffffffff16600003610612576040517f2b62be9b00000000000000000000000000000000000000000000000000000000815263ffffffff861660048201526024015b60405180910390fd5b63ffffffff85166000818152600d6020908152604080832080547fffffffffffffffffffffffffffffffffffffffffff00000000000000000000001690558051938452908301919091527ff264aae70bf6a9d90e68e0f9b393f4e7fbea67b063b0f336e0b36c1581703651910160405180910390a15050505050806001019050610492565b505050565b600e54606090640100000000900463ffffffff1660006106bd600183614842565b63ffffffff1667ffffffffffffffff8111156106db576106db613d25565b60405190808252806020026020018201604052801561076257816020015b6040805160e081018252600080825260208083018290529282018190526060808301829052608083019190915260a0820181905260c082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816106f95790505b509050600060015b8363ffffffff168163ffffffff1610156107d65763ffffffff8082166000908152600d602052604090205416156107ce576107a481612b7e565b8383815181106107b6576107b66147e4565b6020026020010181905250816107cb9061485f565b91505b60010161076a565b506107e2600184614842565b63ffffffff1681146107f2578082525b5092915050565b60408051808201909152600081526060602082015263ffffffff82166000908152600b60209081526040918290208251808401909352805473ffffffffffffffffffffffffffffffffffffffff168352600181018054919284019161085d90614897565b80601f016020809104026020016040519081016040528092919081815260200182805461088990614897565b80156108d65780601f106108ab576101008083540402835291602001916108d6565b820191906000526020600020905b8154815290600101906020018083116108b957829003601f168201915b5050505050815250509050919050565b6040805160e0810182526000808252602082018190529181018290526060808201839052608082019290925260a0810182905260c081019190915261047482612b7e565b610932612ad9565b60005b63ffffffff811682111561069757600083838363ffffffff1681811061095d5761095d6147e4565b905060200201602081019061097291906141eb565b63ffffffff81166000908152600b6020526040812080547fffffffffffffffffffffffff00000000000000000000000000000000000000001681559192506109bd6001830182613cb8565b505060405163ffffffff8216907fa59268ca81d40429e65ccea5385b59cf2d3fc6519371dee92f8eb1dae5107a7a90600090a2506109fa816148ea565b9050610935565b6000805473ffffffffffffffffffffffffffffffffffffffff163314905b82811015610c97576000848483818110610a3b57610a3b6147e4565b602090810292909201356000818152600c90935260409092206001810154929350919050610a98576040517fd82f6adb00000000000000000000000000000000000000000000000000000000815260048101839052602401610609565b6000610aa682600401612b5c565b1115610afb57610ab96004820184612b66565b6040517f60a6d89800000000000000000000000000000000000000000000000000000000815263ffffffff909116600482015260248101839052604401610609565b805468010000000000000000900463ffffffff1615610b635780546040517f60b9df730000000000000000000000000000000000000000000000000000000081526801000000000000000090910463ffffffff16600482015260248101839052604401610609565b83158015610b9d5750805463ffffffff166000908152600b602052604090205473ffffffffffffffffffffffffffffffffffffffff163314155b15610bd6576040517f9473075d000000000000000000000000000000000000000000000000000000008152336004820152602401610609565b6001810154610be790600790612b72565b506002810154610bf990600990612b72565b506000828152600c6020526040812080547fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016815560018101829055600281018290559060048201818181610c4e8282613cf2565b5050505050507f5254e609a97bab37b7cc79fe128f85c097bd6015c6e1624ae0ba392eb975320582604051610c8591815260200190565b60405180910390a15050600101610a1f565b50505050565b610ca5612ad9565b60005b81811015610697576000838383818110610cc457610cc46147e4565b9050602002810190610cd6919061490d565b610cdf9061494b565b805190915073ffffffffffffffffffffffffffffffffffffffff16610d30576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600e54604080518082018252835173ffffffffffffffffffffffffffffffffffffffff908116825260208086015181840190815263ffffffff9095166000818152600b909252939020825181547fffffffffffffffffffffffff00000000000000000000000000000000000000001692169190911781559251919290916001820190610dbc9082614a05565b5050600e8054909150600090610dd79063ffffffff166148ea565b91906101000a81548163ffffffff021916908363ffffffff160217905550816000015173ffffffffffffffffffffffffffffffffffffffff168163ffffffff167f78e94ca80be2c30abc061b99e7eb8583b1254781734b1e3ce339abb57da2fe8e8460200151604051610e4a9190613f76565b60405180910390a35050600101610ca8565b63ffffffff8083166000908152600d60209081526040808320805464010000000090049094168084526001909401825280832085845260030190915281208054606093849390929091610eae90614897565b80601f0160208091040260200160405190810160405280929190818152602001828054610eda90614897565b8015610f275780601f10610efc57610100808354040283529160200191610f27565b820191906000526020600020905b815481529060010190602001808311610f0a57829003601f168201915b5050506000888152600260208190526040909120015492935060609262010000900473ffffffffffffffffffffffffffffffffffffffff1615915061103a905057600086815260026020819052604091829020015490517f8318ed5d00000000000000000000000000000000000000000000000000000000815263ffffffff891660048201526201000090910473ffffffffffffffffffffffffffffffffffffffff1690638318ed5d90602401600060405180830381865afa158015610ff1573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526110379190810190614b1f565b90505b9093509150505b9250929050565b6040805160e0810182526000808252602082018190529181018290526060808201839052608082019290925260a0810182905260c08101919091526040805160e0810182526000848152600c6020908152838220805463ffffffff8082168652640100000000820481168487018190526801000000000000000090920416858701526001820154606086015260028201546080860152835260030190529190912060a08201906110f790612e49565b815260200161111a600c6000868152602001908152602001600020600401612e49565b905292915050565b61112a612ad9565b60005b81811015610697576000838383818110611149576111496147e4565b905060200201359050611166816003612abe90919063ffffffff16565b61119f576040517fe181733f00000000000000000000000000000000000000000000000000000000815260048101829052602401610609565b6111aa600582612e56565b6111e3576040517ff7d7a29400000000000000000000000000000000000000000000000000000000815260048101829052602401610609565b60405181907fdcea1b78b6ddc31592a94607d537543fcaafda6cc52d6d5cc7bbfca1422baf2190600090a25060010161112d565b6000805473ffffffffffffffffffffffffffffffffffffffff163314905b82811015610c97576000848483818110611251576112516147e4565b90506020028101906112639190614b8d565b61126c90614bc1565b6040808201516000908152600c6020908152828220805463ffffffff168352600b82528383208451808601909552805473ffffffffffffffffffffffffffffffffffffffff16855260018101805496975091959394939092840191906112d190614897565b80601f01602080910402602001604051908101604052809291908181526020018280546112fd90614897565b801561134a5780601f1061131f5761010080835404028352916020019161134a565b820191906000526020600020905b81548152906001019060200180831161132d57829003601f168201915b50505091909252505050600183015490915061139a5782604001516040517fd82f6adb00000000000000000000000000000000000000000000000000000000815260040161060991815260200190565b841580156113bf5750805173ffffffffffffffffffffffffffffffffffffffff163314155b156113f8576040517f9473075d000000000000000000000000000000000000000000000000000000008152336004820152602401610609565b6020830151611433576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001820154602084015181146114b457602084015161145490600790612abe565b1561148b576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b602084015160018401556114a0600782612b72565b5060208401516114b290600790612e56565b505b606084015180516000036114f657806040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016106099190614c94565b8354600090859060049061151790640100000000900463ffffffff166148ea565b91906101000a81548163ffffffff021916908363ffffffff1602179055905060005b82518110156115fc5761156f838281518110611557576115576147e4565b60200260200101516003612abe90919063ffffffff16565b6115a757826040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016106099190614c94565b6115f38382815181106115bc576115bc6147e4565b60200260200101518760030160008563ffffffff1663ffffffff168152602001908152602001600020612e5690919063ffffffff16565b50600101611539565b50845468010000000000000000900463ffffffff16801561175d5763ffffffff8082166000908152600d60209081526040808320805464010000000090049094168352600190930181528282206002018054845181840281018401909552808552929392909183018282801561169157602002820191906000526020600020905b81548152602001906001019080831161167d575b5050505050905060005b815181101561175a576116f08282815181106116b9576116b96147e4565b60200260200101518960030160008763ffffffff1663ffffffff168152602001908152602001600020612abe90919063ffffffff16565b61175257818181518110611706576117066147e4565b6020026020010151836040517f03dcd86200000000000000000000000000000000000000000000000000000000815260040161060992919091825263ffffffff16602082015260400190565b60010161169b565b50505b600061176b87600401612e49565b905060005b81518163ffffffff1610156118b1576000828263ffffffff1681518110611799576117996147e4565b60209081029190910181015163ffffffff8082166000908152600d8452604080822080546401000000009004909316825260019092018452818120600201805483518187028101870190945280845293955090939192909183018282801561182057602002820191906000526020600020905b81548152602001906001019080831161180c575b5050505050905060005b815181101561189d5761187f828281518110611848576118486147e4565b60200260200101518c60030160008a63ffffffff1663ffffffff168152602001908152602001600020612abe90919063ffffffff16565b61189557818181518110611706576117066147e4565b60010161182a565b505050806118aa906148ea565b9050611770565b50875187547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff90911690811788556040808a015160028a018190556020808c01518351928352908201527f4b5b465e22eea0c3d40c30e936643245b80d19b2dcf75788c0699fe8d8db645b910160405180910390a25050505050505050806001019050611235565b600e5460609063ffffffff166000611958600183614842565b63ffffffff1667ffffffffffffffff81111561197657611976613d25565b6040519080825280602002602001820160405280156119bc57816020015b6040805180820190915260008152606060208201528152602001906001900390816119945790505b509050600060015b8363ffffffff168163ffffffff161015611b0c5763ffffffff81166000908152600b602052604090205473ffffffffffffffffffffffffffffffffffffffff1615611b045763ffffffff81166000908152600b60209081526040918290208251808401909352805473ffffffffffffffffffffffffffffffffffffffff1683526001810180549192840191611a5890614897565b80601f0160208091040260200160405190810160405280929190818152602001828054611a8490614897565b8015611ad15780601f10611aa657610100808354040283529160200191611ad1565b820191906000526020600020905b815481529060010190602001808311611ab457829003601f168201915b505050505081525050838381518110611aec57611aec6147e4565b602002602001018190525081611b019061485f565b91505b6001016119c4565b50600e546107e29060019063ffffffff16614842565b611b2a612ad9565b60005b81811015610697576000838383818110611b4957611b496147e4565b9050602002810190611b5b9190614cd8565b611b6490614d1b565b90506000611b7a82600001518360200151610446565b9050611b87600382612e56565b611bc0576040517febf5255100000000000000000000000000000000000000000000000000000000815260048101829052602401610609565b611bca8183612e62565b5050600101611b2d565b6000805473ffffffffffffffffffffffffffffffffffffffff163314905b82811015610c97576000848483818110611c0e57611c0e6147e4565b9050602002810190611c209190614b8d565b611c2990614bc1565b805163ffffffff166000908152600b602090815260408083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff168252600181018054959650939491939092840191611c7f90614897565b80601f0160208091040260200160405190810160405280929190818152602001828054611cab90614897565b8015611cf85780601f10611ccd57610100808354040283529160200191611cf8565b820191906000526020600020905b815481529060010190602001808311611cdb57829003601f168201915b50505091909252505081519192505073ffffffffffffffffffffffffffffffffffffffff16611d5e5781516040517fadd9ae1e00000000000000000000000000000000000000000000000000000000815263ffffffff9091166004820152602401610609565b83158015611d835750805173ffffffffffffffffffffffffffffffffffffffff163314155b15611dbc576040517f9473075d000000000000000000000000000000000000000000000000000000008152336004820152602401610609565b6040808301516000908152600c60205220600181015415611e115782604001516040517f5461848300000000000000000000000000000000000000000000000000000000815260040161060991815260200190565b6040830151611e545782604001516040517f64e2ee9200000000000000000000000000000000000000000000000000000000815260040161060991815260200190565b60208301511580611e7157506020830151611e7190600790612abe565b15611ea8576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60608301518051600003611eea57806040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016106099190614c94565b81548290600490611f0890640100000000900463ffffffff166148ea565b82546101009290920a63ffffffff818102199093169183160217909155825464010000000090041660005b8251811015611fde57611f51838281518110611557576115576147e4565b611f8957826040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016106099190614c94565b611fd5838281518110611f9e57611f9e6147e4565b60200260200101518560030160008563ffffffff1663ffffffff168152602001908152602001600020612e5690919063ffffffff16565b50600101611f33565b50845183547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff918216178455604086015160028501556020860151600185018190556120349160079190612e5616565b50604085015161204690600990612e56565b50845160408087015160208089015183519283529082015263ffffffff909216917f74becb12a5e8fd0e98077d02dfba8f647c9670c9df177e42c2418cf17a636f05910160405180910390a25050505050806001019050611bf2565b60015473ffffffffffffffffffffffffffffffffffffffff163314612123576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610609565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b8281146121e2576040517fab8b67c60000000000000000000000000000000000000000000000000000000081526004810184905260248101829052604401610609565b6000805473ffffffffffffffffffffffffffffffffffffffff16905b848110156124d757600086868381811061221a5761221a6147e4565b905060200201602081019061222f91906141eb565b63ffffffff81166000908152600b6020526040902080549192509073ffffffffffffffffffffffffffffffffffffffff1661229e576040517fadd9ae1e00000000000000000000000000000000000000000000000000000000815263ffffffff83166004820152602401610609565b60008686858181106122b2576122b26147e4565b90506020028101906122c4919061490d565b6122cd9061494b565b805190915073ffffffffffffffffffffffffffffffffffffffff1661231e576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815473ffffffffffffffffffffffffffffffffffffffff16331480159061235b57503373ffffffffffffffffffffffffffffffffffffffff861614155b15612394576040517f9473075d000000000000000000000000000000000000000000000000000000008152336004820152602401610609565b8051825473ffffffffffffffffffffffffffffffffffffffff908116911614158061241057506020808201516040516123cd9201613f76565b60405160208183030381529060405280519060200120826001016040516020016123f79190614dc1565b6040516020818303038152906040528051906020012014155b156124c957805182547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9091161782556020810151600183019061246a9082614a05565b50806000015173ffffffffffffffffffffffffffffffffffffffff168363ffffffff167f86f41145bde5dd7f523305452e4aad3685508c181432ec733d5f345009358a2883602001516040516124c09190613f76565b60405180910390a35b5050508060010190506121fe565b505050505050565b6125206040805160e0810182526000808252606060208301819052928201839052909182019081526020016000815260006020820181905260409091015290565b6040805160e0810182528381526000848152600260209081529290208054919283019161254c90614897565b80601f016020809104026020016040519081016040528092919081815260200182805461257890614897565b80156125c55780601f1061259a576101008083540402835291602001916125c5565b820191906000526020600020905b8154815290600101906020018083116125a857829003601f168201915b505050505081526020016002600085815260200190815260200160002060010180546125f090614897565b80601f016020809104026020016040519081016040528092919081815260200182805461261c90614897565b80156126695780601f1061263e57610100808354040283529160200191612669565b820191906000526020600020905b81548152906001019060200180831161264c57829003601f168201915b50505091835250506000848152600260208181526040909220015491019060ff16600381111561269b5761269b61444c565b815260008481526002602081815260409092200154910190610100900460ff1660018111156126cc576126cc61444c565b81526000848152600260208181526040928390209091015462010000900473ffffffffffffffffffffffffffffffffffffffff169083015201612710600585612abe565b1515905292915050565b612722612ad9565b63ffffffff8089166000908152600d6020526040812054640100000000900490911690819003612786576040517f2b62be9b00000000000000000000000000000000000000000000000000000000815263ffffffff8a166004820152602401610609565b6127d8888888886040518060a001604052808f63ffffffff168152602001876127ae906148ea565b97508763ffffffff1681526020018a1515815260200189151581526020018860ff168152506130f6565b505050505050505050565b6127eb612ad9565b600e805460009164010000000090910463ffffffff1690600461280d836148ea565b82546101009290920a63ffffffff81810219909316918316021790915581166000818152600d602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001684179055815160a08101835292835260019083015286151590820152841515606082015260ff841660808201529091506128a39089908990899089906130f6565b5050505050505050565b606060006128bb6003612e49565b90506000815167ffffffffffffffff8111156128d9576128d9613d25565b60405190808252806020026020018201604052801561294b57816020015b6129386040805160e0810182526000808252606060208301819052928201839052909182019081526020016000815260006020820181905260409091015290565b8152602001906001900390816128f75790505b50905060005b82518110156107f25761297c83828151811061296f5761296f6147e4565b60200260200101516124df565b82828151811061298e5761298e6147e4565b6020908102919091010152600101612951565b606060006129af6009612e49565b90506000815167ffffffffffffffff8111156129cd576129cd613d25565b604051908082528060200260200182016040528015612a5457816020015b6040805160e081018252600080825260208083018290529282018190526060808301829052608083019190915260a0820181905260c082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816129eb5790505b50905060005b82518110156107f257612a85838281518110612a7857612a786147e4565b6020026020010151611048565b828281518110612a9757612a976147e4565b6020908102919091010152600101612a5a565b612ab2612ad9565b612abb8161391a565b50565b600081815260018301602052604081205415155b9392505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314612b5a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610609565b565b6000610474825490565b6000612ad28383613a0f565b6000612ad28383613a39565b6040805160e0810182526000808252602080830182905282840182905260608084018390526080840183905260a0840181905260c084015263ffffffff8581168352600d8252848320805464010000000090049091168084526001909101825284832060028101805487518186028101860190985280885295969295919493909190830182828015612c2f57602002820191906000526020600020905b815481526020019060010190808311612c1b575b505050505090506000815167ffffffffffffffff811115612c5257612c52613d25565b604051908082528060200260200182016040528015612c9857816020015b604080518082019091526000815260606020820152815260200190600190039081612c705790505b50905060005b8151811015612db0576040518060400160405280848381518110612cc457612cc46147e4565b60200260200101518152602001856003016000868581518110612ce957612ce96147e4565b602002602001015181526020019081526020016000208054612d0a90614897565b80601f0160208091040260200160405190810160405280929190818152602001828054612d3690614897565b8015612d835780601f10612d5857610100808354040283529160200191612d83565b820191906000526020600020905b815481529060010190602001808311612d6657829003601f168201915b5050505050815250828281518110612d9d57612d9d6147e4565b6020908102919091010152600101612c9e565b506040805160e08101825263ffffffff8089166000818152600d6020818152868320548086168752948b168187015260ff680100000000000000008604811697870197909752690100000000000000000085048716151560608701529290915290526a010000000000000000000090049091161515608082015260a08101612e3785612e49565b81526020019190915295945050505050565b60606000612ad283613b2c565b6000612ad28383613b88565b608081015173ffffffffffffffffffffffffffffffffffffffff1615612fb057608081015173ffffffffffffffffffffffffffffffffffffffff163b1580612f5b575060808101516040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527f78bea72100000000000000000000000000000000000000000000000000000000600482015273ffffffffffffffffffffffffffffffffffffffff909116906301ffc9a790602401602060405180830381865afa158015612f35573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f599190614e6f565b155b15612fb05760808101516040517fabb5e3fd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401610609565b600082815260026020526040902081518291908190612fcf9082614a05565b5060208201516001820190612fe49082614a05565b5060408201516002820180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660018360038111156130265761302661444c565b021790555060608201516002820180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010083600181111561306d5761306d61444c565b0217905550608091909101516002909101805473ffffffffffffffffffffffffffffffffffffffff90921662010000027fffffffffffffffffffff0000000000000000000000000000000000000000ffff90921691909117905560405182907f04f0a9bcf3f3a3b42a4d7ca081119755f82ebe43e0d30c8f7292c4fe0dc4a2ae90600090a25050565b805163ffffffff9081166000908152600d602090815260408083208286015190941683526001909301905220608082015160ff161580613148575060808201518590613143906001614e8c565b60ff16115b156131915760808201516040517f25b4d61800000000000000000000000000000000000000000000000000000000815260ff909116600482015260248101869052604401610609565b6001826020015163ffffffff16111561327957815163ffffffff166000908152600d6020908152604082209084015160019182019183916131d29190614842565b63ffffffff1663ffffffff168152602001908152602001600020905060005b6131fa82612b5c565b81101561327657613229846000015163ffffffff16600c60006105928587600001612b6690919063ffffffff16565b50600c60006132388484612b66565b8152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffff1690556001016131f1565b50505b60005b858110156134b3576132a9878783818110613299576132996147e4565b8592602090910201359050612e56565b61330a5782518787838181106132c1576132c16147e4565b6040517f636e405700000000000000000000000000000000000000000000000000000000815263ffffffff90941660048501526020029190910135602483015250604401610609565b82606001511561346157825163ffffffff16600c6000898985818110613332576133326147e4565b602090810292909201358352508101919091526040016000205468010000000000000000900463ffffffff16148015906133ac5750600c600088888481811061337d5761337d6147e4565b602090810292909201358352508101919091526040016000205468010000000000000000900463ffffffff1615155b1561340e5782518787838181106133c5576133c56147e4565b6040517f60b9df7300000000000000000000000000000000000000000000000000000000815263ffffffff90941660048501526020029190910135602483015250604401610609565b8251600c6000898985818110613426576134266147e4565b90506020020135815260200190815260200160002060000160086101000a81548163ffffffff021916908363ffffffff1602179055506134ab565b82516134a99063ffffffff16600c60008a8a86818110613483576134836147e4565b905060200201358152602001908152602001600020600401612e5690919063ffffffff16565b505b60010161327c565b5060005b8381101561378f57368585838181106134d2576134d26147e4565b90506020028101906134e4919061490d565b90506134f260038235612abe565b61352b576040517fe181733f00000000000000000000000000000000000000000000000000000000815281356004820152602401610609565b61353760058235612abe565b15613571576040517ff7d7a29400000000000000000000000000000000000000000000000000000000815281356004820152602401610609565b803560009081526003840160205260408120805461358e90614897565b905011156135da5783516040517f3927d08000000000000000000000000000000000000000000000000000000000815263ffffffff909116600482015281356024820152604401610609565b60005b878110156136e4576136818235600c60008c8c86818110613600576136006147e4565b9050602002013581526020019081526020016000206003016000600c60008e8e88818110613630576136306147e4565b90506020020135815260200190815260200160002060000160049054906101000a900463ffffffff1663ffffffff1663ffffffff168152602001908152602001600020612abe90919063ffffffff16565b6136dc57888882818110613697576136976147e4565b6040517fa7e792500000000000000000000000000000000000000000000000000000000081526020909102929092013560048301525082356024820152604401610609565b6001016135dd565b506002830180546001810182556000918252602091829020833591015561370d90820182614ea5565b8235600090815260038601602052604090209161372b919083614f0a565b50835160208086015161378692918435908c908c9061374c90880188614ea5565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613bd792505050565b506001016134b7565b50604080830151835163ffffffff9081166000908152600d602090815284822080549415156901000000000000000000027fffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffff90951694909417909355606086015186518316825284822080549115156a0100000000000000000000027fffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffff9092169190911790556080860151865183168252848220805460ff9290921668010000000000000000027fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff909216919091179055918501805186518316845292849020805493909216640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff9093169290921790558351905191517ff264aae70bf6a9d90e68e0f9b393f4e7fbea67b063b0f336e0b36c15817036519261390a929163ffffffff92831681529116602082015260400190565b60405180910390a1505050505050565b3373ffffffffffffffffffffffffffffffffffffffff821603613999576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610609565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000826000018281548110613a2657613a266147e4565b9060005260206000200154905092915050565b60008181526001830160205260408120548015613b22576000613a5d600183615025565b8554909150600090613a7190600190615025565b9050818114613ad6576000866000018281548110613a9157613a916147e4565b9060005260206000200154905080876000018481548110613ab457613ab46147e4565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080613ae757613ae7615038565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610474565b6000915050610474565b606081600001805480602002602001604051908101604052809291908181526020018280548015613b7c57602002820191906000526020600020905b815481526020019060010190808311613b68575b50505050509050919050565b6000818152600183016020526040812054613bcf57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610474565b506000610474565b6000848152600260208190526040909120015462010000900473ffffffffffffffffffffffffffffffffffffffff16156124d757600084815260026020819052604091829020015490517ffba64a7c0000000000000000000000000000000000000000000000000000000081526201000090910473ffffffffffffffffffffffffffffffffffffffff169063fba64a7c90613c7e908690869086908b908d90600401615067565b600060405180830381600087803b158015613c9857600080fd5b505af1158015613cac573d6000803e3d6000fd5b50505050505050505050565b508054613cc490614897565b6000825580601f10613cd4575050565b601f016020900490600052602060002090810190612abb9190613d0c565b5080546000825590600052602060002090810190612abb91905b5b80821115613d215760008155600101613d0d565b5090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516080810167ffffffffffffffff81118282101715613d7757613d77613d25565b60405290565b60405160a0810167ffffffffffffffff81118282101715613d7757613d77613d25565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613de757613de7613d25565b604052919050565b600067ffffffffffffffff821115613e0957613e09613d25565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600082601f830112613e4657600080fd5b8135613e59613e5482613def565b613da0565b818152846020838601011115613e6e57600080fd5b816020850160208301376000918101602001919091529392505050565b60008060408385031215613e9e57600080fd5b823567ffffffffffffffff80821115613eb657600080fd5b613ec286838701613e35565b93506020850135915080821115613ed857600080fd5b50613ee585828601613e35565b9150509250929050565b600060208284031215613f0157600080fd5b5035919050565b60005b83811015613f23578181015183820152602001613f0b565b50506000910152565b60008151808452613f44816020860160208601613f08565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000612ad26020830184613f2c565b60008083601f840112613f9b57600080fd5b50813567ffffffffffffffff811115613fb357600080fd5b6020830191508360208260051b850101111561104157600080fd5b60008060208385031215613fe157600080fd5b823567ffffffffffffffff811115613ff857600080fd5b61400485828601613f89565b90969095509350505050565b60008151808452602080850194506020840160005b8381101561404157815187529582019590820190600101614025565b509495945050505050565b600082825180855260208086019550808260051b84010181860160005b848110156140c9578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001895281518051845284015160408585018190526140b581860183613f2c565b9a86019a9450505090830190600101614069565b5090979650505050505050565b600063ffffffff8083511684528060208401511660208501525060ff604083015116604084015260608201511515606084015260808201511515608084015260a082015160e060a085015261412e60e0850182614010565b905060c083015184820360c0860152614147828261404c565b95945050505050565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b828110156141c5577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526141b38583516140d6565b94509285019290850190600101614179565b5092979650505050505050565b803563ffffffff811681146141e657600080fd5b919050565b6000602082840312156141fd57600080fd5b612ad2826141d2565b73ffffffffffffffffffffffffffffffffffffffff8151168252600060208201516040602085015261423b6040850182613f2c565b949350505050565b602081526000612ad26020830184614206565b602081526000612ad260208301846140d6565b6000806040838503121561427c57600080fd5b614285836141d2565b946020939093013593505050565b6040815260006142a66040830185613f2c565b82810360208401526141478185613f2c565b600063ffffffff808351168452602081818501511681860152816040850151166040860152606084015160608601526080840151608086015260a0840151915060e060a086015261430c60e0860183614010565b60c08581015187830391880191909152805180835290830193506000918301905b8083101561434d578451825293830193600192909201919083019061432d565b509695505050505050565b602081526000612ad260208301846142b8565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b828110156141c5577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526143ce858351614206565b94509285019290850190600101614394565b600080600080604085870312156143f657600080fd5b843567ffffffffffffffff8082111561440e57600080fd5b61441a88838901613f89565b9096509450602087013591508082111561443357600080fd5b5061444087828801613f89565b95989497509550505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b805182526000602082015160e0602085015261449a60e0850182613f2c565b9050604083015184820360408601526144b38282613f2c565b9150506060830151600481106144cb576144cb61444c565b60608501526080830151600281106144e5576144e561444c565b8060808601525060a083015161451360a086018273ffffffffffffffffffffffffffffffffffffffff169052565b5060c083015161452760c086018215159052565b509392505050565b602081526000612ad2602083018461447b565b8015158114612abb57600080fd5b803560ff811681146141e657600080fd5b60008060008060008060008060c0898b03121561457d57600080fd5b614586896141d2565b9750602089013567ffffffffffffffff808211156145a357600080fd5b6145af8c838d01613f89565b909950975060408b01359150808211156145c857600080fd5b506145d58b828c01613f89565b90965094505060608901356145e981614542565b925060808901356145f981614542565b915061460760a08a01614550565b90509295985092959890939650565b600080600080600080600060a0888a03121561463157600080fd5b873567ffffffffffffffff8082111561464957600080fd5b6146558b838c01613f89565b909950975060208a013591508082111561466e57600080fd5b5061467b8a828b01613f89565b909650945050604088013561468f81614542565b9250606088013561469f81614542565b91506146ad60808901614550565b905092959891949750929550565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b828110156141c5577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845261471e85835161447b565b945092850192908501906001016146e4565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b828110156141c5577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526147938583516142b8565b94509285019290850190600101614759565b803573ffffffffffffffffffffffffffffffffffffffff811681146141e657600080fd5b6000602082840312156147db57600080fd5b612ad2826147a5565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b63ffffffff8281168282160390808211156107f2576107f2614813565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361489057614890614813565b5060010190565b600181811c908216806148ab57607f821691505b6020821081036148e4577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600063ffffffff80831681810361490357614903614813565b6001019392505050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc183360301811261494157600080fd5b9190910192915050565b60006040823603121561495d57600080fd5b6040516040810167ffffffffffffffff828210818311171561498157614981613d25565b8160405261498e856147a5565b835260208501359150808211156149a457600080fd5b506149b136828601613e35565b60208301525092915050565b601f821115610697576000816000526020600020601f850160051c810160208610156149e65750805b601f850160051c820191505b818110156124d7578281556001016149f2565b815167ffffffffffffffff811115614a1f57614a1f613d25565b614a3381614a2d8454614897565b846149bd565b602080601f831160018114614a865760008415614a505750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556124d7565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015614ad357888601518255948401946001909101908401614ab4565b5085821015614b0f57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b600060208284031215614b3157600080fd5b815167ffffffffffffffff811115614b4857600080fd5b8201601f81018413614b5957600080fd5b8051614b67613e5482613def565b818152856020838501011115614b7c57600080fd5b614147826020830160208601613f08565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8183360301811261494157600080fd5b600060808236031215614bd357600080fd5b614bdb613d54565b614be4836141d2565b81526020808401358183015260408401356040830152606084013567ffffffffffffffff80821115614c1557600080fd5b9085019036601f830112614c2857600080fd5b813581811115614c3a57614c3a613d25565b8060051b9150614c4b848301613da0565b8181529183018401918481019036841115614c6557600080fd5b938501935b83851015614c8357843582529385019390850190614c6a565b606087015250939695505050505050565b6020808252825182820181905260009190848201906040850190845b81811015614ccc57835183529284019291840191600101614cb0565b50909695505050505050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6183360301811261494157600080fd5b8035600281106141e657600080fd5b600060a08236031215614d2d57600080fd5b614d35613d7d565b823567ffffffffffffffff80821115614d4d57600080fd5b614d5936838701613e35565b83526020850135915080821115614d6f57600080fd5b50614d7c36828601613e35565b602083015250604083013560048110614d9457600080fd5b6040820152614da560608401614d0c565b6060820152614db6608084016147a5565b608082015292915050565b6000602080835260008454614dd581614897565b8060208701526040600180841660008114614df75760018114614e3157614e61565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00851660408a0152604084151560051b8a01019550614e61565b89600052602060002060005b85811015614e585781548b8201860152908301908801614e3d565b8a016040019650505b509398975050505050505050565b600060208284031215614e8157600080fd5b8151612ad281614542565b60ff818116838216019081111561047457610474614813565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112614eda57600080fd5b83018035915067ffffffffffffffff821115614ef557600080fd5b60200191503681900382131561104157600080fd5b67ffffffffffffffff831115614f2257614f22613d25565b614f3683614f308354614897565b836149bd565b6000601f841160018114614f885760008515614f525750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b17835561501e565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b82811015614fd75786850135825560209485019460019092019101614fb7565b5086821015615012577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b8181038181111561047457610474614813565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b6080815284608082015260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8611156150a057600080fd5b8560051b808860a0850137820182810360a090810160208501526150c690820187613f2c565b91505063ffffffff8085166040840152808416606084015250969550505050505056fea164736f6c6343000818000a", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"AccessForbidden\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityIsDeprecated\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"CapabilityRequiredByDON\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"DONDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONCapability\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"DuplicateDONNode\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"proposedConfigurationContract\",\"type\":\"address\"}],\"name\":\"InvalidCapabilityConfigurationContractInterface\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"nodeCount\",\"type\":\"uint256\"}],\"name\":\"InvalidFaultTolerance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"name\":\"InvalidNodeCapabilities\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeOperatorAdmin\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"InvalidNodeP2PId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidNodeSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"lengthOne\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lengthTwo\",\"type\":\"uint256\"}],\"name\":\"LengthMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"NodeAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"NodeDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"NodeDoesNotSupportCapability\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"}],\"name\":\"NodeOperatorDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"NodePartOfCapabilitiesDON\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"nodeP2PId\",\"type\":\"bytes32\"}],\"name\":\"NodePartOfWorkflowDON\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityConfigured\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"CapabilityDeprecated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"}],\"name\":\"NodeAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"}],\"name\":\"NodeOperatorRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"NodeOperatorUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"NodeRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"}],\"name\":\"NodeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"labelledName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"version\",\"type\":\"string\"},{\"internalType\":\"enumCapabilitiesRegistry.CapabilityType\",\"name\":\"capabilityType\",\"type\":\"uint8\"},{\"internalType\":\"enumCapabilitiesRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"}],\"internalType\":\"structCapabilitiesRegistry.Capability[]\",\"name\":\"capabilities\",\"type\":\"tuple[]\"}],\"name\":\"addCapabilities\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"nodes\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilitiesRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"acceptsWorkflows\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"}],\"name\":\"addDON\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilitiesRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"addNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilitiesRegistry.NodeParams[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"addNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"name\":\"deprecateCapabilities\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCapabilities\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"hashedId\",\"type\":\"bytes32\"},{\"internalType\":\"string\",\"name\":\"labelledName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"version\",\"type\":\"string\"},{\"internalType\":\"enumCapabilitiesRegistry.CapabilityType\",\"name\":\"capabilityType\",\"type\":\"uint8\"},{\"internalType\":\"enumCapabilitiesRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"isDeprecated\",\"type\":\"bool\"}],\"internalType\":\"structCapabilitiesRegistry.CapabilityInfo[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedId\",\"type\":\"bytes32\"}],\"name\":\"getCapability\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"hashedId\",\"type\":\"bytes32\"},{\"internalType\":\"string\",\"name\":\"labelledName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"version\",\"type\":\"string\"},{\"internalType\":\"enumCapabilitiesRegistry.CapabilityType\",\"name\":\"capabilityType\",\"type\":\"uint8\"},{\"internalType\":\"enumCapabilitiesRegistry.CapabilityResponseType\",\"name\":\"responseType\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"configurationContract\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"isDeprecated\",\"type\":\"bool\"}],\"internalType\":\"structCapabilitiesRegistry.CapabilityInfo\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"}],\"name\":\"getCapabilityConfigs\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"}],\"name\":\"getDON\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"id\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"acceptsWorkflows\",\"type\":\"bool\"},{\"internalType\":\"bytes32[]\",\"name\":\"nodeP2PIds\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilitiesRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"}],\"internalType\":\"structCapabilitiesRegistry.DONInfo\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDONs\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"id\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"acceptsWorkflows\",\"type\":\"bool\"},{\"internalType\":\"bytes32[]\",\"name\":\"nodeP2PIds\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilitiesRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"}],\"internalType\":\"structCapabilitiesRegistry.DONInfo[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"labelledName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"version\",\"type\":\"string\"}],\"name\":\"getHashedCapabilityId\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"}],\"name\":\"getNode\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"workflowDONId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256[]\",\"name\":\"capabilitiesDONIds\",\"type\":\"uint256[]\"}],\"internalType\":\"structCapabilitiesRegistry.NodeInfo\",\"name\":\"nodeInfo\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"}],\"name\":\"getNodeOperator\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilitiesRegistry.NodeOperator\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getNodeOperators\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilitiesRegistry.NodeOperator[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getNodes\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"workflowDONId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint256[]\",\"name\":\"capabilitiesDONIds\",\"type\":\"uint256[]\"}],\"internalType\":\"structCapabilitiesRegistry.NodeInfo[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hashedCapabilityId\",\"type\":\"bytes32\"}],\"name\":\"isCapabilityDeprecated\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32[]\",\"name\":\"donIds\",\"type\":\"uint32[]\"}],\"name\":\"removeDONs\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint32[]\"}],\"name\":\"removeNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"removedNodeP2PIds\",\"type\":\"bytes32[]\"}],\"name\":\"removeNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32[]\",\"name\":\"nodes\",\"type\":\"bytes32[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"capabilityId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"config\",\"type\":\"bytes\"}],\"internalType\":\"structCapabilitiesRegistry.CapabilityConfiguration[]\",\"name\":\"capabilityConfigurations\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"isPublic\",\"type\":\"bool\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"}],\"name\":\"updateDON\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32[]\",\"name\":\"nodeOperatorIds\",\"type\":\"uint32[]\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"internalType\":\"structCapabilitiesRegistry.NodeOperator[]\",\"name\":\"nodeOperators\",\"type\":\"tuple[]\"}],\"name\":\"updateNodeOperators\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"nodeOperatorId\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"signer\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"p2pId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"hashedCapabilityIds\",\"type\":\"bytes32[]\"}],\"internalType\":\"structCapabilitiesRegistry.NodeParams[]\",\"name\":\"nodes\",\"type\":\"tuple[]\"}],\"name\":\"updateNodes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x6080604052600e80546001600160401b0319166401000000011790553480156200002857600080fd5b503380600081620000805760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000b357620000b381620000bc565b50505062000167565b336001600160a01b03821603620001165760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000077565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b61516480620001776000396000f3fe608060405234801561001057600080fd5b50600436106101ae5760003560e01c80635d83d967116100ee57806386fa424611610097578063d8bc7b6811610071578063d8bc7b68146103f6578063ddbe4f8214610409578063e29581aa1461041e578063f2fde38b1461043357600080fd5b806386fa42461461039b5780638da5cb5b146103ae5780639cb7c5f4146103d657600080fd5b8063715f5295116100c8578063715f52951461036d57806373ac22b41461038057806379ba50971461039357600080fd5b80635d83d967146103325780635e65e3091461034557806366acaa331461035857600080fd5b8063235374051161015b5780632c01a1e8116101355780632c01a1e8146102cb578063398f3773146102de5780633f2a13c9146102f157806350c946fe1461031257600080fd5b80632353740514610285578063275459f2146102a55780632a852933146102b857600080fd5b80631d05394c1161018c5780631d05394c1461023b578063214502431461025057806322bdbcbc1461026557600080fd5b80630fe5800a146101b357806312570011146101d9578063181f5a77146101fc575b600080fd5b6101c66101c1366004613f46565b610446565b6040519081526020015b60405180910390f35b6101ec6101e7366004613faa565b61047a565b60405190151581526020016101d0565b604080518082018252601a81527f4361706162696c6974696573526567697374727920312e302e30000000000000602082015290516101d09190614031565b61024e610249366004614089565b610487565b005b61025861069c565b6040516101d0919061420b565b6102786102733660046142a6565b6107f9565b6040516101d091906142fe565b6102986102933660046142a6565b6108e6565b6040516101d09190614311565b61024e6102b3366004614089565b61092a565b61024e6102c6366004614345565b610a01565b61024e6102d9366004614089565b610ae1565b61024e6102ec366004614089565b610d7d565b6103046102ff3660046143e7565b610f3c565b6040516101d0929190614411565b610325610320366004613faa565b611128565b6040516101d091906144d6565b61024e610340366004614089565b611202565b61024e610353366004614089565b6112f7565b610360611a1f565b6040516101d091906144e9565b61024e61037b366004614089565b611c02565b61024e61038e366004614089565b611cb4565b61024e612182565b61024e6103a936600461455e565b61227f565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101d0565b6103e96103e4366004613faa565b6125bf565b6040516101d091906146ad565b61024e6104043660046146c0565b6127fa565b6104116128c4565b6040516101d09190614746565b6104266129b8565b6040516101d091906147bb565b61024e610441366004614854565b612ac1565b6000828260405160200161045b929190614411565b6040516020818303038152906040528051906020012090505b92915050565b6000610474600583612ad5565b61048f612af0565b60005b818110156106975760008383838181106104ae576104ae61486f565b90506020020160208101906104c391906142a6565b63ffffffff8181166000908152600d60209081526040808320805464010000000081049095168085526001820190935290832094955093909290916a010000000000000000000090910460ff16905b61051b83612b73565b8110156105bb57811561057157600c60006105368584612b7d565b8152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffff1690556105b3565b6105b18663ffffffff16600c60006105928588612b7d90919063ffffffff16565b8152602001908152602001600020600401612b8990919063ffffffff16565b505b600101610512565b508354640100000000900463ffffffff16600003610612576040517f2b62be9b00000000000000000000000000000000000000000000000000000000815263ffffffff861660048201526024015b60405180910390fd5b63ffffffff85166000818152600d6020908152604080832080547fffffffffffffffffffffffffffffffffffffffffff00000000000000000000001690558051938452908301919091527ff264aae70bf6a9d90e68e0f9b393f4e7fbea67b063b0f336e0b36c1581703651910160405180910390a15050505050806001019050610492565b505050565b600e54606090640100000000900463ffffffff1660006106bd6001836148cd565b63ffffffff1667ffffffffffffffff8111156106db576106db613de0565b60405190808252806020026020018201604052801561076257816020015b6040805160e081018252600080825260208083018290529282018190526060808301829052608083019190915260a0820181905260c082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816106f95790505b509050600060015b8363ffffffff168163ffffffff1610156107d65763ffffffff8082166000908152600d602052604090205416156107ce576107a481612b95565b8383815181106107b6576107b661486f565b6020026020010181905250816107cb906148ea565b91505b60010161076a565b506107e26001846148cd565b63ffffffff1681146107f2578082525b5092915050565b60408051808201909152600081526060602082015263ffffffff82166000908152600b60209081526040918290208251808401909352805473ffffffffffffffffffffffffffffffffffffffff168352600181018054919284019161085d90614922565b80601f016020809104026020016040519081016040528092919081815260200182805461088990614922565b80156108d65780601f106108ab576101008083540402835291602001916108d6565b820191906000526020600020905b8154815290600101906020018083116108b957829003601f168201915b5050505050815250509050919050565b6040805160e0810182526000808252602082018190529181018290526060808201839052608082019290925260a0810182905260c081019190915261047482612b95565b610932612af0565b60005b63ffffffff811682111561069757600083838363ffffffff1681811061095d5761095d61486f565b905060200201602081019061097291906142a6565b63ffffffff81166000908152600b6020526040812080547fffffffffffffffffffffffff00000000000000000000000000000000000000001681559192506109bd6001830182613d73565b505060405163ffffffff8216907fa59268ca81d40429e65ccea5385b59cf2d3fc6519371dee92f8eb1dae5107a7a90600090a2506109fa81614975565b9050610935565b610a09612af0565b63ffffffff8088166000908152600d60205260408120805490926401000000009091041690819003610a6f576040517f2b62be9b00000000000000000000000000000000000000000000000000000000815263ffffffff8a166004820152602401610609565b610ad6888888886040518060a001604052808f63ffffffff16815260200187610a9790614975565b63ffffffff811682528b15156020830152895460ff6a01000000000000000000009091048116151560408401528b166060909201919091529650612e60565b505050505050505050565b6000805473ffffffffffffffffffffffffffffffffffffffff163314905b82811015610d77576000848483818110610b1b57610b1b61486f565b602090810292909201356000818152600c90935260409092206001810154929350919050610b78576040517fd82f6adb00000000000000000000000000000000000000000000000000000000815260048101839052602401610609565b6000610b8682600401612b73565b1115610bdb57610b996004820184612b7d565b6040517f60a6d89800000000000000000000000000000000000000000000000000000000815263ffffffff909116600482015260248101839052604401610609565b805468010000000000000000900463ffffffff1615610c435780546040517f60b9df730000000000000000000000000000000000000000000000000000000081526801000000000000000090910463ffffffff16600482015260248101839052604401610609565b83158015610c7d5750805463ffffffff166000908152600b602052604090205473ffffffffffffffffffffffffffffffffffffffff163314155b15610cb6576040517f9473075d000000000000000000000000000000000000000000000000000000008152336004820152602401610609565b6001810154610cc790600790612b89565b506002810154610cd990600990612b89565b506000828152600c6020526040812080547fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016815560018101829055600281018290559060048201818181610d2e8282613dad565b5050505050507f5254e609a97bab37b7cc79fe128f85c097bd6015c6e1624ae0ba392eb975320582604051610d6591815260200190565b60405180910390a15050600101610aff565b50505050565b610d85612af0565b60005b81811015610697576000838383818110610da457610da461486f565b9050602002810190610db69190614998565b610dbf906149d6565b805190915073ffffffffffffffffffffffffffffffffffffffff16610e10576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600e54604080518082018252835173ffffffffffffffffffffffffffffffffffffffff908116825260208086015181840190815263ffffffff9095166000818152600b909252939020825181547fffffffffffffffffffffffff00000000000000000000000000000000000000001692169190911781559251919290916001820190610e9c9082614a90565b5050600e8054909150600090610eb79063ffffffff16614975565b91906101000a81548163ffffffff021916908363ffffffff160217905550816000015173ffffffffffffffffffffffffffffffffffffffff168163ffffffff167f78e94ca80be2c30abc061b99e7eb8583b1254781734b1e3ce339abb57da2fe8e8460200151604051610f2a9190614031565b60405180910390a35050600101610d88565b63ffffffff8083166000908152600d60209081526040808320805464010000000090049094168084526001909401825280832085845260030190915281208054606093849390929091610f8e90614922565b80601f0160208091040260200160405190810160405280929190818152602001828054610fba90614922565b80156110075780601f10610fdc57610100808354040283529160200191611007565b820191906000526020600020905b815481529060010190602001808311610fea57829003601f168201915b5050506000888152600260208190526040909120015492935060609262010000900473ffffffffffffffffffffffffffffffffffffffff1615915061111a905057600086815260026020819052604091829020015490517f8318ed5d00000000000000000000000000000000000000000000000000000000815263ffffffff891660048201526201000090910473ffffffffffffffffffffffffffffffffffffffff1690638318ed5d90602401600060405180830381865afa1580156110d1573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526111179190810190614baa565b90505b9093509150505b9250929050565b6040805160e0810182526000808252602082018190529181018290526060808201839052608082019290925260a0810182905260c08101919091526040805160e0810182526000848152600c6020908152838220805463ffffffff8082168652640100000000820481168487018190526801000000000000000090920416858701526001820154606086015260028201546080860152835260030190529190912060a08201906111d790613685565b81526020016111fa600c6000868152602001908152602001600020600401613685565b905292915050565b61120a612af0565b60005b818110156106975760008383838181106112295761122961486f565b905060200201359050611246816003612ad590919063ffffffff16565b61127f576040517fe181733f00000000000000000000000000000000000000000000000000000000815260048101829052602401610609565b61128a600582613692565b6112c3576040517ff7d7a29400000000000000000000000000000000000000000000000000000000815260048101829052602401610609565b60405181907fdcea1b78b6ddc31592a94607d537543fcaafda6cc52d6d5cc7bbfca1422baf2190600090a25060010161120d565b6000805473ffffffffffffffffffffffffffffffffffffffff163314905b82811015610d775760008484838181106113315761133161486f565b90506020028101906113439190614c18565b61134c90614c4c565b6040808201516000908152600c6020908152828220805463ffffffff168352600b82528383208451808601909552805473ffffffffffffffffffffffffffffffffffffffff16855260018101805496975091959394939092840191906113b190614922565b80601f01602080910402602001604051908101604052809291908181526020018280546113dd90614922565b801561142a5780601f106113ff5761010080835404028352916020019161142a565b820191906000526020600020905b81548152906001019060200180831161140d57829003601f168201915b50505091909252505050600183015490915061147a5782604001516040517fd82f6adb00000000000000000000000000000000000000000000000000000000815260040161060991815260200190565b8415801561149f5750805173ffffffffffffffffffffffffffffffffffffffff163314155b156114d8576040517f9473075d000000000000000000000000000000000000000000000000000000008152336004820152602401610609565b6020830151611513576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018201546020840151811461159457602084015161153490600790612ad5565b1561156b576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208401516001840155611580600782612b89565b50602084015161159290600790613692565b505b606084015180516000036115d657806040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016106099190614d1f565b835460009085906004906115f790640100000000900463ffffffff16614975565b91906101000a81548163ffffffff021916908363ffffffff1602179055905060005b82518110156116dc5761164f8382815181106116375761163761486f565b60200260200101516003612ad590919063ffffffff16565b61168757826040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016106099190614d1f565b6116d383828151811061169c5761169c61486f565b60200260200101518760030160008563ffffffff1663ffffffff16815260200190815260200160002061369290919063ffffffff16565b50600101611619565b50845468010000000000000000900463ffffffff16801561183d5763ffffffff8082166000908152600d60209081526040808320805464010000000090049094168352600190930181528282206002018054845181840281018401909552808552929392909183018282801561177157602002820191906000526020600020905b81548152602001906001019080831161175d575b5050505050905060005b815181101561183a576117d08282815181106117995761179961486f565b60200260200101518960030160008763ffffffff1663ffffffff168152602001908152602001600020612ad590919063ffffffff16565b611832578181815181106117e6576117e661486f565b6020026020010151836040517f03dcd86200000000000000000000000000000000000000000000000000000000815260040161060992919091825263ffffffff16602082015260400190565b60010161177b565b50505b600061184b87600401613685565b905060005b81518163ffffffff161015611991576000828263ffffffff16815181106118795761187961486f565b60209081029190910181015163ffffffff8082166000908152600d8452604080822080546401000000009004909316825260019092018452818120600201805483518187028101870190945280845293955090939192909183018282801561190057602002820191906000526020600020905b8154815260200190600101908083116118ec575b5050505050905060005b815181101561197d5761195f8282815181106119285761192861486f565b60200260200101518c60030160008a63ffffffff1663ffffffff168152602001908152602001600020612ad590919063ffffffff16565b611975578181815181106117e6576117e661486f565b60010161190a565b5050508061198a90614975565b9050611850565b50875187547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff90911690811788556040808a015160028a018190556020808c01518351928352908201527f4b5b465e22eea0c3d40c30e936643245b80d19b2dcf75788c0699fe8d8db645b910160405180910390a25050505050505050806001019050611315565b600e5460609063ffffffff166000611a386001836148cd565b63ffffffff1667ffffffffffffffff811115611a5657611a56613de0565b604051908082528060200260200182016040528015611a9c57816020015b604080518082019091526000815260606020820152815260200190600190039081611a745790505b509050600060015b8363ffffffff168163ffffffff161015611bec5763ffffffff81166000908152600b602052604090205473ffffffffffffffffffffffffffffffffffffffff1615611be45763ffffffff81166000908152600b60209081526040918290208251808401909352805473ffffffffffffffffffffffffffffffffffffffff1683526001810180549192840191611b3890614922565b80601f0160208091040260200160405190810160405280929190818152602001828054611b6490614922565b8015611bb15780601f10611b8657610100808354040283529160200191611bb1565b820191906000526020600020905b815481529060010190602001808311611b9457829003601f168201915b505050505081525050838381518110611bcc57611bcc61486f565b602002602001018190525081611be1906148ea565b91505b600101611aa4565b50600e546107e29060019063ffffffff166148cd565b611c0a612af0565b60005b81811015610697576000838383818110611c2957611c2961486f565b9050602002810190611c3b9190614d63565b611c4490614da6565b90506000611c5a82600001518360200151610446565b9050611c67600382613692565b611ca0576040517febf5255100000000000000000000000000000000000000000000000000000000815260048101829052602401610609565b611caa818361369e565b5050600101611c0d565b6000805473ffffffffffffffffffffffffffffffffffffffff163314905b82811015610d77576000848483818110611cee57611cee61486f565b9050602002810190611d009190614c18565b611d0990614c4c565b805163ffffffff166000908152600b602090815260408083208151808301909252805473ffffffffffffffffffffffffffffffffffffffff168252600181018054959650939491939092840191611d5f90614922565b80601f0160208091040260200160405190810160405280929190818152602001828054611d8b90614922565b8015611dd85780601f10611dad57610100808354040283529160200191611dd8565b820191906000526020600020905b815481529060010190602001808311611dbb57829003601f168201915b50505091909252505081519192505073ffffffffffffffffffffffffffffffffffffffff16611e3e5781516040517fadd9ae1e00000000000000000000000000000000000000000000000000000000815263ffffffff9091166004820152602401610609565b83158015611e635750805173ffffffffffffffffffffffffffffffffffffffff163314155b15611e9c576040517f9473075d000000000000000000000000000000000000000000000000000000008152336004820152602401610609565b6040808301516000908152600c60205220600181015415611ef15782604001516040517f5461848300000000000000000000000000000000000000000000000000000000815260040161060991815260200190565b6040830151611f345782604001516040517f64e2ee9200000000000000000000000000000000000000000000000000000000815260040161060991815260200190565b60208301511580611f5157506020830151611f5190600790612ad5565b15611f88576040517f8377314600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60608301518051600003611fca57806040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016106099190614d1f565b81548290600490611fe890640100000000900463ffffffff16614975565b82546101009290920a63ffffffff818102199093169183160217909155825464010000000090041660005b82518110156120be576120318382815181106116375761163761486f565b61206957826040517f3748d4c60000000000000000000000000000000000000000000000000000000081526004016106099190614d1f565b6120b583828151811061207e5761207e61486f565b60200260200101518560030160008563ffffffff1663ffffffff16815260200190815260200160002061369290919063ffffffff16565b50600101612013565b50845183547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff91821617845560408601516002850155602086015160018501819055612114916007919061369216565b50604085015161212690600990613692565b50845160408087015160208089015183519283529082015263ffffffff909216917f74becb12a5e8fd0e98077d02dfba8f647c9670c9df177e42c2418cf17a636f05910160405180910390a25050505050806001019050611cd2565b60015473ffffffffffffffffffffffffffffffffffffffff163314612203576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610609565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b8281146122c2576040517fab8b67c60000000000000000000000000000000000000000000000000000000081526004810184905260248101829052604401610609565b6000805473ffffffffffffffffffffffffffffffffffffffff16905b848110156125b75760008686838181106122fa576122fa61486f565b905060200201602081019061230f91906142a6565b63ffffffff81166000908152600b6020526040902080549192509073ffffffffffffffffffffffffffffffffffffffff1661237e576040517fadd9ae1e00000000000000000000000000000000000000000000000000000000815263ffffffff83166004820152602401610609565b60008686858181106123925761239261486f565b90506020028101906123a49190614998565b6123ad906149d6565b805190915073ffffffffffffffffffffffffffffffffffffffff166123fe576040517feeacd93900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815473ffffffffffffffffffffffffffffffffffffffff16331480159061243b57503373ffffffffffffffffffffffffffffffffffffffff861614155b15612474576040517f9473075d000000000000000000000000000000000000000000000000000000008152336004820152602401610609565b8051825473ffffffffffffffffffffffffffffffffffffffff90811691161415806124f057506020808201516040516124ad9201614031565b60405160208183030381529060405280519060200120826001016040516020016124d79190614e4c565b6040516020818303038152906040528051906020012014155b156125a957805182547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9091161782556020810151600183019061254a9082614a90565b50806000015173ffffffffffffffffffffffffffffffffffffffff168363ffffffff167f86f41145bde5dd7f523305452e4aad3685508c181432ec733d5f345009358a2883602001516040516125a09190614031565b60405180910390a35b5050508060010190506122de565b505050505050565b6126006040805160e0810182526000808252606060208301819052928201839052909182019081526020016000815260006020820181905260409091015290565b6040805160e0810182528381526000848152600260209081529290208054919283019161262c90614922565b80601f016020809104026020016040519081016040528092919081815260200182805461265890614922565b80156126a55780601f1061267a576101008083540402835291602001916126a5565b820191906000526020600020905b81548152906001019060200180831161268857829003601f168201915b505050505081526020016002600085815260200190815260200160002060010180546126d090614922565b80601f01602080910402602001604051908101604052809291908181526020018280546126fc90614922565b80156127495780601f1061271e57610100808354040283529160200191612749565b820191906000526020600020905b81548152906001019060200180831161272c57829003601f168201915b50505091835250506000848152600260208181526040909220015491019060ff16600381111561277b5761277b6145ca565b815260008481526002602081815260409092200154910190610100900460ff1660018111156127ac576127ac6145ca565b81526000848152600260208181526040928390209091015462010000900473ffffffffffffffffffffffffffffffffffffffff1690830152016127f0600585612ad5565b1515905292915050565b612802612af0565b600e805460009164010000000090910463ffffffff1690600461282483614975565b82546101009290920a63ffffffff81810219909316918316021790915581166000818152600d602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001684179055815160a08101835292835260019083015286151590820152841515606082015260ff841660808201529091506128ba908990899089908990612e60565b5050505050505050565b606060006128d26003613685565b90506000815167ffffffffffffffff8111156128f0576128f0613de0565b60405190808252806020026020018201604052801561296257816020015b61294f6040805160e0810182526000808252606060208301819052928201839052909182019081526020016000815260006020820181905260409091015290565b81526020019060019003908161290e5790505b50905060005b82518110156107f2576129938382815181106129865761298661486f565b60200260200101516125bf565b8282815181106129a5576129a561486f565b6020908102919091010152600101612968565b606060006129c66009613685565b90506000815167ffffffffffffffff8111156129e4576129e4613de0565b604051908082528060200260200182016040528015612a6b57816020015b6040805160e081018252600080825260208083018290529282018190526060808301829052608083019190915260a0820181905260c082015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909201910181612a025790505b50905060005b82518110156107f257612a9c838281518110612a8f57612a8f61486f565b6020026020010151611128565b828281518110612aae57612aae61486f565b6020908102919091010152600101612a71565b612ac9612af0565b612ad281613886565b50565b600081815260018301602052604081205415155b9392505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314612b71576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610609565b565b6000610474825490565b6000612ae9838361397b565b6000612ae983836139a5565b6040805160e0810182526000808252602080830182905282840182905260608084018390526080840183905260a0840181905260c084015263ffffffff8581168352600d8252848320805464010000000090049091168084526001909101825284832060028101805487518186028101860190985280885295969295919493909190830182828015612c4657602002820191906000526020600020905b815481526020019060010190808311612c32575b505050505090506000815167ffffffffffffffff811115612c6957612c69613de0565b604051908082528060200260200182016040528015612caf57816020015b604080518082019091526000815260606020820152815260200190600190039081612c875790505b50905060005b8151811015612dc7576040518060400160405280848381518110612cdb57612cdb61486f565b60200260200101518152602001856003016000868581518110612d0057612d0061486f565b602002602001015181526020019081526020016000208054612d2190614922565b80601f0160208091040260200160405190810160405280929190818152602001828054612d4d90614922565b8015612d9a5780601f10612d6f57610100808354040283529160200191612d9a565b820191906000526020600020905b815481529060010190602001808311612d7d57829003601f168201915b5050505050815250828281518110612db457612db461486f565b6020908102919091010152600101612cb5565b506040805160e08101825263ffffffff8089166000818152600d6020818152868320548086168752948b168187015260ff680100000000000000008604811697870197909752690100000000000000000085048716151560608701529290915290526a010000000000000000000090049091161515608082015260a08101612e4e85613685565b81526020019190915295945050505050565b805163ffffffff9081166000908152600d602090815260408083208286015190941683526001909301905220608082015160ff161580612eb2575060808201518590612ead906001614efa565b60ff16115b15612efb5760808201516040517f25b4d61800000000000000000000000000000000000000000000000000000000815260ff909116600482015260248101869052604401610609565b6001826020015163ffffffff161115612fe357815163ffffffff166000908152600d602090815260408220908401516001918201918391612f3c91906148cd565b63ffffffff1663ffffffff168152602001908152602001600020905060005b612f6482612b73565b811015612fe057612f93846000015163ffffffff16600c60006105928587600001612b7d90919063ffffffff16565b50600c6000612fa28484612b7d565b8152602081019190915260400160002080547fffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffff169055600101612f5b565b50505b60005b8581101561321d576130138787838181106130035761300361486f565b8592602090910201359050613692565b61307457825187878381811061302b5761302b61486f565b6040517f636e405700000000000000000000000000000000000000000000000000000000815263ffffffff90941660048501526020029190910135602483015250604401610609565b8260600151156131cb57825163ffffffff16600c600089898581811061309c5761309c61486f565b602090810292909201358352508101919091526040016000205468010000000000000000900463ffffffff16148015906131165750600c60008888848181106130e7576130e761486f565b602090810292909201358352508101919091526040016000205468010000000000000000900463ffffffff1615155b1561317857825187878381811061312f5761312f61486f565b6040517f60b9df7300000000000000000000000000000000000000000000000000000000815263ffffffff90941660048501526020029190910135602483015250604401610609565b8251600c60008989858181106131905761319061486f565b90506020020135815260200190815260200160002060000160086101000a81548163ffffffff021916908363ffffffff160217905550613215565b82516132139063ffffffff16600c60008a8a868181106131ed576131ed61486f565b90506020020135815260200190815260200160002060040161369290919063ffffffff16565b505b600101612fe6565b5060005b8381101561362b573685858381811061323c5761323c61486f565b905060200281019061324e9190614998565b905061325c60038235612ad5565b613295576040517fe181733f00000000000000000000000000000000000000000000000000000000815281356004820152602401610609565b6132a160058235612ad5565b156132db576040517ff7d7a29400000000000000000000000000000000000000000000000000000000815281356004820152602401610609565b80356000908152600384016020526040812080546132f890614922565b905011156133445783516040517f3927d08000000000000000000000000000000000000000000000000000000000815263ffffffff909116600482015281356024820152604401610609565b60005b8781101561344e576133eb8235600c60008c8c8681811061336a5761336a61486f565b9050602002013581526020019081526020016000206003016000600c60008e8e8881811061339a5761339a61486f565b90506020020135815260200190815260200160002060000160049054906101000a900463ffffffff1663ffffffff1663ffffffff168152602001908152602001600020612ad590919063ffffffff16565b613446578888828181106134015761340161486f565b6040517fa7e792500000000000000000000000000000000000000000000000000000000081526020909102929092013560048301525082356024820152604401610609565b600101613347565b506002830180546001810182556000918252602091829020833591015561347790820182614f13565b82356000908152600386016020526040902091613495919083614f78565b50604080850151855163ffffffff9081166000908152600d602090815284822080549415156901000000000000000000027fffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffff90951694909417909355606088015188518316825284822080549115156a0100000000000000000000027fffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffff9092169190911790556080880151885183168252848220805460ff9290921668010000000000000000027fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff909216919091179055828801805189518416835294909120805494909216640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff9094169390931790558551915161362292918435908c908c906135e890880188614f13565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613a9892505050565b50600101613221565b50815160208301516040517ff264aae70bf6a9d90e68e0f9b393f4e7fbea67b063b0f336e0b36c15817036519261367592909163ffffffff92831681529116602082015260400190565b60405180910390a1505050505050565b60606000612ae983613b79565b6000612ae98383613bd5565b608081015173ffffffffffffffffffffffffffffffffffffffff1615613740576136ec81608001517f78bea72100000000000000000000000000000000000000000000000000000000613c24565b6137405760808101516040517fabb5e3fd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401610609565b60008281526002602052604090208151829190819061375f9082614a90565b50602082015160018201906137749082614a90565b5060408201516002820180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660018360038111156137b6576137b66145ca565b021790555060608201516002820180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101008360018111156137fd576137fd6145ca565b0217905550608091909101516002909101805473ffffffffffffffffffffffffffffffffffffffff90921662010000027fffffffffffffffffffff0000000000000000000000000000000000000000ffff90921691909117905560405182907f04f0a9bcf3f3a3b42a4d7ca081119755f82ebe43e0d30c8f7292c4fe0dc4a2ae90600090a25050565b3373ffffffffffffffffffffffffffffffffffffffff821603613905576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610609565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60008260000182815481106139925761399261486f565b9060005260206000200154905092915050565b60008181526001830160205260408120548015613a8e5760006139c9600183615093565b85549091506000906139dd90600190615093565b9050818114613a425760008660000182815481106139fd576139fd61486f565b9060005260206000200154905080876000018481548110613a2057613a2061486f565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080613a5357613a536150a6565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610474565b6000915050610474565b6000848152600260208190526040909120015462010000900473ffffffffffffffffffffffffffffffffffffffff16156125b757600084815260026020819052604091829020015490517ffba64a7c0000000000000000000000000000000000000000000000000000000081526201000090910473ffffffffffffffffffffffffffffffffffffffff169063fba64a7c90613b3f908690869086908b908d906004016150d5565b600060405180830381600087803b158015613b5957600080fd5b505af1158015613b6d573d6000803e3d6000fd5b50505050505050505050565b606081600001805480602002602001604051908101604052809291908181526020018280548015613bc957602002820191906000526020600020905b815481526020019060010190808311613bb5575b50505050509050919050565b6000818152600183016020526040812054613c1c57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610474565b506000610474565b6000613c2f83613c40565b8015612ae95750612ae98383613ca4565b6000613c6c827f01ffc9a700000000000000000000000000000000000000000000000000000000613ca4565b80156104745750613c9d827fffffffff00000000000000000000000000000000000000000000000000000000613ca4565b1592915050565b604080517fffffffff000000000000000000000000000000000000000000000000000000008316602480830191909152825180830390910181526044909101909152602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a700000000000000000000000000000000000000000000000000000000178152825160009392849283928392918391908a617530fa92503d91506000519050828015613d5c575060208210155b8015613d685750600081115b979650505050505050565b508054613d7f90614922565b6000825580601f10613d8f575050565b601f016020900490600052602060002090810190612ad29190613dc7565b5080546000825590600052602060002090810190612ad291905b5b80821115613ddc5760008155600101613dc8565b5090565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516080810167ffffffffffffffff81118282101715613e3257613e32613de0565b60405290565b60405160a0810167ffffffffffffffff81118282101715613e3257613e32613de0565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613ea257613ea2613de0565b604052919050565b600067ffffffffffffffff821115613ec457613ec4613de0565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600082601f830112613f0157600080fd5b8135613f14613f0f82613eaa565b613e5b565b818152846020838601011115613f2957600080fd5b816020850160208301376000918101602001919091529392505050565b60008060408385031215613f5957600080fd5b823567ffffffffffffffff80821115613f7157600080fd5b613f7d86838701613ef0565b93506020850135915080821115613f9357600080fd5b50613fa085828601613ef0565b9150509250929050565b600060208284031215613fbc57600080fd5b5035919050565b60005b83811015613fde578181015183820152602001613fc6565b50506000910152565b60008151808452613fff816020860160208601613fc3565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000612ae96020830184613fe7565b60008083601f84011261405657600080fd5b50813567ffffffffffffffff81111561406e57600080fd5b6020830191508360208260051b850101111561112157600080fd5b6000806020838503121561409c57600080fd5b823567ffffffffffffffff8111156140b357600080fd5b6140bf85828601614044565b90969095509350505050565b60008151808452602080850194506020840160005b838110156140fc578151875295820195908201906001016140e0565b509495945050505050565b600082825180855260208086019550808260051b84010181860160005b84811015614184578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0018952815180518452840151604085850181905261417081860183613fe7565b9a86019a9450505090830190600101614124565b5090979650505050505050565b600063ffffffff8083511684528060208401511660208501525060ff604083015116604084015260608201511515606084015260808201511515608084015260a082015160e060a08501526141e960e08501826140cb565b905060c083015184820360c08601526142028282614107565b95945050505050565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b82811015614280577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845261426e858351614191565b94509285019290850190600101614234565b5092979650505050505050565b803563ffffffff811681146142a157600080fd5b919050565b6000602082840312156142b857600080fd5b612ae98261428d565b73ffffffffffffffffffffffffffffffffffffffff815116825260006020820151604060208501526142f66040850182613fe7565b949350505050565b602081526000612ae960208301846142c1565b602081526000612ae96020830184614191565b803580151581146142a157600080fd5b803560ff811681146142a157600080fd5b600080600080600080600060a0888a03121561436057600080fd5b6143698861428d565b9650602088013567ffffffffffffffff8082111561438657600080fd5b6143928b838c01614044565b909850965060408a01359150808211156143ab57600080fd5b506143b88a828b01614044565b90955093506143cb905060608901614324565b91506143d960808901614334565b905092959891949750929550565b600080604083850312156143fa57600080fd5b6144038361428d565b946020939093013593505050565b6040815260006144246040830185613fe7565b82810360208401526142028185613fe7565b600063ffffffff808351168452602081818501511681860152816040850151166040860152606084015160608601526080840151608086015260a0840151915060e060a086015261448a60e08601836140cb565b60c08581015187830391880191909152805180835290830193506000918301905b808310156144cb57845182529383019360019290920191908301906144ab565b509695505050505050565b602081526000612ae96020830184614436565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b82811015614280577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845261454c8583516142c1565b94509285019290850190600101614512565b6000806000806040858703121561457457600080fd5b843567ffffffffffffffff8082111561458c57600080fd5b61459888838901614044565b909650945060208701359150808211156145b157600080fd5b506145be87828801614044565b95989497509550505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b805182526000602082015160e0602085015261461860e0850182613fe7565b9050604083015184820360408601526146318282613fe7565b915050606083015160048110614649576146496145ca565b6060850152608083015160028110614663576146636145ca565b8060808601525060a083015161469160a086018273ffffffffffffffffffffffffffffffffffffffff169052565b5060c08301516146a560c086018215159052565b509392505050565b602081526000612ae960208301846145f9565b600080600080600080600060a0888a0312156146db57600080fd5b873567ffffffffffffffff808211156146f357600080fd5b6146ff8b838c01614044565b909950975060208a013591508082111561471857600080fd5b506147258a828b01614044565b9096509450614738905060408901614324565b92506143cb60608901614324565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b82811015614280577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526147a98583516145f9565b9450928501929085019060010161476f565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b82811015614280577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845261481e858351614436565b945092850192908501906001016147e4565b803573ffffffffffffffffffffffffffffffffffffffff811681146142a157600080fd5b60006020828403121561486657600080fd5b612ae982614830565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b63ffffffff8281168282160390808211156107f2576107f261489e565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361491b5761491b61489e565b5060010190565b600181811c9082168061493657607f821691505b60208210810361496f577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600063ffffffff80831681810361498e5761498e61489e565b6001019392505050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18336030181126149cc57600080fd5b9190910192915050565b6000604082360312156149e857600080fd5b6040516040810167ffffffffffffffff8282108183111715614a0c57614a0c613de0565b81604052614a1985614830565b83526020850135915080821115614a2f57600080fd5b50614a3c36828601613ef0565b60208301525092915050565b601f821115610697576000816000526020600020601f850160051c81016020861015614a715750805b601f850160051c820191505b818110156125b757828155600101614a7d565b815167ffffffffffffffff811115614aaa57614aaa613de0565b614abe81614ab88454614922565b84614a48565b602080601f831160018114614b115760008415614adb5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556125b7565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015614b5e57888601518255948401946001909101908401614b3f565b5085821015614b9a57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b600060208284031215614bbc57600080fd5b815167ffffffffffffffff811115614bd357600080fd5b8201601f81018413614be457600080fd5b8051614bf2613f0f82613eaa565b818152856020838501011115614c0757600080fd5b614202826020830160208601613fc3565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff818336030181126149cc57600080fd5b600060808236031215614c5e57600080fd5b614c66613e0f565b614c6f8361428d565b81526020808401358183015260408401356040830152606084013567ffffffffffffffff80821115614ca057600080fd5b9085019036601f830112614cb357600080fd5b813581811115614cc557614cc5613de0565b8060051b9150614cd6848301613e5b565b8181529183018401918481019036841115614cf057600080fd5b938501935b83851015614d0e57843582529385019390850190614cf5565b606087015250939695505050505050565b6020808252825182820181905260009190848201906040850190845b81811015614d5757835183529284019291840191600101614d3b565b50909695505050505050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff618336030181126149cc57600080fd5b8035600281106142a157600080fd5b600060a08236031215614db857600080fd5b614dc0613e38565b823567ffffffffffffffff80821115614dd857600080fd5b614de436838701613ef0565b83526020850135915080821115614dfa57600080fd5b50614e0736828601613ef0565b602083015250604083013560048110614e1f57600080fd5b6040820152614e3060608401614d97565b6060820152614e4160808401614830565b608082015292915050565b6000602080835260008454614e6081614922565b8060208701526040600180841660008114614e825760018114614ebc57614eec565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00851660408a0152604084151560051b8a01019550614eec565b89600052602060002060005b85811015614ee35781548b8201860152908301908801614ec8565b8a016040019650505b509398975050505050505050565b60ff81811683821601908111156104745761047461489e565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112614f4857600080fd5b83018035915067ffffffffffffffff821115614f6357600080fd5b60200191503681900382131561112157600080fd5b67ffffffffffffffff831115614f9057614f90613de0565b614fa483614f9e8354614922565b83614a48565b6000601f841160018114614ff65760008515614fc05750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b17835561508c565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156150455786850135825560209485019460019092019101615025565b5086821015615080577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b818103818111156104745761047461489e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b6080815284608082015260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86111561510e57600080fd5b8560051b808860a0850137820182810360a0908101602085015261513490820187613fe7565b91505063ffffffff8085166040840152808416606084015250969550505050505056fea164736f6c6343000818000a", } var CapabilitiesRegistryABI = CapabilitiesRegistryMetaData.ABI @@ -633,16 +633,16 @@ func (_CapabilitiesRegistry *CapabilitiesRegistryTransactorSession) TransferOwne return _CapabilitiesRegistry.Contract.TransferOwnership(&_CapabilitiesRegistry.TransactOpts, to) } -func (_CapabilitiesRegistry *CapabilitiesRegistryTransactor) UpdateDON(opts *bind.TransactOpts, donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilitiesRegistryCapabilityConfiguration, isPublic bool, acceptsWorkflows bool, f uint8) (*types.Transaction, error) { - return _CapabilitiesRegistry.contract.Transact(opts, "updateDON", donId, nodes, capabilityConfigurations, isPublic, acceptsWorkflows, f) +func (_CapabilitiesRegistry *CapabilitiesRegistryTransactor) UpdateDON(opts *bind.TransactOpts, donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilitiesRegistryCapabilityConfiguration, isPublic bool, f uint8) (*types.Transaction, error) { + return _CapabilitiesRegistry.contract.Transact(opts, "updateDON", donId, nodes, capabilityConfigurations, isPublic, f) } -func (_CapabilitiesRegistry *CapabilitiesRegistrySession) UpdateDON(donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilitiesRegistryCapabilityConfiguration, isPublic bool, acceptsWorkflows bool, f uint8) (*types.Transaction, error) { - return _CapabilitiesRegistry.Contract.UpdateDON(&_CapabilitiesRegistry.TransactOpts, donId, nodes, capabilityConfigurations, isPublic, acceptsWorkflows, f) +func (_CapabilitiesRegistry *CapabilitiesRegistrySession) UpdateDON(donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilitiesRegistryCapabilityConfiguration, isPublic bool, f uint8) (*types.Transaction, error) { + return _CapabilitiesRegistry.Contract.UpdateDON(&_CapabilitiesRegistry.TransactOpts, donId, nodes, capabilityConfigurations, isPublic, f) } -func (_CapabilitiesRegistry *CapabilitiesRegistryTransactorSession) UpdateDON(donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilitiesRegistryCapabilityConfiguration, isPublic bool, acceptsWorkflows bool, f uint8) (*types.Transaction, error) { - return _CapabilitiesRegistry.Contract.UpdateDON(&_CapabilitiesRegistry.TransactOpts, donId, nodes, capabilityConfigurations, isPublic, acceptsWorkflows, f) +func (_CapabilitiesRegistry *CapabilitiesRegistryTransactorSession) UpdateDON(donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilitiesRegistryCapabilityConfiguration, isPublic bool, f uint8) (*types.Transaction, error) { + return _CapabilitiesRegistry.Contract.UpdateDON(&_CapabilitiesRegistry.TransactOpts, donId, nodes, capabilityConfigurations, isPublic, f) } func (_CapabilitiesRegistry *CapabilitiesRegistryTransactor) UpdateNodeOperators(opts *bind.TransactOpts, nodeOperatorIds []uint32, nodeOperators []CapabilitiesRegistryNodeOperator) (*types.Transaction, error) { @@ -2214,7 +2214,7 @@ type CapabilitiesRegistryInterface interface { TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) - UpdateDON(opts *bind.TransactOpts, donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilitiesRegistryCapabilityConfiguration, isPublic bool, acceptsWorkflows bool, f uint8) (*types.Transaction, error) + UpdateDON(opts *bind.TransactOpts, donId uint32, nodes [][32]byte, capabilityConfigurations []CapabilitiesRegistryCapabilityConfiguration, isPublic bool, f uint8) (*types.Transaction, error) UpdateNodeOperators(opts *bind.TransactOpts, nodeOperatorIds []uint32, nodeOperators []CapabilitiesRegistryNodeOperator) (*types.Transaction, error) diff --git a/core/gethwrappers/keystone/generated/forwarder/forwarder.go b/core/gethwrappers/keystone/generated/forwarder/forwarder.go index 0412241cf7..a7a78ab67f 100644 --- a/core/gethwrappers/keystone/generated/forwarder/forwarder.go +++ b/core/gethwrappers/keystone/generated/forwarder/forwarder.go @@ -30,9 +30,18 @@ var ( _ = abi.ConvertType ) +type IRouterTransmissionInfo struct { + TransmissionId [32]byte + State uint8 + Transmitter common.Address + InvalidReceiver bool + Success bool + GasLimit *big.Int +} + var KeystoneForwarderMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"transmissionId\",\"type\":\"bytes32\"}],\"name\":\"AlreadyAttempted\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"DuplicateSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"numSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxSigners\",\"type\":\"uint256\"}],\"name\":\"ExcessSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FaultToleranceMustBePositive\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"numSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minSigners\",\"type\":\"uint256\"}],\"name\":\"InsufficientSigners\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"configId\",\"type\":\"uint64\"}],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReport\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"InvalidSignature\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"received\",\"type\":\"uint256\"}],\"name\":\"InvalidSignatureCount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"InvalidSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedForwarder\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"configVersion\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"forwarder\",\"type\":\"address\"}],\"name\":\"ForwarderAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"forwarder\",\"type\":\"address\"}],\"name\":\"ForwarderRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes2\",\"name\":\"reportId\",\"type\":\"bytes2\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"result\",\"type\":\"bool\"}],\"name\":\"ReportProcessed\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"forwarder\",\"type\":\"address\"}],\"name\":\"addForwarder\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configVersion\",\"type\":\"uint32\"}],\"name\":\"clearConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes2\",\"name\":\"reportId\",\"type\":\"bytes2\"}],\"name\":\"getTransmissionId\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes2\",\"name\":\"reportId\",\"type\":\"bytes2\"}],\"name\":\"getTransmissionState\",\"outputs\":[{\"internalType\":\"enumIRouter.TransmissionState\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes2\",\"name\":\"reportId\",\"type\":\"bytes2\"}],\"name\":\"getTransmitter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"forwarder\",\"type\":\"address\"}],\"name\":\"isForwarder\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"forwarder\",\"type\":\"address\"}],\"name\":\"removeForwarder\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"rawReport\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"reportContext\",\"type\":\"bytes\"},{\"internalType\":\"bytes[]\",\"name\":\"signatures\",\"type\":\"bytes[]\"}],\"name\":\"report\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"transmissionId\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"metadata\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"validatedReport\",\"type\":\"bytes\"}],\"name\":\"route\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configVersion\",\"type\":\"uint32\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", - Bin: "0x608060405234801561001057600080fd5b5033806000816100675760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b038481169190911790915581161561009757610097816100b9565b5050306000908152600360205260409020805460ff1916600117905550610162565b336001600160a01b038216036101115760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161005e565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b611b2d80620001726000396000f3fe608060405234801561001057600080fd5b50600436106100ea5760003560e01c806379ba50971161008c578063abcef55411610066578063abcef5541461023e578063ee59d26c14610277578063ef6e17a01461028a578063f2fde38b1461029d57600080fd5b806379ba5097146101e05780638864b864146101e85780638da5cb5b1461022057600080fd5b8063354bdd66116100c8578063354bdd661461017957806343c164671461019a5780634d93172d146101ba5780635c41d2fe146101cd57600080fd5b806311289565146100ef578063181f5a7714610104578063233fd52d14610156575b600080fd5b6101026100fd366004611474565b6102b0565b005b6101406040518060400160405280601a81526020017f466f7277617264657220616e6420526f7574657220312e302e3000000000000081525081565b60405161014d919061151f565b60405180910390f35b61016961016436600461158c565b61080d565b604051901515815260200161014d565b61018c610187366004611614565b610a00565b60405190815260200161014d565b6101ad6101a8366004611614565b610a84565b60405161014d9190611679565b6101026101c83660046116ba565b610b09565b6101026101db3660046116ba565b610b85565b610102610c04565b6101fb6101f6366004611614565b610d01565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161014d565b60005473ffffffffffffffffffffffffffffffffffffffff166101fb565b61016961024c3660046116ba565b73ffffffffffffffffffffffffffffffffffffffff1660009081526003602052604090205460ff1690565b6101026102853660046116e9565b610d41565b610102610298366004611767565b6110ba565b6101026102ab3660046116ba565b61115a565b606d8510156102eb576040517fb55ac75400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080600061032f89898080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061116e92505050565b67ffffffffffffffff8216600090815260026020526040812080549497509195509193509160ff16908190036103a2576040517fdf3b81ea00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024015b60405180910390fd5b856103ae8260016117c9565b60ff1614610400576103c18160016117c9565b6040517fd6022e8e00000000000000000000000000000000000000000000000000000000815260ff909116600482015260248101879052604401610399565b60008b8b6040516104129291906117e8565b60405190819003812061042b918c908c906020016117f8565b60405160208183030381529060405280519060200120905061044b611301565b60005b888110156106cd573660008b8b8481811061046b5761046b611812565b905060200281019061047d9190611841565b9092509050604181146104c05781816040517f2adfdc300000000000000000000000000000000000000000000000000000000081526004016103999291906118ef565b6000600186848460408181106104d8576104d8611812565b6104ea92013560f81c9050601b6117c9565b6104f860206000878961190b565b61050191611935565b61050f60406020888a61190b565b61051891611935565b6040805160008152602081018083529590955260ff909316928401929092526060830152608082015260a0016020604051602081039080840390855afa158015610566573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015173ffffffffffffffffffffffffffffffffffffffff8116600090815260028c0160205291822054909350915081900361060c576040517fbf18af4300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83166004820152602401610399565b600086826020811061062057610620611812565b602002015173ffffffffffffffffffffffffffffffffffffffff161461068a576040517fe021c4f200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83166004820152602401610399565b8186826020811061069d5761069d611812565b73ffffffffffffffffffffffffffffffffffffffff909216602092909202015250506001909201915061044e9050565b50505050505060003073ffffffffffffffffffffffffffffffffffffffff1663233fd52d6106fc8c8686610a00565b338d8d8d602d90606d926107129392919061190b565b8f8f606d9080926107259392919061190b565b6040518863ffffffff1660e01b81526004016107479796959493929190611971565b6020604051808303816000875af1158015610766573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078a91906119d2565b9050817dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916838b73ffffffffffffffffffffffffffffffffffffffff167f3617b009e9785c42daebadb6d3fb553243a4bf586d07ea72d65d80013ce116b5846040516107f9911515815260200190565b60405180910390a450505050505050505050565b3360009081526003602052604081205460ff16610856576040517fd79e123d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008881526004602052604090205473ffffffffffffffffffffffffffffffffffffffff16156108b5576040517fa53dc8ca00000000000000000000000000000000000000000000000000000000815260048101899052602401610399565b600088815260046020526040812080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8a81169190911790915587163b9003610917575060006109f5565b6040517f805f213200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff87169063805f21329061096f9088908890889088906004016119f4565b600060405180830381600087803b15801561098957600080fd5b505af192505050801561099a575060015b6109a6575060006109f5565b50600087815260046020526040902080547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167401000000000000000000000000000000000000000017905560015b979650505050505050565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606085901b166020820152603481018390527fffff000000000000000000000000000000000000000000000000000000000000821660548201526000906056016040516020818303038152906040528051906020012090505b9392505050565b600080610a92858585610a00565b60008181526004602052604090205490915073ffffffffffffffffffffffffffffffffffffffff16610ac8576000915050610a7d565b60008181526004602052604090205474010000000000000000000000000000000000000000900460ff16610afd576002610b00565b60015b95945050505050565b610b11611189565b73ffffffffffffffffffffffffffffffffffffffff811660008181526003602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055517fb96d15bf9258c7b8df062753a6a262864611fc7b060a5ee2e57e79b85f898d389190a250565b610b8d611189565b73ffffffffffffffffffffffffffffffffffffffff811660008181526003602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055517f0ea0ce2c048ff45a4a95f2947879de3fb94abec2f152190400cab2d1272a68e79190a250565b60015473ffffffffffffffffffffffffffffffffffffffff163314610c85576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610399565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b600060046000610d12868686610a00565b815260208101919091526040016000205473ffffffffffffffffffffffffffffffffffffffff16949350505050565b610d49611189565b8260ff16600003610d86576040517f0743bae600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601f811115610dcb576040517f61750f4000000000000000000000000000000000000000000000000000000000815260048101829052601f6024820152604401610399565b610dd6836003611a1b565b60ff168111610e345780610deb846003611a1b565b610df69060016117c9565b6040517f9dd9e6d8000000000000000000000000000000000000000000000000000000008152600481019290925260ff166024820152604401610399565b67ffffffff00000000602086901b1663ffffffff85161760005b67ffffffffffffffff8216600090815260026020526040902060010154811015610ee45767ffffffffffffffff8216600090815260026020819052604082206001810180549190920192919084908110610eaa57610eaa611812565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff168352820192909252604001812055600101610e4e565b5060005b82811015610ffc576000848483818110610f0457610f04611812565b9050602002016020810190610f1991906116ba565b67ffffffffffffffff8416600090815260026020818152604080842073ffffffffffffffffffffffffffffffffffffffff86168552909201905290205490915015610fa8576040517fe021c4f200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610399565b610fb3826001611a3e565b67ffffffffffffffff8416600090815260026020818152604080842073ffffffffffffffffffffffffffffffffffffffff90961684529490910190529190912055600101610ee8565b5067ffffffffffffffff81166000908152600260205260409020611024906001018484611320565b5067ffffffffffffffff81166000908152600260205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff87161790555163ffffffff86811691908816907f4120bd3b23957dd423555817d55654d4481b438aa15485c21b4180c784f1a455906110aa90889088908890611a51565b60405180910390a3505050505050565b6110c2611189565b63ffffffff818116602084811b67ffffffff00000000168217600090815260028252604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690558051828152928301905291928516917f4120bd3b23957dd423555817d55654d4481b438aa15485c21b4180c784f1a4559160405161114e929190611ab7565b60405180910390a35050565b611162611189565b61116b8161120c565b50565b60218101516045820151608b90920151909260c09290921c91565b60005473ffffffffffffffffffffffffffffffffffffffff16331461120a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610399565b565b3373ffffffffffffffffffffffffffffffffffffffff82160361128b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610399565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6040518061040001604052806020906020820280368337509192915050565b828054828255906000526020600020908101928215611398579160200282015b828111156113985781547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff843516178255602090920191600190910190611340565b506113a49291506113a8565b5090565b5b808211156113a457600081556001016113a9565b803573ffffffffffffffffffffffffffffffffffffffff811681146113e157600080fd5b919050565b60008083601f8401126113f857600080fd5b50813567ffffffffffffffff81111561141057600080fd5b60208301915083602082850101111561142857600080fd5b9250929050565b60008083601f84011261144157600080fd5b50813567ffffffffffffffff81111561145957600080fd5b6020830191508360208260051b850101111561142857600080fd5b60008060008060008060006080888a03121561148f57600080fd5b611498886113bd565b9650602088013567ffffffffffffffff808211156114b557600080fd5b6114c18b838c016113e6565b909850965060408a01359150808211156114da57600080fd5b6114e68b838c016113e6565b909650945060608a01359150808211156114ff57600080fd5b5061150c8a828b0161142f565b989b979a50959850939692959293505050565b60006020808352835180602085015260005b8181101561154d57858101830151858201604001528201611531565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b600080600080600080600060a0888a0312156115a757600080fd5b873596506115b7602089016113bd565b95506115c5604089016113bd565b9450606088013567ffffffffffffffff808211156115e257600080fd5b6115ee8b838c016113e6565b909650945060808a013591508082111561160757600080fd5b5061150c8a828b016113e6565b60008060006060848603121561162957600080fd5b611632846113bd565b92506020840135915060408401357fffff0000000000000000000000000000000000000000000000000000000000008116811461166e57600080fd5b809150509250925092565b60208101600383106116b4577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b91905290565b6000602082840312156116cc57600080fd5b610a7d826113bd565b803563ffffffff811681146113e157600080fd5b60008060008060006080868803121561170157600080fd5b61170a866116d5565b9450611718602087016116d5565b9350604086013560ff8116811461172e57600080fd5b9250606086013567ffffffffffffffff81111561174a57600080fd5b6117568882890161142f565b969995985093965092949392505050565b6000806040838503121561177a57600080fd5b611783836116d5565b9150611791602084016116d5565b90509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60ff81811683821601908111156117e2576117e261179a565b92915050565b8183823760009101908152919050565b838152818360208301376000910160200190815292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261187657600080fd5b83018035915067ffffffffffffffff82111561189157600080fd5b60200191503681900382131561142857600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b6020815260006119036020830184866118a6565b949350505050565b6000808585111561191b57600080fd5b8386111561192857600080fd5b5050820193919092039150565b803560208310156117e2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602084900360031b1b1692915050565b878152600073ffffffffffffffffffffffffffffffffffffffff808916602084015280881660408401525060a060608301526119b160a0830186886118a6565b82810360808401526119c48185876118a6565b9a9950505050505050505050565b6000602082840312156119e457600080fd5b81518015158114610a7d57600080fd5b604081526000611a086040830186886118a6565b82810360208401526109f58185876118a6565b60ff8181168382160290811690818114611a3757611a3761179a565b5092915050565b808201808211156117e2576117e261179a565b60ff8416815260406020808301829052908201839052600090849060608401835b86811015611aab5773ffffffffffffffffffffffffffffffffffffffff611a98856113bd565b1682529282019290820190600101611a72565b50979650505050505050565b60006040820160ff8516835260206040602085015281855180845260608601915060208701935060005b81811015611b1357845173ffffffffffffffffffffffffffffffffffffffff1683529383019391830191600101611ae1565b509097965050505050505056fea164736f6c6343000818000a", + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"transmissionId\",\"type\":\"bytes32\"}],\"name\":\"AlreadyAttempted\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"DuplicateSigner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"numSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxSigners\",\"type\":\"uint256\"}],\"name\":\"ExcessSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FaultToleranceMustBePositive\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"transmissionId\",\"type\":\"bytes32\"}],\"name\":\"InsufficientGasForRouting\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"numSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minSigners\",\"type\":\"uint256\"}],\"name\":\"InsufficientSigners\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"configId\",\"type\":\"uint64\"}],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReport\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"InvalidSignature\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"received\",\"type\":\"uint256\"}],\"name\":\"InvalidSignatureCount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"InvalidSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedForwarder\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"indexed\":true,\"internalType\":\"uint32\",\"name\":\"configVersion\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"forwarder\",\"type\":\"address\"}],\"name\":\"ForwarderAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"forwarder\",\"type\":\"address\"}],\"name\":\"ForwarderRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes2\",\"name\":\"reportId\",\"type\":\"bytes2\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"result\",\"type\":\"bool\"}],\"name\":\"ReportProcessed\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"forwarder\",\"type\":\"address\"}],\"name\":\"addForwarder\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configVersion\",\"type\":\"uint32\"}],\"name\":\"clearConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes2\",\"name\":\"reportId\",\"type\":\"bytes2\"}],\"name\":\"getTransmissionId\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes2\",\"name\":\"reportId\",\"type\":\"bytes2\"}],\"name\":\"getTransmissionInfo\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"transmissionId\",\"type\":\"bytes32\"},{\"internalType\":\"enumIRouter.TransmissionState\",\"name\":\"state\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"invalidReceiver\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"uint80\",\"name\":\"gasLimit\",\"type\":\"uint80\"}],\"internalType\":\"structIRouter.TransmissionInfo\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"workflowExecutionId\",\"type\":\"bytes32\"},{\"internalType\":\"bytes2\",\"name\":\"reportId\",\"type\":\"bytes2\"}],\"name\":\"getTransmitter\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"forwarder\",\"type\":\"address\"}],\"name\":\"isForwarder\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"forwarder\",\"type\":\"address\"}],\"name\":\"removeForwarder\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"rawReport\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"reportContext\",\"type\":\"bytes\"},{\"internalType\":\"bytes[]\",\"name\":\"signatures\",\"type\":\"bytes[]\"}],\"name\":\"report\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"transmissionId\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"metadata\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"validatedReport\",\"type\":\"bytes\"}],\"name\":\"route\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"configVersion\",\"type\":\"uint32\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x60806040523480156200001157600080fd5b503380600081620000695760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b03848116919091179091558116156200009c576200009c81620000bf565b5050306000908152600360205260409020805460ff19166001179055506200016a565b336001600160a01b03821603620001195760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000060565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b612141806200017a6000396000f3fe608060405234801561001057600080fd5b50600436106100ea5760003560e01c806379ba50971161008c578063abcef55411610066578063abcef5541461035d578063ee59d26c14610396578063ef6e17a0146103a9578063f2fde38b146103bc57600080fd5b806379ba50971461025e5780638864b864146102665780638da5cb5b1461033f57600080fd5b8063272cbd93116100c8578063272cbd9314610179578063354bdd66146101995780634d93172d146102385780635c41d2fe1461024b57600080fd5b806311289565146100ef578063181f5a7714610104578063233fd52d14610156575b600080fd5b6101026100fd3660046119df565b6103cf565b005b6101406040518060400160405280601a81526020017f466f7277617264657220616e6420526f7574657220312e302e3000000000000081525081565b60405161014d9190611a8a565b60405180910390f35b610169610164366004611af7565b610989565b604051901515815260200161014d565b61018c610187366004611b7f565b610e4a565b60405161014d9190611c13565b61022a6101a7366004611b7f565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606085901b166020820152603481018390527fffff000000000000000000000000000000000000000000000000000000000000821660548201526000906056016040516020818303038152906040528051906020012090509392505050565b60405190815260200161014d565b610102610246366004611cbb565b611050565b610102610259366004611cbb565b6110cc565b61010261114b565b61031a610274366004611b7f565b6040805160609490941b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660208086019190915260348501939093527fffff000000000000000000000000000000000000000000000000000000000000919091166054840152805160368185030181526056909301815282519282019290922060009081526004909152205473ffffffffffffffffffffffffffffffffffffffff1690565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161014d565b60005473ffffffffffffffffffffffffffffffffffffffff1661031a565b61016961036b366004611cbb565b73ffffffffffffffffffffffffffffffffffffffff1660009081526003602052604090205460ff1690565b6101026103a4366004611cf1565b611248565b6101026103b7366004611d6f565b611625565b6101026103ca366004611cbb565b6116c5565b606d85101561040a576040517fb55ac75400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080600061044e89898080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506116d992505050565b67ffffffffffffffff8216600090815260026020526040812080549497509195509193509160ff16908190036104c1576040517fdf3b81ea00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024015b60405180910390fd5b856104cd826001611dd1565b60ff161461051f576104e0816001611dd1565b6040517fd6022e8e00000000000000000000000000000000000000000000000000000000815260ff9091166004820152602481018790526044016104b8565b60008b8b604051610531929190611df0565b60405190819003812061054a918c908c90602001611e00565b60405160208183030381529060405280519060200120905061056a61186c565b60005b888110156107ec573660008b8b8481811061058a5761058a611e1a565b905060200281019061059c9190611e49565b9092509050604181146105df5781816040517f2adfdc300000000000000000000000000000000000000000000000000000000081526004016104b8929190611ef7565b6000600186848460408181106105f7576105f7611e1a565b61060992013560f81c9050601b611dd1565b610617602060008789611f13565b61062091611f3d565b61062e60406020888a611f13565b61063791611f3d565b6040805160008152602081018083529590955260ff909316928401929092526060830152608082015260a0016020604051602081039080840390855afa158015610685573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015173ffffffffffffffffffffffffffffffffffffffff8116600090815260028c0160205291822054909350915081900361072b576040517fbf18af4300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff831660048201526024016104b8565b600086826020811061073f5761073f611e1a565b602002015173ffffffffffffffffffffffffffffffffffffffff16146107a9576040517fe021c4f200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff831660048201526024016104b8565b818682602081106107bc576107bc611e1a565b73ffffffffffffffffffffffffffffffffffffffff909216602092909202015250506001909201915061056d9050565b50506040805160608f901b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016602080830191909152603482018990527fffff0000000000000000000000000000000000000000000000000000000000008816605483015282516036818403018152605690920190925280519101206000945030935063233fd52d92509050338d8d8d602d90606d9261088e93929190611f13565b8f8f606d9080926108a193929190611f13565b6040518863ffffffff1660e01b81526004016108c39796959493929190611f79565b6020604051808303816000875af11580156108e2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109069190611fda565b9050817dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916838b73ffffffffffffffffffffffffffffffffffffffff167f3617b009e9785c42daebadb6d3fb553243a4bf586d07ea72d65d80013ce116b584604051610975911515815260200190565b60405180910390a450505050505050505050565b3360009081526003602052604081205460ff166109d2576040517fd79e123d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005a9050619c40811015610a16576040517f0bfecd63000000000000000000000000000000000000000000000000000000008152600481018a90526024016104b8565b6000898152600460209081526040918290208251608081018452905473ffffffffffffffffffffffffffffffffffffffff8116825274010000000000000000000000000000000000000000810460ff90811615159383019390935275010000000000000000000000000000000000000000008104909216151592810183905276010000000000000000000000000000000000000000000090910469ffffffffffffffffffff1660608201529080610ace575080602001515b15610b08576040517fa53dc8ca000000000000000000000000000000000000000000000000000000008152600481018b90526024016104b8565b6000610b16619c4084611ffc565b905089600460008d815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600460008d815260200190815260200160002060000160166101000a81548169ffffffffffffffffffff021916908369ffffffffffffffffffff1602179055508873ffffffffffffffffffffffffffffffffffffffff163b600003610c2257505050600088815260046020526040812080547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001790559050610e3f565b6040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527f805f213200000000000000000000000000000000000000000000000000000000600482015273ffffffffffffffffffffffffffffffffffffffff8a16906301ffc9a790602401602060405180830381865afa925050508015610ce6575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252610ce391810190611fda565b60015b610d3f57505050600088815260046020526040812080547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001790559050610e3f565b5060008089898989604051602401610d5a949392919061200f565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f805f21320000000000000000000000000000000000000000000000000000000017815281519192506000918291828f88f191508115610e335760008d815260046020526040902080547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff1675010000000000000000000000000000000000000000001790555b509350610e3f92505050565b979650505050505050565b6040805160c0810182526000808252602080830182905282840182905260608084018390526080840183905260a0840183905284519088901b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001681830152603481018790527fffff000000000000000000000000000000000000000000000000000000000000861660548201528451603681830301815260568201808752815191840191909120808552600490935285842060d68301909652945473ffffffffffffffffffffffffffffffffffffffff811680875274010000000000000000000000000000000000000000820460ff9081161515607685015275010000000000000000000000000000000000000000008304161515609684015276010000000000000000000000000000000000000000000090910469ffffffffffffffffffff1660b69092019190915292939092909190610fa857506000610fd0565b816020015115610fba57506002610fd0565b8160400151610fca576003610fcd565b60015b90505b6040518060c00160405280848152602001826003811115610ff357610ff3611be4565b8152602001836000015173ffffffffffffffffffffffffffffffffffffffff168152602001836020015115158152602001836040015115158152602001836060015169ffffffffffffffffffff1681525093505050509392505050565b6110586116f4565b73ffffffffffffffffffffffffffffffffffffffff811660008181526003602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055517fb96d15bf9258c7b8df062753a6a262864611fc7b060a5ee2e57e79b85f898d389190a250565b6110d46116f4565b73ffffffffffffffffffffffffffffffffffffffff811660008181526003602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055517f0ea0ce2c048ff45a4a95f2947879de3fb94abec2f152190400cab2d1272a68e79190a250565b60015473ffffffffffffffffffffffffffffffffffffffff1633146111cc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016104b8565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b6112506116f4565b8260ff1660000361128d576040517f0743bae600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601f8111156112d2576040517f61750f4000000000000000000000000000000000000000000000000000000000815260048101829052601f60248201526044016104b8565b6112dd836003612036565b60ff16811161133b57806112f2846003612036565b6112fd906001611dd1565b6040517f9dd9e6d8000000000000000000000000000000000000000000000000000000008152600481019290925260ff1660248201526044016104b8565b67ffffffff00000000602086901b1663ffffffff85161760005b67ffffffffffffffff82166000908152600260205260409020600101548110156113eb5767ffffffffffffffff82166000908152600260208190526040822060018101805491909201929190849081106113b1576113b1611e1a565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff168352820192909252604001812055600101611355565b5060005b8281101561156757600084848381811061140b5761140b611e1a565b90506020020160208101906114209190611cbb565b905073ffffffffffffffffffffffffffffffffffffffff8116611487576040517fbf18af4300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016104b8565b67ffffffffffffffff8316600090815260026020818152604080842073ffffffffffffffffffffffffffffffffffffffff86168552909201905290205415611513576040517fe021c4f200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016104b8565b61151e826001612052565b67ffffffffffffffff8416600090815260026020818152604080842073ffffffffffffffffffffffffffffffffffffffff909616845294909101905291909120556001016113ef565b5067ffffffffffffffff8116600090815260026020526040902061158f90600101848461188b565b5067ffffffffffffffff81166000908152600260205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff87161790555163ffffffff86811691908816907f4120bd3b23957dd423555817d55654d4481b438aa15485c21b4180c784f1a4559061161590889088908890612065565b60405180910390a3505050505050565b61162d6116f4565b63ffffffff818116602084811b67ffffffff00000000168217600090815260028252604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690558051828152928301905291928516917f4120bd3b23957dd423555817d55654d4481b438aa15485c21b4180c784f1a455916040516116b99291906120cb565b60405180910390a35050565b6116cd6116f4565b6116d681611777565b50565b60218101516045820151608b90920151909260c09290921c91565b60005473ffffffffffffffffffffffffffffffffffffffff163314611775576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016104b8565b565b3373ffffffffffffffffffffffffffffffffffffffff8216036117f6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016104b8565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6040518061040001604052806020906020820280368337509192915050565b828054828255906000526020600020908101928215611903579160200282015b828111156119035781547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8435161782556020909201916001909101906118ab565b5061190f929150611913565b5090565b5b8082111561190f5760008155600101611914565b803573ffffffffffffffffffffffffffffffffffffffff8116811461194c57600080fd5b919050565b60008083601f84011261196357600080fd5b50813567ffffffffffffffff81111561197b57600080fd5b60208301915083602082850101111561199357600080fd5b9250929050565b60008083601f8401126119ac57600080fd5b50813567ffffffffffffffff8111156119c457600080fd5b6020830191508360208260051b850101111561199357600080fd5b60008060008060008060006080888a0312156119fa57600080fd5b611a0388611928565b9650602088013567ffffffffffffffff80821115611a2057600080fd5b611a2c8b838c01611951565b909850965060408a0135915080821115611a4557600080fd5b611a518b838c01611951565b909650945060608a0135915080821115611a6a57600080fd5b50611a778a828b0161199a565b989b979a50959850939692959293505050565b60006020808352835180602085015260005b81811015611ab857858101830151858201604001528201611a9c565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b600080600080600080600060a0888a031215611b1257600080fd5b87359650611b2260208901611928565b9550611b3060408901611928565b9450606088013567ffffffffffffffff80821115611b4d57600080fd5b611b598b838c01611951565b909650945060808a0135915080821115611b7257600080fd5b50611a778a828b01611951565b600080600060608486031215611b9457600080fd5b611b9d84611928565b92506020840135915060408401357fffff00000000000000000000000000000000000000000000000000000000000081168114611bd957600080fd5b809150509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b81518152602082015160c082019060048110611c58577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8060208401525073ffffffffffffffffffffffffffffffffffffffff604084015116604083015260608301511515606083015260808301511515608083015260a0830151611cb460a084018269ffffffffffffffffffff169052565b5092915050565b600060208284031215611ccd57600080fd5b611cd682611928565b9392505050565b803563ffffffff8116811461194c57600080fd5b600080600080600060808688031215611d0957600080fd5b611d1286611cdd565b9450611d2060208701611cdd565b9350604086013560ff81168114611d3657600080fd5b9250606086013567ffffffffffffffff811115611d5257600080fd5b611d5e8882890161199a565b969995985093965092949392505050565b60008060408385031215611d8257600080fd5b611d8b83611cdd565b9150611d9960208401611cdd565b90509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60ff8181168382160190811115611dea57611dea611da2565b92915050565b8183823760009101908152919050565b838152818360208301376000910160200190815292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112611e7e57600080fd5b83018035915067ffffffffffffffff821115611e9957600080fd5b60200191503681900382131561199357600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b602081526000611f0b602083018486611eae565b949350505050565b60008085851115611f2357600080fd5b83861115611f3057600080fd5b5050820193919092039150565b80356020831015611dea577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602084900360031b1b1692915050565b878152600073ffffffffffffffffffffffffffffffffffffffff808916602084015280881660408401525060a06060830152611fb960a083018688611eae565b8281036080840152611fcc818587611eae565b9a9950505050505050505050565b600060208284031215611fec57600080fd5b81518015158114611cd657600080fd5b81810381811115611dea57611dea611da2565b604081526000612023604083018688611eae565b8281036020840152610e3f818587611eae565b60ff8181168382160290811690818114611cb457611cb4611da2565b80820180821115611dea57611dea611da2565b60ff8416815260406020808301829052908201839052600090849060608401835b868110156120bf5773ffffffffffffffffffffffffffffffffffffffff6120ac85611928565b1682529282019290820190600101612086565b50979650505050505050565b60006040820160ff8516835260206040602085015281855180845260608601915060208701935060005b8181101561212757845173ffffffffffffffffffffffffffffffffffffffff16835293830193918301916001016120f5565b509097965050505050505056fea164736f6c6343000818000a", } var KeystoneForwarderABI = KeystoneForwarderMetaData.ABI @@ -193,26 +202,26 @@ func (_KeystoneForwarder *KeystoneForwarderCallerSession) GetTransmissionId(rece return _KeystoneForwarder.Contract.GetTransmissionId(&_KeystoneForwarder.CallOpts, receiver, workflowExecutionId, reportId) } -func (_KeystoneForwarder *KeystoneForwarderCaller) GetTransmissionState(opts *bind.CallOpts, receiver common.Address, workflowExecutionId [32]byte, reportId [2]byte) (uint8, error) { +func (_KeystoneForwarder *KeystoneForwarderCaller) GetTransmissionInfo(opts *bind.CallOpts, receiver common.Address, workflowExecutionId [32]byte, reportId [2]byte) (IRouterTransmissionInfo, error) { var out []interface{} - err := _KeystoneForwarder.contract.Call(opts, &out, "getTransmissionState", receiver, workflowExecutionId, reportId) + err := _KeystoneForwarder.contract.Call(opts, &out, "getTransmissionInfo", receiver, workflowExecutionId, reportId) if err != nil { - return *new(uint8), err + return *new(IRouterTransmissionInfo), err } - out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + out0 := *abi.ConvertType(out[0], new(IRouterTransmissionInfo)).(*IRouterTransmissionInfo) return out0, err } -func (_KeystoneForwarder *KeystoneForwarderSession) GetTransmissionState(receiver common.Address, workflowExecutionId [32]byte, reportId [2]byte) (uint8, error) { - return _KeystoneForwarder.Contract.GetTransmissionState(&_KeystoneForwarder.CallOpts, receiver, workflowExecutionId, reportId) +func (_KeystoneForwarder *KeystoneForwarderSession) GetTransmissionInfo(receiver common.Address, workflowExecutionId [32]byte, reportId [2]byte) (IRouterTransmissionInfo, error) { + return _KeystoneForwarder.Contract.GetTransmissionInfo(&_KeystoneForwarder.CallOpts, receiver, workflowExecutionId, reportId) } -func (_KeystoneForwarder *KeystoneForwarderCallerSession) GetTransmissionState(receiver common.Address, workflowExecutionId [32]byte, reportId [2]byte) (uint8, error) { - return _KeystoneForwarder.Contract.GetTransmissionState(&_KeystoneForwarder.CallOpts, receiver, workflowExecutionId, reportId) +func (_KeystoneForwarder *KeystoneForwarderCallerSession) GetTransmissionInfo(receiver common.Address, workflowExecutionId [32]byte, reportId [2]byte) (IRouterTransmissionInfo, error) { + return _KeystoneForwarder.Contract.GetTransmissionInfo(&_KeystoneForwarder.CallOpts, receiver, workflowExecutionId, reportId) } func (_KeystoneForwarder *KeystoneForwarderCaller) GetTransmitter(opts *bind.CallOpts, receiver common.Address, workflowExecutionId [32]byte, reportId [2]byte) (common.Address, error) { @@ -1260,7 +1269,7 @@ func (_KeystoneForwarder *KeystoneForwarder) Address() common.Address { type KeystoneForwarderInterface interface { GetTransmissionId(opts *bind.CallOpts, receiver common.Address, workflowExecutionId [32]byte, reportId [2]byte) ([32]byte, error) - GetTransmissionState(opts *bind.CallOpts, receiver common.Address, workflowExecutionId [32]byte, reportId [2]byte) (uint8, error) + GetTransmissionInfo(opts *bind.CallOpts, receiver common.Address, workflowExecutionId [32]byte, reportId [2]byte) (IRouterTransmissionInfo, error) GetTransmitter(opts *bind.CallOpts, receiver common.Address, workflowExecutionId [32]byte, reportId [2]byte) (common.Address, error) diff --git a/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 40cf539277..26ad8f3315 100644 --- a/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/keystone/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,5 +1,5 @@ GETH_VERSION: 1.13.8 -capabilities_registry: ../../../contracts/solc/v0.8.24/CapabilitiesRegistry/CapabilitiesRegistry.abi ../../../contracts/solc/v0.8.24/CapabilitiesRegistry/CapabilitiesRegistry.bin 6d2e3aa3a6f3aed2cf24b613743bb9ae4b9558f48a6864dc03b8b0ebb37235e3 +capabilities_registry: ../../../contracts/solc/v0.8.24/CapabilitiesRegistry/CapabilitiesRegistry.abi ../../../contracts/solc/v0.8.24/CapabilitiesRegistry/CapabilitiesRegistry.bin 2c8947475e3db9e4feadde2c4325bb093f905e352879518dadac470f33e000ce feeds_consumer: ../../../contracts/solc/v0.8.24/KeystoneFeedsConsumer/KeystoneFeedsConsumer.abi ../../../contracts/solc/v0.8.24/KeystoneFeedsConsumer/KeystoneFeedsConsumer.bin 8c3a2b18a80be41e7c40d2bc3a4c8d1b5e18d55c1fd20ad5af68cebb66109fc5 -forwarder: ../../../contracts/solc/v0.8.24/KeystoneForwarder/KeystoneForwarder.abi ../../../contracts/solc/v0.8.24/KeystoneForwarder/KeystoneForwarder.bin dc98a86a3775ead987b79d5b6079ee0e26f31c0626032bdd6508f986e2423227 +forwarder: ../../../contracts/solc/v0.8.24/KeystoneForwarder/KeystoneForwarder.abi ../../../contracts/solc/v0.8.24/KeystoneForwarder/KeystoneForwarder.bin 45d9b866c64b41c1349a90b6764aee42a6d078b454d38f369b5fe02b23b9d16e ocr3_capability: ../../../contracts/solc/v0.8.24/OCR3Capability/OCR3Capability.abi ../../../contracts/solc/v0.8.24/OCR3Capability/OCR3Capability.bin 8bf0f53f222efce7143dea6134552eb26ea1eef845407b4475a0d79b7d7ba9f8 diff --git a/core/gethwrappers/llo-feeds/generated/channel_config_store/channel_config_store.go b/core/gethwrappers/llo-feeds/generated/channel_config_store/channel_config_store.go index 3c67b64227..7dd4407b3a 100644 --- a/core/gethwrappers/llo-feeds/generated/channel_config_store/channel_config_store.go +++ b/core/gethwrappers/llo-feeds/generated/channel_config_store/channel_config_store.go @@ -30,15 +30,9 @@ var ( _ = abi.ConvertType ) -type IChannelConfigStoreChannelDefinition struct { - ReportFormat uint32 - ChainSelector uint64 - StreamIDs []uint32 -} - var ChannelConfigStoreMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ChannelDefinitionNotFound\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EmptyStreamIDs\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByEOA\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"StagingConfigAlreadyPromoted\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroChainSelector\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroReportFormat\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"channelId\",\"type\":\"uint32\"}],\"name\":\"ChannelDefinitionRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"channelId\",\"type\":\"uint32\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"reportFormat\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint32[]\",\"name\":\"streamIDs\",\"type\":\"uint32[]\"}],\"indexed\":false,\"internalType\":\"structIChannelConfigStore.ChannelDefinition\",\"name\":\"channelDefinition\",\"type\":\"tuple\"}],\"name\":\"NewChannelDefinition\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"channelId\",\"type\":\"uint32\"}],\"name\":\"PromoteStagingConfig\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"channelId\",\"type\":\"uint32\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"reportFormat\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint32[]\",\"name\":\"streamIDs\",\"type\":\"uint32[]\"}],\"internalType\":\"structIChannelConfigStore.ChannelDefinition\",\"name\":\"channelDefinition\",\"type\":\"tuple\"}],\"name\":\"addChannel\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"channelId\",\"type\":\"uint32\"}],\"name\":\"getChannelDefinitions\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"reportFormat\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"chainSelector\",\"type\":\"uint64\"},{\"internalType\":\"uint32[]\",\"name\":\"streamIDs\",\"type\":\"uint32[]\"}],\"internalType\":\"structIChannelConfigStore.ChannelDefinition\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"channelId\",\"type\":\"uint32\"}],\"name\":\"removeChannel\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", - Bin: "0x608060405234801561001057600080fd5b5033806000816100675760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615610097576100978161009f565b505050610148565b336001600160a01b038216036100f75760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161005e565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b610e4f806101576000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c80638da5cb5b1161005b5780638da5cb5b146101535780639682a4501461017b578063f2fde38b1461018e578063f5810719146101a157600080fd5b806301ffc9a71461008d578063181f5a77146100f757806379ba5097146101365780637e37e71914610140575b600080fd5b6100e261009b3660046107d1565b7fffffffff00000000000000000000000000000000000000000000000000000000167f1d344450000000000000000000000000000000000000000000000000000000001490565b60405190151581526020015b60405180910390f35b604080518082018252601881527f4368616e6e656c436f6e66696753746f726520302e302e300000000000000000602082015290516100ee919061081a565b61013e6101c1565b005b61013e61014e366004610898565b6102c3565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100ee565b61013e6101893660046108b5565b6103a3565b61013e61019c36600461090c565b6104f3565b6101b46101af366004610898565b610507565b6040516100ee9190610942565b60015473ffffffffffffffffffffffffffffffffffffffff163314610247576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b6102cb610620565b63ffffffff8116600090815260026020526040812060010154900361031c576040517fd1a751e200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff8116600090815260026020526040812080547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000168155906103656001830182610798565b505060405163ffffffff821681527f334e877e9691ecae0660510061973bebaa8b4fb37332ed6090052e630c9798619060200160405180910390a150565b6103ab610620565b6103b860408201826109bc565b90506000036103f3576040517f4b620e2400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6104036040820160208301610a41565b67ffffffffffffffff16600003610446576040517ff89d762900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6104536020820182610898565b63ffffffff16600003610492576040517febd3ef0200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff8216600090815260026020526040902081906104b38282610c37565b9050507f35d63e43dd8abd374a4c4e0b5b02c8294dd20e1f493e7344a1751123d11ecc1482826040516104e7929190610d7f565b60405180910390a15050565b6104fb610620565b610504816106a3565b50565b6040805160608082018352600080835260208301529181019190915233321461055c576040517f74e2cd5100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff82811660009081526002602090815260409182902082516060810184528154948516815264010000000090940467ffffffffffffffff16848301526001810180548451818502810185018652818152929486019383018282801561061057602002820191906000526020600020906000905b82829054906101000a900463ffffffff1663ffffffff16815260200190600401906020826003010492830192600103820291508084116105d35790505b5050505050815250509050919050565b60005473ffffffffffffffffffffffffffffffffffffffff1633146106a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640161023e565b565b3373ffffffffffffffffffffffffffffffffffffffff821603610722576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161023e565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b50805460008255600701600890049060005260206000209081019061050491905b808211156107cd57600081556001016107b9565b5090565b6000602082840312156107e357600080fd5b81357fffffffff000000000000000000000000000000000000000000000000000000008116811461081357600080fd5b9392505050565b600060208083528351808285015260005b818110156108475785810183015185820160400152820161082b565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b63ffffffff8116811461050457600080fd5b6000602082840312156108aa57600080fd5b813561081381610886565b600080604083850312156108c857600080fd5b82356108d381610886565b9150602083013567ffffffffffffffff8111156108ef57600080fd5b83016060818603121561090157600080fd5b809150509250929050565b60006020828403121561091e57600080fd5b813573ffffffffffffffffffffffffffffffffffffffff8116811461081357600080fd5b600060208083526080830163ffffffff808651168386015267ffffffffffffffff83870151166040860152604086015160608087015282815180855260a0880191508583019450600092505b808310156109b05784518416825293850193600192909201919085019061098e565b50979650505050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126109f157600080fd5b83018035915067ffffffffffffffff821115610a0c57600080fd5b6020019150600581901b3603821315610a2457600080fd5b9250929050565b67ffffffffffffffff8116811461050457600080fd5b600060208284031215610a5357600080fd5b813561081381610a2b565b60008135610a6b81610886565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b68010000000000000000821115610ab957610ab9610a71565b805482825580831015610b3e576000828152602081206007850160031c81016007840160031c82019150601c8660021b168015610b25577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8083018054828460200360031b1c16815550505b505b81811015610b3a57828155600101610b27565b5050505b505050565b67ffffffffffffffff831115610b5b57610b5b610a71565b610b658382610aa0565b60008181526020902082908460031c60005b81811015610bd0576000805b6008811015610bc357610bb2610b9887610a5e565b63ffffffff908116600584901b90811b91901b1984161790565b602096909601959150600101610b83565b5083820155600101610b77565b507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff88616808703818814610c2d576000805b82811015610c2757610c16610b9888610a5e565b602097909701969150600101610c02565b50848401555b5050505050505050565b8135610c4281610886565b63ffffffff811690508154817fffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000082161783556020840135610c8281610a2b565b6bffffffffffffffff000000008160201b16837fffffffffffffffffffffffffffffffffffffffff00000000000000000000000084161717845550505060408201357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1833603018112610cf457600080fd5b8201803567ffffffffffffffff811115610d0d57600080fd5b6020820191508060051b3603821315610d2557600080fd5b610d33818360018601610b43565b50505050565b8183526000602080850194508260005b85811015610d74578135610d5c81610886565b63ffffffff1687529582019590820190600101610d49565b509495945050505050565b600063ffffffff8085168352604060208401528335610d9d81610886565b1660408301526020830135610db181610a2b565b67ffffffffffffffff8082166060850152604085013591507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1853603018212610df957600080fd5b6020918501918201913581811115610e1057600080fd5b8060051b3603831315610e2257600080fd5b60606080860152610e3760a086018285610d39565b97965050505050505056fea164736f6c6343000813000a", + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"donId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"version\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"url\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"sha\",\"type\":\"bytes32\"}],\"name\":\"NewChannelDefinition\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"donId\",\"type\":\"uint32\"},{\"internalType\":\"string\",\"name\":\"url\",\"type\":\"string\"},{\"internalType\":\"bytes32\",\"name\":\"sha\",\"type\":\"bytes32\"}],\"name\":\"setChannelDefinitions\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b5033806000816100675760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615610097576100978161009f565b505050610148565b336001600160a01b038216036100f75760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161005e565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6106d2806101576000396000f3fe608060405234801561001057600080fd5b50600436106100725760003560e01c806379ba50971161005057806379ba5097146101355780638da5cb5b1461013d578063f2fde38b1461016557600080fd5b806301ffc9a714610077578063181f5a77146100e15780635ba5bac214610120575b600080fd5b6100cc610085366004610483565b7fffffffff00000000000000000000000000000000000000000000000000000000167f5ba5bac2000000000000000000000000000000000000000000000000000000001490565b60405190151581526020015b60405180910390f35b604080518082018252601881527f4368616e6e656c436f6e66696753746f726520302e302e310000000000000000602082015290516100d891906104cc565b61013361012e366004610538565b610178565b005b6101336101f5565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100d8565b6101336101733660046105cc565b6102f7565b61018061030b565b63ffffffff84166000908152600260205260408120805482906101a290610602565b91905081905590508463ffffffff167fe5b641a7879fb491e4e5a35a1ce950f0237b2537ee9b1b1e4fb65e29aff1f5e8828686866040516101e69493929190610661565b60405180910390a25050505050565b60015473ffffffffffffffffffffffffffffffffffffffff16331461027b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b6102ff61030b565b6103088161038e565b50565b60005473ffffffffffffffffffffffffffffffffffffffff16331461038c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610272565b565b3373ffffffffffffffffffffffffffffffffffffffff82160361040d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610272565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60006020828403121561049557600080fd5b81357fffffffff00000000000000000000000000000000000000000000000000000000811681146104c557600080fd5b9392505050565b600060208083528351808285015260005b818110156104f9578581018301518582016040015282016104dd565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b6000806000806060858703121561054e57600080fd5b843563ffffffff8116811461056257600080fd5b9350602085013567ffffffffffffffff8082111561057f57600080fd5b818701915087601f83011261059357600080fd5b8135818111156105a257600080fd5b8860208285010111156105b457600080fd5b95986020929092019750949560400135945092505050565b6000602082840312156105de57600080fd5b813573ffffffffffffffffffffffffffffffffffffffff811681146104c557600080fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361065a577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b63ffffffff851681526060602082015282606082015282846080830137600060808483010152600060807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f86011683010190508260408301529594505050505056fea164736f6c6343000813000a", } var ChannelConfigStoreABI = ChannelConfigStoreMetaData.ABI @@ -177,28 +171,6 @@ func (_ChannelConfigStore *ChannelConfigStoreTransactorRaw) Transact(opts *bind. return _ChannelConfigStore.Contract.contract.Transact(opts, method, params...) } -func (_ChannelConfigStore *ChannelConfigStoreCaller) GetChannelDefinitions(opts *bind.CallOpts, channelId uint32) (IChannelConfigStoreChannelDefinition, error) { - var out []interface{} - err := _ChannelConfigStore.contract.Call(opts, &out, "getChannelDefinitions", channelId) - - if err != nil { - return *new(IChannelConfigStoreChannelDefinition), err - } - - out0 := *abi.ConvertType(out[0], new(IChannelConfigStoreChannelDefinition)).(*IChannelConfigStoreChannelDefinition) - - return out0, err - -} - -func (_ChannelConfigStore *ChannelConfigStoreSession) GetChannelDefinitions(channelId uint32) (IChannelConfigStoreChannelDefinition, error) { - return _ChannelConfigStore.Contract.GetChannelDefinitions(&_ChannelConfigStore.CallOpts, channelId) -} - -func (_ChannelConfigStore *ChannelConfigStoreCallerSession) GetChannelDefinitions(channelId uint32) (IChannelConfigStoreChannelDefinition, error) { - return _ChannelConfigStore.Contract.GetChannelDefinitions(&_ChannelConfigStore.CallOpts, channelId) -} - func (_ChannelConfigStore *ChannelConfigStoreCaller) Owner(opts *bind.CallOpts) (common.Address, error) { var out []interface{} err := _ChannelConfigStore.contract.Call(opts, &out, "owner") @@ -277,28 +249,16 @@ func (_ChannelConfigStore *ChannelConfigStoreTransactorSession) AcceptOwnership( return _ChannelConfigStore.Contract.AcceptOwnership(&_ChannelConfigStore.TransactOpts) } -func (_ChannelConfigStore *ChannelConfigStoreTransactor) AddChannel(opts *bind.TransactOpts, channelId uint32, channelDefinition IChannelConfigStoreChannelDefinition) (*types.Transaction, error) { - return _ChannelConfigStore.contract.Transact(opts, "addChannel", channelId, channelDefinition) -} - -func (_ChannelConfigStore *ChannelConfigStoreSession) AddChannel(channelId uint32, channelDefinition IChannelConfigStoreChannelDefinition) (*types.Transaction, error) { - return _ChannelConfigStore.Contract.AddChannel(&_ChannelConfigStore.TransactOpts, channelId, channelDefinition) -} - -func (_ChannelConfigStore *ChannelConfigStoreTransactorSession) AddChannel(channelId uint32, channelDefinition IChannelConfigStoreChannelDefinition) (*types.Transaction, error) { - return _ChannelConfigStore.Contract.AddChannel(&_ChannelConfigStore.TransactOpts, channelId, channelDefinition) -} - -func (_ChannelConfigStore *ChannelConfigStoreTransactor) RemoveChannel(opts *bind.TransactOpts, channelId uint32) (*types.Transaction, error) { - return _ChannelConfigStore.contract.Transact(opts, "removeChannel", channelId) +func (_ChannelConfigStore *ChannelConfigStoreTransactor) SetChannelDefinitions(opts *bind.TransactOpts, donId uint32, url string, sha [32]byte) (*types.Transaction, error) { + return _ChannelConfigStore.contract.Transact(opts, "setChannelDefinitions", donId, url, sha) } -func (_ChannelConfigStore *ChannelConfigStoreSession) RemoveChannel(channelId uint32) (*types.Transaction, error) { - return _ChannelConfigStore.Contract.RemoveChannel(&_ChannelConfigStore.TransactOpts, channelId) +func (_ChannelConfigStore *ChannelConfigStoreSession) SetChannelDefinitions(donId uint32, url string, sha [32]byte) (*types.Transaction, error) { + return _ChannelConfigStore.Contract.SetChannelDefinitions(&_ChannelConfigStore.TransactOpts, donId, url, sha) } -func (_ChannelConfigStore *ChannelConfigStoreTransactorSession) RemoveChannel(channelId uint32) (*types.Transaction, error) { - return _ChannelConfigStore.Contract.RemoveChannel(&_ChannelConfigStore.TransactOpts, channelId) +func (_ChannelConfigStore *ChannelConfigStoreTransactorSession) SetChannelDefinitions(donId uint32, url string, sha [32]byte) (*types.Transaction, error) { + return _ChannelConfigStore.Contract.SetChannelDefinitions(&_ChannelConfigStore.TransactOpts, donId, url, sha) } func (_ChannelConfigStore *ChannelConfigStoreTransactor) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { @@ -313,123 +273,6 @@ func (_ChannelConfigStore *ChannelConfigStoreTransactorSession) TransferOwnershi return _ChannelConfigStore.Contract.TransferOwnership(&_ChannelConfigStore.TransactOpts, to) } -type ChannelConfigStoreChannelDefinitionRemovedIterator struct { - Event *ChannelConfigStoreChannelDefinitionRemoved - - contract *bind.BoundContract - event string - - logs chan types.Log - sub ethereum.Subscription - done bool - fail error -} - -func (it *ChannelConfigStoreChannelDefinitionRemovedIterator) Next() bool { - - if it.fail != nil { - return false - } - - if it.done { - select { - case log := <-it.logs: - it.Event = new(ChannelConfigStoreChannelDefinitionRemoved) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - default: - return false - } - } - - select { - case log := <-it.logs: - it.Event = new(ChannelConfigStoreChannelDefinitionRemoved) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - case err := <-it.sub.Err(): - it.done = true - it.fail = err - return it.Next() - } -} - -func (it *ChannelConfigStoreChannelDefinitionRemovedIterator) Error() error { - return it.fail -} - -func (it *ChannelConfigStoreChannelDefinitionRemovedIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -type ChannelConfigStoreChannelDefinitionRemoved struct { - ChannelId uint32 - Raw types.Log -} - -func (_ChannelConfigStore *ChannelConfigStoreFilterer) FilterChannelDefinitionRemoved(opts *bind.FilterOpts) (*ChannelConfigStoreChannelDefinitionRemovedIterator, error) { - - logs, sub, err := _ChannelConfigStore.contract.FilterLogs(opts, "ChannelDefinitionRemoved") - if err != nil { - return nil, err - } - return &ChannelConfigStoreChannelDefinitionRemovedIterator{contract: _ChannelConfigStore.contract, event: "ChannelDefinitionRemoved", logs: logs, sub: sub}, nil -} - -func (_ChannelConfigStore *ChannelConfigStoreFilterer) WatchChannelDefinitionRemoved(opts *bind.WatchOpts, sink chan<- *ChannelConfigStoreChannelDefinitionRemoved) (event.Subscription, error) { - - logs, sub, err := _ChannelConfigStore.contract.WatchLogs(opts, "ChannelDefinitionRemoved") - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - - event := new(ChannelConfigStoreChannelDefinitionRemoved) - if err := _ChannelConfigStore.contract.UnpackLog(event, "ChannelDefinitionRemoved", log); err != nil { - return err - } - event.Raw = log - - select { - case sink <- event: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -func (_ChannelConfigStore *ChannelConfigStoreFilterer) ParseChannelDefinitionRemoved(log types.Log) (*ChannelConfigStoreChannelDefinitionRemoved, error) { - event := new(ChannelConfigStoreChannelDefinitionRemoved) - if err := _ChannelConfigStore.contract.UnpackLog(event, "ChannelDefinitionRemoved", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} - type ChannelConfigStoreNewChannelDefinitionIterator struct { Event *ChannelConfigStoreNewChannelDefinition @@ -491,23 +334,35 @@ func (it *ChannelConfigStoreNewChannelDefinitionIterator) Close() error { } type ChannelConfigStoreNewChannelDefinition struct { - ChannelId uint32 - ChannelDefinition IChannelConfigStoreChannelDefinition - Raw types.Log + DonId *big.Int + Version uint32 + Url string + Sha [32]byte + Raw types.Log } -func (_ChannelConfigStore *ChannelConfigStoreFilterer) FilterNewChannelDefinition(opts *bind.FilterOpts) (*ChannelConfigStoreNewChannelDefinitionIterator, error) { +func (_ChannelConfigStore *ChannelConfigStoreFilterer) FilterNewChannelDefinition(opts *bind.FilterOpts, donId []*big.Int) (*ChannelConfigStoreNewChannelDefinitionIterator, error) { + + var donIdRule []interface{} + for _, donIdItem := range donId { + donIdRule = append(donIdRule, donIdItem) + } - logs, sub, err := _ChannelConfigStore.contract.FilterLogs(opts, "NewChannelDefinition") + logs, sub, err := _ChannelConfigStore.contract.FilterLogs(opts, "NewChannelDefinition", donIdRule) if err != nil { return nil, err } return &ChannelConfigStoreNewChannelDefinitionIterator{contract: _ChannelConfigStore.contract, event: "NewChannelDefinition", logs: logs, sub: sub}, nil } -func (_ChannelConfigStore *ChannelConfigStoreFilterer) WatchNewChannelDefinition(opts *bind.WatchOpts, sink chan<- *ChannelConfigStoreNewChannelDefinition) (event.Subscription, error) { +func (_ChannelConfigStore *ChannelConfigStoreFilterer) WatchNewChannelDefinition(opts *bind.WatchOpts, sink chan<- *ChannelConfigStoreNewChannelDefinition, donId []*big.Int) (event.Subscription, error) { + + var donIdRule []interface{} + for _, donIdItem := range donId { + donIdRule = append(donIdRule, donIdItem) + } - logs, sub, err := _ChannelConfigStore.contract.WatchLogs(opts, "NewChannelDefinition") + logs, sub, err := _ChannelConfigStore.contract.WatchLogs(opts, "NewChannelDefinition", donIdRule) if err != nil { return nil, err } @@ -820,147 +675,22 @@ func (_ChannelConfigStore *ChannelConfigStoreFilterer) ParseOwnershipTransferred return event, nil } -type ChannelConfigStorePromoteStagingConfigIterator struct { - Event *ChannelConfigStorePromoteStagingConfig - - contract *bind.BoundContract - event string - - logs chan types.Log - sub ethereum.Subscription - done bool - fail error -} - -func (it *ChannelConfigStorePromoteStagingConfigIterator) Next() bool { - - if it.fail != nil { - return false - } - - if it.done { - select { - case log := <-it.logs: - it.Event = new(ChannelConfigStorePromoteStagingConfig) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - default: - return false - } - } - - select { - case log := <-it.logs: - it.Event = new(ChannelConfigStorePromoteStagingConfig) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - case err := <-it.sub.Err(): - it.done = true - it.fail = err - return it.Next() - } -} - -func (it *ChannelConfigStorePromoteStagingConfigIterator) Error() error { - return it.fail -} - -func (it *ChannelConfigStorePromoteStagingConfigIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -type ChannelConfigStorePromoteStagingConfig struct { - ChannelId uint32 - Raw types.Log -} - -func (_ChannelConfigStore *ChannelConfigStoreFilterer) FilterPromoteStagingConfig(opts *bind.FilterOpts) (*ChannelConfigStorePromoteStagingConfigIterator, error) { - - logs, sub, err := _ChannelConfigStore.contract.FilterLogs(opts, "PromoteStagingConfig") - if err != nil { - return nil, err - } - return &ChannelConfigStorePromoteStagingConfigIterator{contract: _ChannelConfigStore.contract, event: "PromoteStagingConfig", logs: logs, sub: sub}, nil -} - -func (_ChannelConfigStore *ChannelConfigStoreFilterer) WatchPromoteStagingConfig(opts *bind.WatchOpts, sink chan<- *ChannelConfigStorePromoteStagingConfig) (event.Subscription, error) { - - logs, sub, err := _ChannelConfigStore.contract.WatchLogs(opts, "PromoteStagingConfig") - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - - event := new(ChannelConfigStorePromoteStagingConfig) - if err := _ChannelConfigStore.contract.UnpackLog(event, "PromoteStagingConfig", log); err != nil { - return err - } - event.Raw = log - - select { - case sink <- event: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -func (_ChannelConfigStore *ChannelConfigStoreFilterer) ParsePromoteStagingConfig(log types.Log) (*ChannelConfigStorePromoteStagingConfig, error) { - event := new(ChannelConfigStorePromoteStagingConfig) - if err := _ChannelConfigStore.contract.UnpackLog(event, "PromoteStagingConfig", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} - func (_ChannelConfigStore *ChannelConfigStore) ParseLog(log types.Log) (generated.AbigenLog, error) { switch log.Topics[0] { - case _ChannelConfigStore.abi.Events["ChannelDefinitionRemoved"].ID: - return _ChannelConfigStore.ParseChannelDefinitionRemoved(log) case _ChannelConfigStore.abi.Events["NewChannelDefinition"].ID: return _ChannelConfigStore.ParseNewChannelDefinition(log) case _ChannelConfigStore.abi.Events["OwnershipTransferRequested"].ID: return _ChannelConfigStore.ParseOwnershipTransferRequested(log) case _ChannelConfigStore.abi.Events["OwnershipTransferred"].ID: return _ChannelConfigStore.ParseOwnershipTransferred(log) - case _ChannelConfigStore.abi.Events["PromoteStagingConfig"].ID: - return _ChannelConfigStore.ParsePromoteStagingConfig(log) default: return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) } } -func (ChannelConfigStoreChannelDefinitionRemoved) Topic() common.Hash { - return common.HexToHash("0x334e877e9691ecae0660510061973bebaa8b4fb37332ed6090052e630c979861") -} - func (ChannelConfigStoreNewChannelDefinition) Topic() common.Hash { - return common.HexToHash("0x35d63e43dd8abd374a4c4e0b5b02c8294dd20e1f493e7344a1751123d11ecc14") + return common.HexToHash("0xe5b641a7879fb491e4e5a35a1ce950f0237b2537ee9b1b1e4fb65e29aff1f5e8") } func (ChannelConfigStoreOwnershipTransferRequested) Topic() common.Hash { @@ -971,17 +701,11 @@ func (ChannelConfigStoreOwnershipTransferred) Topic() common.Hash { return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") } -func (ChannelConfigStorePromoteStagingConfig) Topic() common.Hash { - return common.HexToHash("0xbdd8ee023f9979bf23e8af6fd7241f484024e83fb0fabd11bb7fd5e9bed7308a") -} - func (_ChannelConfigStore *ChannelConfigStore) Address() common.Address { return _ChannelConfigStore.address } type ChannelConfigStoreInterface interface { - GetChannelDefinitions(opts *bind.CallOpts, channelId uint32) (IChannelConfigStoreChannelDefinition, error) - Owner(opts *bind.CallOpts) (common.Address, error) SupportsInterface(opts *bind.CallOpts, interfaceId [4]byte) (bool, error) @@ -990,21 +714,13 @@ type ChannelConfigStoreInterface interface { AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) - AddChannel(opts *bind.TransactOpts, channelId uint32, channelDefinition IChannelConfigStoreChannelDefinition) (*types.Transaction, error) - - RemoveChannel(opts *bind.TransactOpts, channelId uint32) (*types.Transaction, error) + SetChannelDefinitions(opts *bind.TransactOpts, donId uint32, url string, sha [32]byte) (*types.Transaction, error) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) - FilterChannelDefinitionRemoved(opts *bind.FilterOpts) (*ChannelConfigStoreChannelDefinitionRemovedIterator, error) - - WatchChannelDefinitionRemoved(opts *bind.WatchOpts, sink chan<- *ChannelConfigStoreChannelDefinitionRemoved) (event.Subscription, error) - - ParseChannelDefinitionRemoved(log types.Log) (*ChannelConfigStoreChannelDefinitionRemoved, error) - - FilterNewChannelDefinition(opts *bind.FilterOpts) (*ChannelConfigStoreNewChannelDefinitionIterator, error) + FilterNewChannelDefinition(opts *bind.FilterOpts, donId []*big.Int) (*ChannelConfigStoreNewChannelDefinitionIterator, error) - WatchNewChannelDefinition(opts *bind.WatchOpts, sink chan<- *ChannelConfigStoreNewChannelDefinition) (event.Subscription, error) + WatchNewChannelDefinition(opts *bind.WatchOpts, sink chan<- *ChannelConfigStoreNewChannelDefinition, donId []*big.Int) (event.Subscription, error) ParseNewChannelDefinition(log types.Log) (*ChannelConfigStoreNewChannelDefinition, error) @@ -1020,12 +736,6 @@ type ChannelConfigStoreInterface interface { ParseOwnershipTransferred(log types.Log) (*ChannelConfigStoreOwnershipTransferred, error) - FilterPromoteStagingConfig(opts *bind.FilterOpts) (*ChannelConfigStorePromoteStagingConfigIterator, error) - - WatchPromoteStagingConfig(opts *bind.WatchOpts, sink chan<- *ChannelConfigStorePromoteStagingConfig) (event.Subscription, error) - - ParsePromoteStagingConfig(log types.Log) (*ChannelConfigStorePromoteStagingConfig, error) - ParseLog(log types.Log) (generated.AbigenLog, error) Address() common.Address diff --git a/core/gethwrappers/llo-feeds/generated/destination_fee_manager/destination_fee_manager.go b/core/gethwrappers/llo-feeds/generated/destination_fee_manager/destination_fee_manager.go new file mode 100644 index 0000000000..b87cf068ac --- /dev/null +++ b/core/gethwrappers/llo-feeds/generated/destination_fee_manager/destination_fee_manager.go @@ -0,0 +1,1790 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package destination_fee_manager + +import ( + "errors" + "fmt" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" +) + +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +type CommonAddressAndWeight struct { + Addr common.Address + Weight uint64 +} + +type CommonAsset struct { + AssetAddress common.Address + Amount *big.Int +} + +type IDestinationRewardManagerFeePayment struct { + PoolId [32]byte + Amount *big.Int +} + +var DestinationFeeManagerMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_linkAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_nativeAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_verifierAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_rewardManagerAddress\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"ExpiredReport\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidAddress\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidDeposit\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidDiscount\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidQuote\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidReceivingAddress\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSurcharge\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PoolIdMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Unauthorized\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroDeficit\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"subscriber\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"assetAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"indexed\":false,\"internalType\":\"structCommon.Asset\",\"name\":\"fee\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"assetAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"indexed\":false,\"internalType\":\"structCommon.Asset\",\"name\":\"reward\",\"type\":\"tuple\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"appliedDiscount\",\"type\":\"uint256\"}],\"name\":\"DiscountApplied\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"poolId\",\"type\":\"bytes32\"},{\"internalType\":\"uint192\",\"name\":\"amount\",\"type\":\"uint192\"}],\"indexed\":false,\"internalType\":\"structIDestinationRewardManager.FeePayment[]\",\"name\":\"rewards\",\"type\":\"tuple[]\"}],\"name\":\"InsufficientLink\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"linkQuantity\",\"type\":\"uint256\"}],\"name\":\"LinkDeficitCleared\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"newSurcharge\",\"type\":\"uint64\"}],\"name\":\"NativeSurchargeUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"subscriber\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"feedId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"discount\",\"type\":\"uint64\"}],\"name\":\"SubscriberDiscountUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"adminAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"assetAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint192\",\"name\":\"quantity\",\"type\":\"uint192\"}],\"name\":\"Withdraw\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"verifierAddress\",\"type\":\"address\"}],\"name\":\"addVerifier\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"subscriber\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"report\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"quoteAddress\",\"type\":\"address\"}],\"name\":\"getFeeAndReward\",\"outputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"assetAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structCommon.Asset\",\"name\":\"\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"assetAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structCommon.Asset\",\"name\":\"\",\"type\":\"tuple\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"i_linkAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"i_nativeAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"i_rewardManager\",\"outputs\":[{\"internalType\":\"contractIDestinationRewardManager\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"linkAvailableForPayment\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"}],\"name\":\"payLinkDeficit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"payload\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"parameterPayload\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"subscriber\",\"type\":\"address\"}],\"name\":\"processFee\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"poolIds\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes[]\",\"name\":\"payloads\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes\",\"name\":\"parameterPayload\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"subscriber\",\"type\":\"address\"}],\"name\":\"processFeeBulk\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"verifierAddress\",\"type\":\"address\"}],\"name\":\"removeVerifier\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"s_linkDeficit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_nativeSurcharge\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"s_subscriberDiscounts\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"s_verifierAddressList\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"weight\",\"type\":\"uint64\"}],\"internalType\":\"structCommon.AddressAndWeight[]\",\"name\":\"rewardRecipientAndWeights\",\"type\":\"tuple[]\"}],\"name\":\"setFeeRecipients\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"surcharge\",\"type\":\"uint64\"}],\"name\":\"setNativeSurcharge\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"rewardManagerAddress\",\"type\":\"address\"}],\"name\":\"setRewardManager\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"subscriber\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"feedId\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"discount\",\"type\":\"uint64\"}],\"name\":\"updateSubscriberDiscount\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"assetAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint192\",\"name\":\"quantity\",\"type\":\"uint192\"}],\"name\":\"withdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60c06040523480156200001157600080fd5b5060405162003c3238038062003c328339810160408190526200003491620002af565b33806000816200008b5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000be57620000be81620001e7565b5050506001600160a01b0384161580620000df57506001600160a01b038316155b80620000f257506001600160a01b038216155b806200010557506001600160a01b038116155b15620001245760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b03848116608081905284821660a05283821660008181526004602081905260409182902080546001600160a01b03199081169094179055600580549093169486169485179092555163095ea7b360e01b81529081019290925260001960248301529063095ea7b3906044016020604051808303816000875af1158015620001b6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001dc91906200030c565b505050505062000337565b336001600160a01b03821603620002415760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000082565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b80516001600160a01b0381168114620002aa57600080fd5b919050565b60008060008060808587031215620002c657600080fd5b620002d18562000292565b9350620002e16020860162000292565b9250620002f16040860162000292565b9150620003016060860162000292565b905092959194509250565b6000602082840312156200031f57600080fd5b815180151581146200033057600080fd5b9392505050565b60805160a051613850620003e26000396000818161033b0152818161151c0152818161177a015281816117d101528181611a7e0152818161248e01526125370152600081816105430152818161098e01528181610a9801528181610e9b015281816111f4015281816114c50152818161165c0152818161179f015281816118280152818161196d015281816119da01528181611a1a01528181612109015261262b01526138506000f3fe60806040526004361061018b5760003560e01c806386968cfd116100d6578063d09dc3391161007f578063ea4b861b11610059578063ea4b861b14610531578063f2fde38b14610565578063f65df9621461058557600080fd5b8063d09dc33914610491578063e03dab1a146104a6578063e389d9a41461051157600080fd5b80639000b3d6116100b05780639000b3d614610431578063ca2dfd0a14610451578063ce7817d11461047157600080fd5b806386968cfd146103b557806387d6d843146103c85780638da5cb5b1461040657600080fd5b80633690750911610138578063638786681161011257806363878668146103295780637700feeb1461035d57806379ba5097146103a057600080fd5b806336907509146102a45780633aa5ac07146102b7578063505380941461030957600080fd5b8063181f5a7711610169578063181f5a77146102225780631d4d84a21461026e57806332f5f7461461028e57600080fd5b8063013f542b1461019057806301ffc9a7146101d0578063153ee55414610200575b600080fd5b34801561019c57600080fd5b506101bd6101ab366004612d9a565b60036020526000908152604090205481565b6040519081526020015b60405180910390f35b3480156101dc57600080fd5b506101f06101eb366004612db3565b6105a5565b60405190151581526020016101c7565b34801561020c57600080fd5b5061022061021b366004612e27565b6108ea565b005b34801561022e57600080fd5b50604080518082018252601b81527f44657374696e6174696f6e4665654d616e6167657220312e302e300000000000602082015290516101c79190612e68565b34801561027a57600080fd5b50610220610289366004612edf565b610b0b565b34801561029a57600080fd5b506101bd60065481565b6102206102b2366004613036565b610c9f565b3480156102c357600080fd5b506005546102e49073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101c7565b34801561031557600080fd5b50610220610324366004613156565b610f50565b34801561033557600080fd5b506102e47f000000000000000000000000000000000000000000000000000000000000000081565b34801561036957600080fd5b506102e4610378366004612e27565b60046020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b3480156103ac57600080fd5b50610220610fea565b6102206103c3366004613171565b6110ec565b3480156103d457600080fd5b506101bd6103e33660046131fd565b600260209081526000938452604080852082529284528284209052825290205481565b34801561041257600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff166102e4565b34801561043d57600080fd5b5061022061044c366004612e27565b61126d565b34801561045d57600080fd5b5061022061046c366004612e27565b611370565b34801561047d57600080fd5b5061022061048c366004613234565b61146f565b34801561049d57600080fd5b506101bd61162b565b3480156104b257600080fd5b506104c66104c1366004613313565b6116e1565b60408051845173ffffffffffffffffffffffffffffffffffffffff9081168252602095860151868301528451169181019190915292909101516060830152608082015260a0016101c7565b34801561051d57600080fd5b5061022061052c366004612d9a565b611ae0565b34801561053d57600080fd5b506102e47f000000000000000000000000000000000000000000000000000000000000000081565b34801561057157600080fd5b50610220610580366004612e27565b611c95565b34801561059157600080fd5b506102206105a036600461336c565b611ca9565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fe03dab1a00000000000000000000000000000000000000000000000000000000148061063857507fffffffff0000000000000000000000000000000000000000000000000000000082167f5053809400000000000000000000000000000000000000000000000000000000145b8061068457507fffffffff0000000000000000000000000000000000000000000000000000000082167fce7817d100000000000000000000000000000000000000000000000000000000145b806106d057507fffffffff0000000000000000000000000000000000000000000000000000000082167f1d4d84a200000000000000000000000000000000000000000000000000000000145b8061071c57507fffffffff0000000000000000000000000000000000000000000000000000000082167fd09dc33900000000000000000000000000000000000000000000000000000000145b8061076857507fffffffff0000000000000000000000000000000000000000000000000000000082167fe389d9a400000000000000000000000000000000000000000000000000000000145b806107b457507fffffffff0000000000000000000000000000000000000000000000000000000082167f9000b3d600000000000000000000000000000000000000000000000000000000145b8061080057507fffffffff0000000000000000000000000000000000000000000000000000000082167fca2dfd0a00000000000000000000000000000000000000000000000000000000145b8061084c57507fffffffff0000000000000000000000000000000000000000000000000000000082167f86968cfd00000000000000000000000000000000000000000000000000000000145b8061089857507fffffffff0000000000000000000000000000000000000000000000000000000082167f3690750900000000000000000000000000000000000000000000000000000000145b806108e457507fffffffff0000000000000000000000000000000000000000000000000000000082167ff65df96200000000000000000000000000000000000000000000000000000000145b92915050565b6108f2611dbd565b73ffffffffffffffffffffffffffffffffffffffff811661093f576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005546040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9182166004820152600060248201527f00000000000000000000000000000000000000000000000000000000000000009091169063095ea7b3906044016020604051808303816000875af11580156109d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109fd91906133eb565b50600580547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8381169182179092556040517f095ea7b300000000000000000000000000000000000000000000000000000000815260048101919091527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60248201527f00000000000000000000000000000000000000000000000000000000000000009091169063095ea7b3906044016020604051808303816000875af1158015610ae3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0791906133eb565b5050565b610b13611dbd565b73ffffffffffffffffffffffffffffffffffffffff8316610be85760008273ffffffffffffffffffffffffffffffffffffffff168277ffffffffffffffffffffffffffffffffffffffffffffffff1660405160006040518083038185875af1925050503d8060008114610ba2576040519150601f19603f3d011682016040523d82523d6000602084013e610ba7565b606091505b5050905080610be2576040517fef2af20100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505050565b610c2373ffffffffffffffffffffffffffffffffffffffff84168377ffffffffffffffffffffffffffffffffffffffffffffffff8416611e40565b6040805133815273ffffffffffffffffffffffffffffffffffffffff848116602083015285168183015277ffffffffffffffffffffffffffffffffffffffffffffffff8316606082015290517f7ff78a71698bdb18dcca96f52ab25e0a1b146fb6a49adf8e6845299e49021f299181900360800190a15b505050565b3360008181526004602052604090205473ffffffffffffffffffffffffffffffffffffffff1614610cfc576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b85518414610d35576040517e154a0200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008467ffffffffffffffff811115610d5057610d50612f2a565b604051908082528060200260200182016040528015610d8957816020015b610d76612d0d565b815260200190600190039081610d6e5790505b5090506000806000805b88811015610f16576000801b8b8281518110610db157610db161340d565b602002602001015103610df0576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000806000610e248d8d86818110610e0a57610e0a61340d565b9050602002810190610e1c919061343c565b8d8d8d611f14565b9250925092508260200151600014610f025760405180608001604052808f8681518110610e5357610e5361340d565b6020026020010151815260200184815260200183815260200182815250888680610e7c906134d0565b975081518110610e8e57610e8e61340d565b60200260200101819052507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16836000015173ffffffffffffffffffffffffffffffffffffffff1603610efb57866001019650610f02565b8560010195505b50505080610f0f906134d0565b9050610d93565b5082151580610f2457508115155b15610f3a57610f3585858585612024565b610f44565b610f44853461281e565b50505050505050505050565b610f58611dbd565b670de0b6b3a764000067ffffffffffffffff82161115610fa4576040517f05e8ac2900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff811660068190556040519081527f08f7c0d17932ddb8523bc06754d42ff19ebc77d76a8b9bfde02c28ab1ed3d6399060200160405180910390a150565b60015473ffffffffffffffffffffffffffffffffffffffff163314611070576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b3360008181526004602052604090205473ffffffffffffffffffffffffffffffffffffffff1614611149576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080600061115b8888888888611f14565b925092509250826020015160000361117f57611177843461281e565b505050611265565b604080516001808252818301909252600091816020015b61119e612d0d565b81526020019060019003908161119657905050905060405180608001604052808b815260200185815260200184815260200183815250816000815181106111e7576111e761340d565b60200260200101819052507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16846000015173ffffffffffffffffffffffffffffffffffffffff160361125757610f35858260016000612024565b610f44858260006001612024565b505050505050565b611275611dbd565b73ffffffffffffffffffffffffffffffffffffffff81166112c2576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8181166000908152600460205260409020541615611321576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff16600081815260046020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169091179055565b611378611dbd565b73ffffffffffffffffffffffffffffffffffffffff81166113c5576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81811660009081526004602052604090205416611423576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff16600090815260046020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b611477611dbd565b670de0b6b3a764000067ffffffffffffffff821611156114c3576040517f997ea36000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415801561156b57507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b156115a2576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff848116600081815260026020908152604080832088845282528083209487168084529482529182902067ffffffffffffffff86169081905582519485529084015285927f5eba5a8afa39780f0f99b6cbeb95f3da6a7040ca00abd46bdc91a0a060134139910160405180910390a350505050565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa1580156116b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116dc9190613508565b905090565b604080518082018252600080825260208083018290528351808501855282815280820183905284518086018652838152808301849052855180870190965283865291850183905292938261173488613521565b90507fffff0000000000000000000000000000000000000000000000000000000000008082169081016117cf57505073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811683527f0000000000000000000000000000000000000000000000000000000000000000168152909350915060009050611ad7565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff161415801561187757507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff1614155b156118ae576040517ff861803000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060008b8060200190518101906118c7919061357a565b77ffffffffffffffffffffffffffffffffffffffffffffffff91821698509116955063ffffffff169350505042821015905061192f576040517fb6c405f500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff808e16600090815260026020908152604080832089845282528083208f851684529091529020547f000000000000000000000000000000000000000000000000000000000000000090911687526119be6119a682670de0b6b3a76400006135e0565b6119b090866135f3565b670de0b6b3a7640000612867565b602088015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000008116908d1603611a4b5773ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016885260208088015190890152611ac8565b600654600090611a67906119a690670de0b6b3a764000061360a565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168a529050611ac1611ab783670de0b6b3a76400006135e0565b6119b090836135f3565b60208a0152505b96995094975094955050505050505b93509350939050565b611ae8611dbd565b60008181526003602052604081205490819003611b31576040517f03aad31200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000828152600360205260408082208290558051600180825281830190925290816020015b6040805180820190915260008082526020820152815260200190600190039081611b5657905050905060405180604001604052808481526020018377ffffffffffffffffffffffffffffffffffffffffffffffff1681525081600081518110611bc157611bc161340d565b60209081029190910101526005546040517fb0d9fa1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091169063b0d9fa1990611c24908490309060040161367d565b600060405180830381600087803b158015611c3e57600080fd5b505af1158015611c52573d6000803e3d6000fd5b50505050827f843f0b103e50b42b08f9d30f12f961845a6d02623730872e24644899c0dd989583604051611c8891815260200190565b60405180910390a2505050565b611c9d611dbd565b611ca68161289f565b50565b3360008181526004602052604090205473ffffffffffffffffffffffffffffffffffffffff1614801590611cf5575060005473ffffffffffffffffffffffffffffffffffffffff163314155b15611d2c576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005546040517f14060f2300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff909116906314060f2390611d86908690869086906004016136b5565b600060405180830381600087803b158015611da057600080fd5b505af1158015611db4573d6000803e3d6000fd5b50505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314611e3e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401611067565b565b60405173ffffffffffffffffffffffffffffffffffffffff8316602482015260448101829052610c9a9084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152612994565b6040805180820190915260008082526020820152604080518082019091526000808252602082015260003073ffffffffffffffffffffffffffffffffffffffff851603611f8d576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611f9b888a018a613735565b915050600081611faa90613521565b905060007e010000000000000000000000000000000000000000000000000000000000007fffff00000000000000000000000000000000000000000000000000000000000083161461200557612002888a018a612e27565b90505b6120108784836116e1565b955095509550505050955095509592505050565b60008267ffffffffffffffff81111561203f5761203f612f2a565b60405190808252806020026020018201604052801561208457816020015b604080518082019091526000808252602082015281526020019060019003908161205d5790505b50905060008267ffffffffffffffff8111156120a2576120a2612f2a565b6040519080825280602002602001820160405280156120e757816020015b60408051808201909152600080825260208201528152602001906001900390816120c05790505b5090506000808080806120fa888a61360a565b905060005b81811015612449577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168b82815181106121505761215061340d565b6020026020010151602001516000015173ffffffffffffffffffffffffffffffffffffffff16036122165760405180604001604052808c83815181106121985761219861340d565b60200260200101516000015181526020018c83815181106121bb576121bb61340d565b6020026020010151604001516020015177ffffffffffffffffffffffffffffffffffffffffffffffff168152508885806121f4906134d0565b9650815181106122065761220661340d565b602002602001018190525061230b565b60405180604001604052808c83815181106122335761223361340d565b60200260200101516000015181526020018c83815181106122565761225661340d565b6020026020010151604001516020015177ffffffffffffffffffffffffffffffffffffffffffffffff1681525087848061228f906134d0565b9550815181106122a1576122a161340d565b60200260200101819052508a81815181106122be576122be61340d565b60200260200101516020015160200151866122d9919061360a565b95508a81815181106122ed576122ed61340d565b6020026020010151604001516020015185612308919061360a565b94505b8a818151811061231d5761231d61340d565b602002602001015160600151600014612439578b73ffffffffffffffffffffffffffffffffffffffff168b82815181106123595761235961340d565b6020026020010151600001517f88b15eb682210089cddf967648e2cb2a4535aeadc8f8f36050922e33c04e71258d84815181106123985761239861340d565b6020026020010151602001518e85815181106123b6576123b661340d565b6020026020010151604001518f86815181106123d4576123d461340d565b60200260200101516060015160405161243093929190835173ffffffffffffffffffffffffffffffffffffffff908116825260209485015185830152835116604082015291909201516060820152608081019190915260a00190565b60405180910390a35b612442816134d0565b90506120ff565b5060003415612517573486111561248c576040517fb2e532de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663d0e30db0876040518263ffffffff1660e01b81526004016000604051808303818588803b1580156124f457600080fd5b505af1158015612508573d6000803e3d6000fd5b5050505050853403905061255f565b851561255f5761255f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168d3089612aa0565b8751156125f657600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b0d9fa19898e6040518363ffffffff1660e01b81526004016125c392919061367d565b600060405180830381600087803b1580156125dd57600080fd5b505af11580156125f1573d6000803e3d6000fd5b505050505b865115612806576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015612687573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126ab9190613508565b85111561277b5760005b875181101561273e578781815181106126d0576126d061340d565b60200260200101516020015177ffffffffffffffffffffffffffffffffffffffffffffffff16600360008a848151811061270c5761270c61340d565b60209081029190910181015151825281019190915260400160002080549091019055612737816134d0565b90506126b5565b507ff52e5907b69d97c33392936c12d78b494463b78c5b72df50b4c497eee5720b678760405161276e91906137d9565b60405180910390a1612806565b6005546040517fb0d9fa1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091169063b0d9fa19906127d3908a90309060040161367d565b600060405180830381600087803b1580156127ed57600080fd5b505af1158015612801573d6000803e3d6000fd5b505050505b6128108c8261281e565b505050505050505050505050565b8015610b075760405173ffffffffffffffffffffffffffffffffffffffff83169082156108fc029083906000818181858888f19350505050158015610c9a573d6000803e3d6000fd5b60008215612895578161287b6001856135e0565b61288591906137ec565b61289090600161360a565b612898565b60005b9392505050565b3373ffffffffffffffffffffffffffffffffffffffff82160361291e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401611067565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60006129f6826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff16612afe9092919063ffffffff16565b805190915015610c9a5780806020019051810190612a1491906133eb565b610c9a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401611067565b60405173ffffffffffffffffffffffffffffffffffffffff80851660248301528316604482015260648101829052610be29085907f23b872dd0000000000000000000000000000000000000000000000000000000090608401611e92565b6060612b0d8484600085612b15565b949350505050565b606082471015612ba7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401611067565b6000808673ffffffffffffffffffffffffffffffffffffffff168587604051612bd09190613827565b60006040518083038185875af1925050503d8060008114612c0d576040519150601f19603f3d011682016040523d82523d6000602084013e612c12565b606091505b5091509150612c2387838387612c2e565b979650505050505050565b60608315612cc4578251600003612cbd5773ffffffffffffffffffffffffffffffffffffffff85163b612cbd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611067565b5081612b0d565b612b0d8383815115612cd95781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110679190612e68565b604051806080016040528060008019168152602001612d556040518060400160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001600081525090565b8152602001612d8d6040518060400160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001600081525090565b8152602001600081525090565b600060208284031215612dac57600080fd5b5035919050565b600060208284031215612dc557600080fd5b81357fffffffff000000000000000000000000000000000000000000000000000000008116811461289857600080fd5b73ffffffffffffffffffffffffffffffffffffffff81168114611ca657600080fd5b8035612e2281612df5565b919050565b600060208284031215612e3957600080fd5b813561289881612df5565b60005b83811015612e5f578181015183820152602001612e47565b50506000910152565b6020815260008251806020840152612e87816040850160208701612e44565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b77ffffffffffffffffffffffffffffffffffffffffffffffff81168114611ca657600080fd5b600080600060608486031215612ef457600080fd5b8335612eff81612df5565b92506020840135612f0f81612df5565b91506040840135612f1f81612eb9565b809150509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715612fa057612fa0612f2a565b604052919050565b60008083601f840112612fba57600080fd5b50813567ffffffffffffffff811115612fd257600080fd5b6020830191508360208260051b8501011115612fed57600080fd5b9250929050565b60008083601f84011261300657600080fd5b50813567ffffffffffffffff81111561301e57600080fd5b602083019150836020828501011115612fed57600080fd5b6000806000806000806080878903121561304f57600080fd5b863567ffffffffffffffff8082111561306757600080fd5b818901915089601f83011261307b57600080fd5b813560208282111561308f5761308f612f2a565b8160051b61309e828201612f59565b928352848101820192828101908e8511156130b857600080fd5b958301955b848710156130d6578635825295830195908301906130bd565b9b5050508a0135925050808211156130ed57600080fd5b6130f98a838b01612fa8565b9097509550604089013591508082111561311257600080fd5b5061311f89828a01612ff4565b9094509250613132905060608801612e17565b90509295509295509295565b803567ffffffffffffffff81168114612e2257600080fd5b60006020828403121561316857600080fd5b6128988261313e565b6000806000806000806080878903121561318a57600080fd5b86359550602087013567ffffffffffffffff808211156131a957600080fd5b6131b58a838b01612ff4565b909750955060408901359150808211156131ce57600080fd5b506131db89828a01612ff4565b90945092505060608701356131ef81612df5565b809150509295509295509295565b60008060006060848603121561321257600080fd5b833561321d81612df5565b9250602084013591506040840135612f1f81612df5565b6000806000806080858703121561324a57600080fd5b843561325581612df5565b935060208501359250604085013561326c81612df5565b915061327a6060860161313e565b905092959194509250565b600082601f83011261329657600080fd5b813567ffffffffffffffff8111156132b0576132b0612f2a565b6132e160207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601612f59565b8181528460208386010111156132f657600080fd5b816020850160208301376000918101602001919091529392505050565b60008060006060848603121561332857600080fd5b833561333381612df5565b9250602084013567ffffffffffffffff81111561334f57600080fd5b61335b86828701613285565b9250506040840135612f1f81612df5565b60008060006040848603121561338157600080fd5b83359250602084013567ffffffffffffffff808211156133a057600080fd5b818601915086601f8301126133b457600080fd5b8135818111156133c357600080fd5b8760208260061b85010111156133d857600080fd5b6020830194508093505050509250925092565b6000602082840312156133fd57600080fd5b8151801515811461289857600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261347157600080fd5b83018035915067ffffffffffffffff82111561348c57600080fd5b602001915036819003821315612fed57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613501576135016134a1565b5060010190565b60006020828403121561351a57600080fd5b5051919050565b80516020808301519190811015613560577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8160200360031b1b821691505b50919050565b805163ffffffff81168114612e2257600080fd5b60008060008060008060c0878903121561359357600080fd5b865195506135a360208801613566565b94506135b160408801613566565b935060608701516135c181612eb9565b60808801519093506135d281612eb9565b915061313260a08801613566565b818103818111156108e4576108e46134a1565b80820281158282048414176108e4576108e46134a1565b808201808211156108e4576108e46134a1565b600081518084526020808501945080840160005b838110156136725781518051885283015177ffffffffffffffffffffffffffffffffffffffffffffffff168388015260409096019590820190600101613631565b509495945050505050565b604081526000613690604083018561361d565b905073ffffffffffffffffffffffffffffffffffffffff831660208301529392505050565b8381526040602080830182905282820184905260009190859060608501845b878110156137285783356136e781612df5565b73ffffffffffffffffffffffffffffffffffffffff16825267ffffffffffffffff61371385850161313e565b168284015292840192908401906001016136d4565b5098975050505050505050565b6000806080838503121561374857600080fd5b83601f84011261375757600080fd5b6040516060810167ffffffffffffffff828210818311171561377b5761377b612f2a565b81604052829150606086018781111561379357600080fd5b865b818110156137ad578035845260209384019301613795565b50929450913591808311156137c157600080fd5b50506137cf85828601613285565b9150509250929050565b602081526000612898602083018461361d565b600082613822577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b60008251613839818460208701612e44565b919091019291505056fea164736f6c6343000813000a", +} + +var DestinationFeeManagerABI = DestinationFeeManagerMetaData.ABI + +var DestinationFeeManagerBin = DestinationFeeManagerMetaData.Bin + +func DeployDestinationFeeManager(auth *bind.TransactOpts, backend bind.ContractBackend, _linkAddress common.Address, _nativeAddress common.Address, _verifierAddress common.Address, _rewardManagerAddress common.Address) (common.Address, *types.Transaction, *DestinationFeeManager, error) { + parsed, err := DestinationFeeManagerMetaData.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(DestinationFeeManagerBin), backend, _linkAddress, _nativeAddress, _verifierAddress, _rewardManagerAddress) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &DestinationFeeManager{address: address, abi: *parsed, DestinationFeeManagerCaller: DestinationFeeManagerCaller{contract: contract}, DestinationFeeManagerTransactor: DestinationFeeManagerTransactor{contract: contract}, DestinationFeeManagerFilterer: DestinationFeeManagerFilterer{contract: contract}}, nil +} + +type DestinationFeeManager struct { + address common.Address + abi abi.ABI + DestinationFeeManagerCaller + DestinationFeeManagerTransactor + DestinationFeeManagerFilterer +} + +type DestinationFeeManagerCaller struct { + contract *bind.BoundContract +} + +type DestinationFeeManagerTransactor struct { + contract *bind.BoundContract +} + +type DestinationFeeManagerFilterer struct { + contract *bind.BoundContract +} + +type DestinationFeeManagerSession struct { + Contract *DestinationFeeManager + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type DestinationFeeManagerCallerSession struct { + Contract *DestinationFeeManagerCaller + CallOpts bind.CallOpts +} + +type DestinationFeeManagerTransactorSession struct { + Contract *DestinationFeeManagerTransactor + TransactOpts bind.TransactOpts +} + +type DestinationFeeManagerRaw struct { + Contract *DestinationFeeManager +} + +type DestinationFeeManagerCallerRaw struct { + Contract *DestinationFeeManagerCaller +} + +type DestinationFeeManagerTransactorRaw struct { + Contract *DestinationFeeManagerTransactor +} + +func NewDestinationFeeManager(address common.Address, backend bind.ContractBackend) (*DestinationFeeManager, error) { + abi, err := abi.JSON(strings.NewReader(DestinationFeeManagerABI)) + if err != nil { + return nil, err + } + contract, err := bindDestinationFeeManager(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &DestinationFeeManager{address: address, abi: abi, DestinationFeeManagerCaller: DestinationFeeManagerCaller{contract: contract}, DestinationFeeManagerTransactor: DestinationFeeManagerTransactor{contract: contract}, DestinationFeeManagerFilterer: DestinationFeeManagerFilterer{contract: contract}}, nil +} + +func NewDestinationFeeManagerCaller(address common.Address, caller bind.ContractCaller) (*DestinationFeeManagerCaller, error) { + contract, err := bindDestinationFeeManager(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &DestinationFeeManagerCaller{contract: contract}, nil +} + +func NewDestinationFeeManagerTransactor(address common.Address, transactor bind.ContractTransactor) (*DestinationFeeManagerTransactor, error) { + contract, err := bindDestinationFeeManager(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &DestinationFeeManagerTransactor{contract: contract}, nil +} + +func NewDestinationFeeManagerFilterer(address common.Address, filterer bind.ContractFilterer) (*DestinationFeeManagerFilterer, error) { + contract, err := bindDestinationFeeManager(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &DestinationFeeManagerFilterer{contract: contract}, nil +} + +func bindDestinationFeeManager(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := DestinationFeeManagerMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_DestinationFeeManager *DestinationFeeManagerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _DestinationFeeManager.Contract.DestinationFeeManagerCaller.contract.Call(opts, result, method, params...) +} + +func (_DestinationFeeManager *DestinationFeeManagerRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _DestinationFeeManager.Contract.DestinationFeeManagerTransactor.contract.Transfer(opts) +} + +func (_DestinationFeeManager *DestinationFeeManagerRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _DestinationFeeManager.Contract.DestinationFeeManagerTransactor.contract.Transact(opts, method, params...) +} + +func (_DestinationFeeManager *DestinationFeeManagerCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _DestinationFeeManager.Contract.contract.Call(opts, result, method, params...) +} + +func (_DestinationFeeManager *DestinationFeeManagerTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _DestinationFeeManager.Contract.contract.Transfer(opts) +} + +func (_DestinationFeeManager *DestinationFeeManagerTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _DestinationFeeManager.Contract.contract.Transact(opts, method, params...) +} + +func (_DestinationFeeManager *DestinationFeeManagerCaller) GetFeeAndReward(opts *bind.CallOpts, subscriber common.Address, report []byte, quoteAddress common.Address) (CommonAsset, CommonAsset, *big.Int, error) { + var out []interface{} + err := _DestinationFeeManager.contract.Call(opts, &out, "getFeeAndReward", subscriber, report, quoteAddress) + + if err != nil { + return *new(CommonAsset), *new(CommonAsset), *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(CommonAsset)).(*CommonAsset) + out1 := *abi.ConvertType(out[1], new(CommonAsset)).(*CommonAsset) + out2 := *abi.ConvertType(out[2], new(*big.Int)).(**big.Int) + + return out0, out1, out2, err + +} + +func (_DestinationFeeManager *DestinationFeeManagerSession) GetFeeAndReward(subscriber common.Address, report []byte, quoteAddress common.Address) (CommonAsset, CommonAsset, *big.Int, error) { + return _DestinationFeeManager.Contract.GetFeeAndReward(&_DestinationFeeManager.CallOpts, subscriber, report, quoteAddress) +} + +func (_DestinationFeeManager *DestinationFeeManagerCallerSession) GetFeeAndReward(subscriber common.Address, report []byte, quoteAddress common.Address) (CommonAsset, CommonAsset, *big.Int, error) { + return _DestinationFeeManager.Contract.GetFeeAndReward(&_DestinationFeeManager.CallOpts, subscriber, report, quoteAddress) +} + +func (_DestinationFeeManager *DestinationFeeManagerCaller) ILinkAddress(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _DestinationFeeManager.contract.Call(opts, &out, "i_linkAddress") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_DestinationFeeManager *DestinationFeeManagerSession) ILinkAddress() (common.Address, error) { + return _DestinationFeeManager.Contract.ILinkAddress(&_DestinationFeeManager.CallOpts) +} + +func (_DestinationFeeManager *DestinationFeeManagerCallerSession) ILinkAddress() (common.Address, error) { + return _DestinationFeeManager.Contract.ILinkAddress(&_DestinationFeeManager.CallOpts) +} + +func (_DestinationFeeManager *DestinationFeeManagerCaller) INativeAddress(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _DestinationFeeManager.contract.Call(opts, &out, "i_nativeAddress") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_DestinationFeeManager *DestinationFeeManagerSession) INativeAddress() (common.Address, error) { + return _DestinationFeeManager.Contract.INativeAddress(&_DestinationFeeManager.CallOpts) +} + +func (_DestinationFeeManager *DestinationFeeManagerCallerSession) INativeAddress() (common.Address, error) { + return _DestinationFeeManager.Contract.INativeAddress(&_DestinationFeeManager.CallOpts) +} + +func (_DestinationFeeManager *DestinationFeeManagerCaller) IRewardManager(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _DestinationFeeManager.contract.Call(opts, &out, "i_rewardManager") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_DestinationFeeManager *DestinationFeeManagerSession) IRewardManager() (common.Address, error) { + return _DestinationFeeManager.Contract.IRewardManager(&_DestinationFeeManager.CallOpts) +} + +func (_DestinationFeeManager *DestinationFeeManagerCallerSession) IRewardManager() (common.Address, error) { + return _DestinationFeeManager.Contract.IRewardManager(&_DestinationFeeManager.CallOpts) +} + +func (_DestinationFeeManager *DestinationFeeManagerCaller) LinkAvailableForPayment(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _DestinationFeeManager.contract.Call(opts, &out, "linkAvailableForPayment") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_DestinationFeeManager *DestinationFeeManagerSession) LinkAvailableForPayment() (*big.Int, error) { + return _DestinationFeeManager.Contract.LinkAvailableForPayment(&_DestinationFeeManager.CallOpts) +} + +func (_DestinationFeeManager *DestinationFeeManagerCallerSession) LinkAvailableForPayment() (*big.Int, error) { + return _DestinationFeeManager.Contract.LinkAvailableForPayment(&_DestinationFeeManager.CallOpts) +} + +func (_DestinationFeeManager *DestinationFeeManagerCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _DestinationFeeManager.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_DestinationFeeManager *DestinationFeeManagerSession) Owner() (common.Address, error) { + return _DestinationFeeManager.Contract.Owner(&_DestinationFeeManager.CallOpts) +} + +func (_DestinationFeeManager *DestinationFeeManagerCallerSession) Owner() (common.Address, error) { + return _DestinationFeeManager.Contract.Owner(&_DestinationFeeManager.CallOpts) +} + +func (_DestinationFeeManager *DestinationFeeManagerCaller) SLinkDeficit(opts *bind.CallOpts, arg0 [32]byte) (*big.Int, error) { + var out []interface{} + err := _DestinationFeeManager.contract.Call(opts, &out, "s_linkDeficit", arg0) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_DestinationFeeManager *DestinationFeeManagerSession) SLinkDeficit(arg0 [32]byte) (*big.Int, error) { + return _DestinationFeeManager.Contract.SLinkDeficit(&_DestinationFeeManager.CallOpts, arg0) +} + +func (_DestinationFeeManager *DestinationFeeManagerCallerSession) SLinkDeficit(arg0 [32]byte) (*big.Int, error) { + return _DestinationFeeManager.Contract.SLinkDeficit(&_DestinationFeeManager.CallOpts, arg0) +} + +func (_DestinationFeeManager *DestinationFeeManagerCaller) SNativeSurcharge(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _DestinationFeeManager.contract.Call(opts, &out, "s_nativeSurcharge") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_DestinationFeeManager *DestinationFeeManagerSession) SNativeSurcharge() (*big.Int, error) { + return _DestinationFeeManager.Contract.SNativeSurcharge(&_DestinationFeeManager.CallOpts) +} + +func (_DestinationFeeManager *DestinationFeeManagerCallerSession) SNativeSurcharge() (*big.Int, error) { + return _DestinationFeeManager.Contract.SNativeSurcharge(&_DestinationFeeManager.CallOpts) +} + +func (_DestinationFeeManager *DestinationFeeManagerCaller) SSubscriberDiscounts(opts *bind.CallOpts, arg0 common.Address, arg1 [32]byte, arg2 common.Address) (*big.Int, error) { + var out []interface{} + err := _DestinationFeeManager.contract.Call(opts, &out, "s_subscriberDiscounts", arg0, arg1, arg2) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_DestinationFeeManager *DestinationFeeManagerSession) SSubscriberDiscounts(arg0 common.Address, arg1 [32]byte, arg2 common.Address) (*big.Int, error) { + return _DestinationFeeManager.Contract.SSubscriberDiscounts(&_DestinationFeeManager.CallOpts, arg0, arg1, arg2) +} + +func (_DestinationFeeManager *DestinationFeeManagerCallerSession) SSubscriberDiscounts(arg0 common.Address, arg1 [32]byte, arg2 common.Address) (*big.Int, error) { + return _DestinationFeeManager.Contract.SSubscriberDiscounts(&_DestinationFeeManager.CallOpts, arg0, arg1, arg2) +} + +func (_DestinationFeeManager *DestinationFeeManagerCaller) SVerifierAddressList(opts *bind.CallOpts, arg0 common.Address) (common.Address, error) { + var out []interface{} + err := _DestinationFeeManager.contract.Call(opts, &out, "s_verifierAddressList", arg0) + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_DestinationFeeManager *DestinationFeeManagerSession) SVerifierAddressList(arg0 common.Address) (common.Address, error) { + return _DestinationFeeManager.Contract.SVerifierAddressList(&_DestinationFeeManager.CallOpts, arg0) +} + +func (_DestinationFeeManager *DestinationFeeManagerCallerSession) SVerifierAddressList(arg0 common.Address) (common.Address, error) { + return _DestinationFeeManager.Contract.SVerifierAddressList(&_DestinationFeeManager.CallOpts, arg0) +} + +func (_DestinationFeeManager *DestinationFeeManagerCaller) SupportsInterface(opts *bind.CallOpts, interfaceId [4]byte) (bool, error) { + var out []interface{} + err := _DestinationFeeManager.contract.Call(opts, &out, "supportsInterface", interfaceId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +func (_DestinationFeeManager *DestinationFeeManagerSession) SupportsInterface(interfaceId [4]byte) (bool, error) { + return _DestinationFeeManager.Contract.SupportsInterface(&_DestinationFeeManager.CallOpts, interfaceId) +} + +func (_DestinationFeeManager *DestinationFeeManagerCallerSession) SupportsInterface(interfaceId [4]byte) (bool, error) { + return _DestinationFeeManager.Contract.SupportsInterface(&_DestinationFeeManager.CallOpts, interfaceId) +} + +func (_DestinationFeeManager *DestinationFeeManagerCaller) TypeAndVersion(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _DestinationFeeManager.contract.Call(opts, &out, "typeAndVersion") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +func (_DestinationFeeManager *DestinationFeeManagerSession) TypeAndVersion() (string, error) { + return _DestinationFeeManager.Contract.TypeAndVersion(&_DestinationFeeManager.CallOpts) +} + +func (_DestinationFeeManager *DestinationFeeManagerCallerSession) TypeAndVersion() (string, error) { + return _DestinationFeeManager.Contract.TypeAndVersion(&_DestinationFeeManager.CallOpts) +} + +func (_DestinationFeeManager *DestinationFeeManagerTransactor) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _DestinationFeeManager.contract.Transact(opts, "acceptOwnership") +} + +func (_DestinationFeeManager *DestinationFeeManagerSession) AcceptOwnership() (*types.Transaction, error) { + return _DestinationFeeManager.Contract.AcceptOwnership(&_DestinationFeeManager.TransactOpts) +} + +func (_DestinationFeeManager *DestinationFeeManagerTransactorSession) AcceptOwnership() (*types.Transaction, error) { + return _DestinationFeeManager.Contract.AcceptOwnership(&_DestinationFeeManager.TransactOpts) +} + +func (_DestinationFeeManager *DestinationFeeManagerTransactor) AddVerifier(opts *bind.TransactOpts, verifierAddress common.Address) (*types.Transaction, error) { + return _DestinationFeeManager.contract.Transact(opts, "addVerifier", verifierAddress) +} + +func (_DestinationFeeManager *DestinationFeeManagerSession) AddVerifier(verifierAddress common.Address) (*types.Transaction, error) { + return _DestinationFeeManager.Contract.AddVerifier(&_DestinationFeeManager.TransactOpts, verifierAddress) +} + +func (_DestinationFeeManager *DestinationFeeManagerTransactorSession) AddVerifier(verifierAddress common.Address) (*types.Transaction, error) { + return _DestinationFeeManager.Contract.AddVerifier(&_DestinationFeeManager.TransactOpts, verifierAddress) +} + +func (_DestinationFeeManager *DestinationFeeManagerTransactor) PayLinkDeficit(opts *bind.TransactOpts, configDigest [32]byte) (*types.Transaction, error) { + return _DestinationFeeManager.contract.Transact(opts, "payLinkDeficit", configDigest) +} + +func (_DestinationFeeManager *DestinationFeeManagerSession) PayLinkDeficit(configDigest [32]byte) (*types.Transaction, error) { + return _DestinationFeeManager.Contract.PayLinkDeficit(&_DestinationFeeManager.TransactOpts, configDigest) +} + +func (_DestinationFeeManager *DestinationFeeManagerTransactorSession) PayLinkDeficit(configDigest [32]byte) (*types.Transaction, error) { + return _DestinationFeeManager.Contract.PayLinkDeficit(&_DestinationFeeManager.TransactOpts, configDigest) +} + +func (_DestinationFeeManager *DestinationFeeManagerTransactor) ProcessFee(opts *bind.TransactOpts, recipient [32]byte, payload []byte, parameterPayload []byte, subscriber common.Address) (*types.Transaction, error) { + return _DestinationFeeManager.contract.Transact(opts, "processFee", recipient, payload, parameterPayload, subscriber) +} + +func (_DestinationFeeManager *DestinationFeeManagerSession) ProcessFee(recipient [32]byte, payload []byte, parameterPayload []byte, subscriber common.Address) (*types.Transaction, error) { + return _DestinationFeeManager.Contract.ProcessFee(&_DestinationFeeManager.TransactOpts, recipient, payload, parameterPayload, subscriber) +} + +func (_DestinationFeeManager *DestinationFeeManagerTransactorSession) ProcessFee(recipient [32]byte, payload []byte, parameterPayload []byte, subscriber common.Address) (*types.Transaction, error) { + return _DestinationFeeManager.Contract.ProcessFee(&_DestinationFeeManager.TransactOpts, recipient, payload, parameterPayload, subscriber) +} + +func (_DestinationFeeManager *DestinationFeeManagerTransactor) ProcessFeeBulk(opts *bind.TransactOpts, poolIds [][32]byte, payloads [][]byte, parameterPayload []byte, subscriber common.Address) (*types.Transaction, error) { + return _DestinationFeeManager.contract.Transact(opts, "processFeeBulk", poolIds, payloads, parameterPayload, subscriber) +} + +func (_DestinationFeeManager *DestinationFeeManagerSession) ProcessFeeBulk(poolIds [][32]byte, payloads [][]byte, parameterPayload []byte, subscriber common.Address) (*types.Transaction, error) { + return _DestinationFeeManager.Contract.ProcessFeeBulk(&_DestinationFeeManager.TransactOpts, poolIds, payloads, parameterPayload, subscriber) +} + +func (_DestinationFeeManager *DestinationFeeManagerTransactorSession) ProcessFeeBulk(poolIds [][32]byte, payloads [][]byte, parameterPayload []byte, subscriber common.Address) (*types.Transaction, error) { + return _DestinationFeeManager.Contract.ProcessFeeBulk(&_DestinationFeeManager.TransactOpts, poolIds, payloads, parameterPayload, subscriber) +} + +func (_DestinationFeeManager *DestinationFeeManagerTransactor) RemoveVerifier(opts *bind.TransactOpts, verifierAddress common.Address) (*types.Transaction, error) { + return _DestinationFeeManager.contract.Transact(opts, "removeVerifier", verifierAddress) +} + +func (_DestinationFeeManager *DestinationFeeManagerSession) RemoveVerifier(verifierAddress common.Address) (*types.Transaction, error) { + return _DestinationFeeManager.Contract.RemoveVerifier(&_DestinationFeeManager.TransactOpts, verifierAddress) +} + +func (_DestinationFeeManager *DestinationFeeManagerTransactorSession) RemoveVerifier(verifierAddress common.Address) (*types.Transaction, error) { + return _DestinationFeeManager.Contract.RemoveVerifier(&_DestinationFeeManager.TransactOpts, verifierAddress) +} + +func (_DestinationFeeManager *DestinationFeeManagerTransactor) SetFeeRecipients(opts *bind.TransactOpts, configDigest [32]byte, rewardRecipientAndWeights []CommonAddressAndWeight) (*types.Transaction, error) { + return _DestinationFeeManager.contract.Transact(opts, "setFeeRecipients", configDigest, rewardRecipientAndWeights) +} + +func (_DestinationFeeManager *DestinationFeeManagerSession) SetFeeRecipients(configDigest [32]byte, rewardRecipientAndWeights []CommonAddressAndWeight) (*types.Transaction, error) { + return _DestinationFeeManager.Contract.SetFeeRecipients(&_DestinationFeeManager.TransactOpts, configDigest, rewardRecipientAndWeights) +} + +func (_DestinationFeeManager *DestinationFeeManagerTransactorSession) SetFeeRecipients(configDigest [32]byte, rewardRecipientAndWeights []CommonAddressAndWeight) (*types.Transaction, error) { + return _DestinationFeeManager.Contract.SetFeeRecipients(&_DestinationFeeManager.TransactOpts, configDigest, rewardRecipientAndWeights) +} + +func (_DestinationFeeManager *DestinationFeeManagerTransactor) SetNativeSurcharge(opts *bind.TransactOpts, surcharge uint64) (*types.Transaction, error) { + return _DestinationFeeManager.contract.Transact(opts, "setNativeSurcharge", surcharge) +} + +func (_DestinationFeeManager *DestinationFeeManagerSession) SetNativeSurcharge(surcharge uint64) (*types.Transaction, error) { + return _DestinationFeeManager.Contract.SetNativeSurcharge(&_DestinationFeeManager.TransactOpts, surcharge) +} + +func (_DestinationFeeManager *DestinationFeeManagerTransactorSession) SetNativeSurcharge(surcharge uint64) (*types.Transaction, error) { + return _DestinationFeeManager.Contract.SetNativeSurcharge(&_DestinationFeeManager.TransactOpts, surcharge) +} + +func (_DestinationFeeManager *DestinationFeeManagerTransactor) SetRewardManager(opts *bind.TransactOpts, rewardManagerAddress common.Address) (*types.Transaction, error) { + return _DestinationFeeManager.contract.Transact(opts, "setRewardManager", rewardManagerAddress) +} + +func (_DestinationFeeManager *DestinationFeeManagerSession) SetRewardManager(rewardManagerAddress common.Address) (*types.Transaction, error) { + return _DestinationFeeManager.Contract.SetRewardManager(&_DestinationFeeManager.TransactOpts, rewardManagerAddress) +} + +func (_DestinationFeeManager *DestinationFeeManagerTransactorSession) SetRewardManager(rewardManagerAddress common.Address) (*types.Transaction, error) { + return _DestinationFeeManager.Contract.SetRewardManager(&_DestinationFeeManager.TransactOpts, rewardManagerAddress) +} + +func (_DestinationFeeManager *DestinationFeeManagerTransactor) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { + return _DestinationFeeManager.contract.Transact(opts, "transferOwnership", to) +} + +func (_DestinationFeeManager *DestinationFeeManagerSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _DestinationFeeManager.Contract.TransferOwnership(&_DestinationFeeManager.TransactOpts, to) +} + +func (_DestinationFeeManager *DestinationFeeManagerTransactorSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _DestinationFeeManager.Contract.TransferOwnership(&_DestinationFeeManager.TransactOpts, to) +} + +func (_DestinationFeeManager *DestinationFeeManagerTransactor) UpdateSubscriberDiscount(opts *bind.TransactOpts, subscriber common.Address, feedId [32]byte, token common.Address, discount uint64) (*types.Transaction, error) { + return _DestinationFeeManager.contract.Transact(opts, "updateSubscriberDiscount", subscriber, feedId, token, discount) +} + +func (_DestinationFeeManager *DestinationFeeManagerSession) UpdateSubscriberDiscount(subscriber common.Address, feedId [32]byte, token common.Address, discount uint64) (*types.Transaction, error) { + return _DestinationFeeManager.Contract.UpdateSubscriberDiscount(&_DestinationFeeManager.TransactOpts, subscriber, feedId, token, discount) +} + +func (_DestinationFeeManager *DestinationFeeManagerTransactorSession) UpdateSubscriberDiscount(subscriber common.Address, feedId [32]byte, token common.Address, discount uint64) (*types.Transaction, error) { + return _DestinationFeeManager.Contract.UpdateSubscriberDiscount(&_DestinationFeeManager.TransactOpts, subscriber, feedId, token, discount) +} + +func (_DestinationFeeManager *DestinationFeeManagerTransactor) Withdraw(opts *bind.TransactOpts, assetAddress common.Address, recipient common.Address, quantity *big.Int) (*types.Transaction, error) { + return _DestinationFeeManager.contract.Transact(opts, "withdraw", assetAddress, recipient, quantity) +} + +func (_DestinationFeeManager *DestinationFeeManagerSession) Withdraw(assetAddress common.Address, recipient common.Address, quantity *big.Int) (*types.Transaction, error) { + return _DestinationFeeManager.Contract.Withdraw(&_DestinationFeeManager.TransactOpts, assetAddress, recipient, quantity) +} + +func (_DestinationFeeManager *DestinationFeeManagerTransactorSession) Withdraw(assetAddress common.Address, recipient common.Address, quantity *big.Int) (*types.Transaction, error) { + return _DestinationFeeManager.Contract.Withdraw(&_DestinationFeeManager.TransactOpts, assetAddress, recipient, quantity) +} + +type DestinationFeeManagerDiscountAppliedIterator struct { + Event *DestinationFeeManagerDiscountApplied + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *DestinationFeeManagerDiscountAppliedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(DestinationFeeManagerDiscountApplied) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(DestinationFeeManagerDiscountApplied) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *DestinationFeeManagerDiscountAppliedIterator) Error() error { + return it.fail +} + +func (it *DestinationFeeManagerDiscountAppliedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type DestinationFeeManagerDiscountApplied struct { + ConfigDigest [32]byte + Subscriber common.Address + Fee CommonAsset + Reward CommonAsset + AppliedDiscount *big.Int + Raw types.Log +} + +func (_DestinationFeeManager *DestinationFeeManagerFilterer) FilterDiscountApplied(opts *bind.FilterOpts, configDigest [][32]byte, subscriber []common.Address) (*DestinationFeeManagerDiscountAppliedIterator, error) { + + var configDigestRule []interface{} + for _, configDigestItem := range configDigest { + configDigestRule = append(configDigestRule, configDigestItem) + } + var subscriberRule []interface{} + for _, subscriberItem := range subscriber { + subscriberRule = append(subscriberRule, subscriberItem) + } + + logs, sub, err := _DestinationFeeManager.contract.FilterLogs(opts, "DiscountApplied", configDigestRule, subscriberRule) + if err != nil { + return nil, err + } + return &DestinationFeeManagerDiscountAppliedIterator{contract: _DestinationFeeManager.contract, event: "DiscountApplied", logs: logs, sub: sub}, nil +} + +func (_DestinationFeeManager *DestinationFeeManagerFilterer) WatchDiscountApplied(opts *bind.WatchOpts, sink chan<- *DestinationFeeManagerDiscountApplied, configDigest [][32]byte, subscriber []common.Address) (event.Subscription, error) { + + var configDigestRule []interface{} + for _, configDigestItem := range configDigest { + configDigestRule = append(configDigestRule, configDigestItem) + } + var subscriberRule []interface{} + for _, subscriberItem := range subscriber { + subscriberRule = append(subscriberRule, subscriberItem) + } + + logs, sub, err := _DestinationFeeManager.contract.WatchLogs(opts, "DiscountApplied", configDigestRule, subscriberRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(DestinationFeeManagerDiscountApplied) + if err := _DestinationFeeManager.contract.UnpackLog(event, "DiscountApplied", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_DestinationFeeManager *DestinationFeeManagerFilterer) ParseDiscountApplied(log types.Log) (*DestinationFeeManagerDiscountApplied, error) { + event := new(DestinationFeeManagerDiscountApplied) + if err := _DestinationFeeManager.contract.UnpackLog(event, "DiscountApplied", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type DestinationFeeManagerInsufficientLinkIterator struct { + Event *DestinationFeeManagerInsufficientLink + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *DestinationFeeManagerInsufficientLinkIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(DestinationFeeManagerInsufficientLink) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(DestinationFeeManagerInsufficientLink) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *DestinationFeeManagerInsufficientLinkIterator) Error() error { + return it.fail +} + +func (it *DestinationFeeManagerInsufficientLinkIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type DestinationFeeManagerInsufficientLink struct { + Rewards []IDestinationRewardManagerFeePayment + Raw types.Log +} + +func (_DestinationFeeManager *DestinationFeeManagerFilterer) FilterInsufficientLink(opts *bind.FilterOpts) (*DestinationFeeManagerInsufficientLinkIterator, error) { + + logs, sub, err := _DestinationFeeManager.contract.FilterLogs(opts, "InsufficientLink") + if err != nil { + return nil, err + } + return &DestinationFeeManagerInsufficientLinkIterator{contract: _DestinationFeeManager.contract, event: "InsufficientLink", logs: logs, sub: sub}, nil +} + +func (_DestinationFeeManager *DestinationFeeManagerFilterer) WatchInsufficientLink(opts *bind.WatchOpts, sink chan<- *DestinationFeeManagerInsufficientLink) (event.Subscription, error) { + + logs, sub, err := _DestinationFeeManager.contract.WatchLogs(opts, "InsufficientLink") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(DestinationFeeManagerInsufficientLink) + if err := _DestinationFeeManager.contract.UnpackLog(event, "InsufficientLink", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_DestinationFeeManager *DestinationFeeManagerFilterer) ParseInsufficientLink(log types.Log) (*DestinationFeeManagerInsufficientLink, error) { + event := new(DestinationFeeManagerInsufficientLink) + if err := _DestinationFeeManager.contract.UnpackLog(event, "InsufficientLink", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type DestinationFeeManagerLinkDeficitClearedIterator struct { + Event *DestinationFeeManagerLinkDeficitCleared + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *DestinationFeeManagerLinkDeficitClearedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(DestinationFeeManagerLinkDeficitCleared) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(DestinationFeeManagerLinkDeficitCleared) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *DestinationFeeManagerLinkDeficitClearedIterator) Error() error { + return it.fail +} + +func (it *DestinationFeeManagerLinkDeficitClearedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type DestinationFeeManagerLinkDeficitCleared struct { + ConfigDigest [32]byte + LinkQuantity *big.Int + Raw types.Log +} + +func (_DestinationFeeManager *DestinationFeeManagerFilterer) FilterLinkDeficitCleared(opts *bind.FilterOpts, configDigest [][32]byte) (*DestinationFeeManagerLinkDeficitClearedIterator, error) { + + var configDigestRule []interface{} + for _, configDigestItem := range configDigest { + configDigestRule = append(configDigestRule, configDigestItem) + } + + logs, sub, err := _DestinationFeeManager.contract.FilterLogs(opts, "LinkDeficitCleared", configDigestRule) + if err != nil { + return nil, err + } + return &DestinationFeeManagerLinkDeficitClearedIterator{contract: _DestinationFeeManager.contract, event: "LinkDeficitCleared", logs: logs, sub: sub}, nil +} + +func (_DestinationFeeManager *DestinationFeeManagerFilterer) WatchLinkDeficitCleared(opts *bind.WatchOpts, sink chan<- *DestinationFeeManagerLinkDeficitCleared, configDigest [][32]byte) (event.Subscription, error) { + + var configDigestRule []interface{} + for _, configDigestItem := range configDigest { + configDigestRule = append(configDigestRule, configDigestItem) + } + + logs, sub, err := _DestinationFeeManager.contract.WatchLogs(opts, "LinkDeficitCleared", configDigestRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(DestinationFeeManagerLinkDeficitCleared) + if err := _DestinationFeeManager.contract.UnpackLog(event, "LinkDeficitCleared", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_DestinationFeeManager *DestinationFeeManagerFilterer) ParseLinkDeficitCleared(log types.Log) (*DestinationFeeManagerLinkDeficitCleared, error) { + event := new(DestinationFeeManagerLinkDeficitCleared) + if err := _DestinationFeeManager.contract.UnpackLog(event, "LinkDeficitCleared", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type DestinationFeeManagerNativeSurchargeUpdatedIterator struct { + Event *DestinationFeeManagerNativeSurchargeUpdated + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *DestinationFeeManagerNativeSurchargeUpdatedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(DestinationFeeManagerNativeSurchargeUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(DestinationFeeManagerNativeSurchargeUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *DestinationFeeManagerNativeSurchargeUpdatedIterator) Error() error { + return it.fail +} + +func (it *DestinationFeeManagerNativeSurchargeUpdatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type DestinationFeeManagerNativeSurchargeUpdated struct { + NewSurcharge uint64 + Raw types.Log +} + +func (_DestinationFeeManager *DestinationFeeManagerFilterer) FilterNativeSurchargeUpdated(opts *bind.FilterOpts) (*DestinationFeeManagerNativeSurchargeUpdatedIterator, error) { + + logs, sub, err := _DestinationFeeManager.contract.FilterLogs(opts, "NativeSurchargeUpdated") + if err != nil { + return nil, err + } + return &DestinationFeeManagerNativeSurchargeUpdatedIterator{contract: _DestinationFeeManager.contract, event: "NativeSurchargeUpdated", logs: logs, sub: sub}, nil +} + +func (_DestinationFeeManager *DestinationFeeManagerFilterer) WatchNativeSurchargeUpdated(opts *bind.WatchOpts, sink chan<- *DestinationFeeManagerNativeSurchargeUpdated) (event.Subscription, error) { + + logs, sub, err := _DestinationFeeManager.contract.WatchLogs(opts, "NativeSurchargeUpdated") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(DestinationFeeManagerNativeSurchargeUpdated) + if err := _DestinationFeeManager.contract.UnpackLog(event, "NativeSurchargeUpdated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_DestinationFeeManager *DestinationFeeManagerFilterer) ParseNativeSurchargeUpdated(log types.Log) (*DestinationFeeManagerNativeSurchargeUpdated, error) { + event := new(DestinationFeeManagerNativeSurchargeUpdated) + if err := _DestinationFeeManager.contract.UnpackLog(event, "NativeSurchargeUpdated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type DestinationFeeManagerOwnershipTransferRequestedIterator struct { + Event *DestinationFeeManagerOwnershipTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *DestinationFeeManagerOwnershipTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(DestinationFeeManagerOwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(DestinationFeeManagerOwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *DestinationFeeManagerOwnershipTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *DestinationFeeManagerOwnershipTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type DestinationFeeManagerOwnershipTransferRequested struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_DestinationFeeManager *DestinationFeeManagerFilterer) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*DestinationFeeManagerOwnershipTransferRequestedIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _DestinationFeeManager.contract.FilterLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return &DestinationFeeManagerOwnershipTransferRequestedIterator{contract: _DestinationFeeManager.contract, event: "OwnershipTransferRequested", logs: logs, sub: sub}, nil +} + +func (_DestinationFeeManager *DestinationFeeManagerFilterer) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *DestinationFeeManagerOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _DestinationFeeManager.contract.WatchLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(DestinationFeeManagerOwnershipTransferRequested) + if err := _DestinationFeeManager.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_DestinationFeeManager *DestinationFeeManagerFilterer) ParseOwnershipTransferRequested(log types.Log) (*DestinationFeeManagerOwnershipTransferRequested, error) { + event := new(DestinationFeeManagerOwnershipTransferRequested) + if err := _DestinationFeeManager.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type DestinationFeeManagerOwnershipTransferredIterator struct { + Event *DestinationFeeManagerOwnershipTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *DestinationFeeManagerOwnershipTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(DestinationFeeManagerOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(DestinationFeeManagerOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *DestinationFeeManagerOwnershipTransferredIterator) Error() error { + return it.fail +} + +func (it *DestinationFeeManagerOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type DestinationFeeManagerOwnershipTransferred struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_DestinationFeeManager *DestinationFeeManagerFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*DestinationFeeManagerOwnershipTransferredIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _DestinationFeeManager.contract.FilterLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return &DestinationFeeManagerOwnershipTransferredIterator{contract: _DestinationFeeManager.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +func (_DestinationFeeManager *DestinationFeeManagerFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *DestinationFeeManagerOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _DestinationFeeManager.contract.WatchLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(DestinationFeeManagerOwnershipTransferred) + if err := _DestinationFeeManager.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_DestinationFeeManager *DestinationFeeManagerFilterer) ParseOwnershipTransferred(log types.Log) (*DestinationFeeManagerOwnershipTransferred, error) { + event := new(DestinationFeeManagerOwnershipTransferred) + if err := _DestinationFeeManager.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type DestinationFeeManagerSubscriberDiscountUpdatedIterator struct { + Event *DestinationFeeManagerSubscriberDiscountUpdated + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *DestinationFeeManagerSubscriberDiscountUpdatedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(DestinationFeeManagerSubscriberDiscountUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(DestinationFeeManagerSubscriberDiscountUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *DestinationFeeManagerSubscriberDiscountUpdatedIterator) Error() error { + return it.fail +} + +func (it *DestinationFeeManagerSubscriberDiscountUpdatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type DestinationFeeManagerSubscriberDiscountUpdated struct { + Subscriber common.Address + FeedId [32]byte + Token common.Address + Discount uint64 + Raw types.Log +} + +func (_DestinationFeeManager *DestinationFeeManagerFilterer) FilterSubscriberDiscountUpdated(opts *bind.FilterOpts, subscriber []common.Address, feedId [][32]byte) (*DestinationFeeManagerSubscriberDiscountUpdatedIterator, error) { + + var subscriberRule []interface{} + for _, subscriberItem := range subscriber { + subscriberRule = append(subscriberRule, subscriberItem) + } + var feedIdRule []interface{} + for _, feedIdItem := range feedId { + feedIdRule = append(feedIdRule, feedIdItem) + } + + logs, sub, err := _DestinationFeeManager.contract.FilterLogs(opts, "SubscriberDiscountUpdated", subscriberRule, feedIdRule) + if err != nil { + return nil, err + } + return &DestinationFeeManagerSubscriberDiscountUpdatedIterator{contract: _DestinationFeeManager.contract, event: "SubscriberDiscountUpdated", logs: logs, sub: sub}, nil +} + +func (_DestinationFeeManager *DestinationFeeManagerFilterer) WatchSubscriberDiscountUpdated(opts *bind.WatchOpts, sink chan<- *DestinationFeeManagerSubscriberDiscountUpdated, subscriber []common.Address, feedId [][32]byte) (event.Subscription, error) { + + var subscriberRule []interface{} + for _, subscriberItem := range subscriber { + subscriberRule = append(subscriberRule, subscriberItem) + } + var feedIdRule []interface{} + for _, feedIdItem := range feedId { + feedIdRule = append(feedIdRule, feedIdItem) + } + + logs, sub, err := _DestinationFeeManager.contract.WatchLogs(opts, "SubscriberDiscountUpdated", subscriberRule, feedIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(DestinationFeeManagerSubscriberDiscountUpdated) + if err := _DestinationFeeManager.contract.UnpackLog(event, "SubscriberDiscountUpdated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_DestinationFeeManager *DestinationFeeManagerFilterer) ParseSubscriberDiscountUpdated(log types.Log) (*DestinationFeeManagerSubscriberDiscountUpdated, error) { + event := new(DestinationFeeManagerSubscriberDiscountUpdated) + if err := _DestinationFeeManager.contract.UnpackLog(event, "SubscriberDiscountUpdated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type DestinationFeeManagerWithdrawIterator struct { + Event *DestinationFeeManagerWithdraw + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *DestinationFeeManagerWithdrawIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(DestinationFeeManagerWithdraw) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(DestinationFeeManagerWithdraw) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *DestinationFeeManagerWithdrawIterator) Error() error { + return it.fail +} + +func (it *DestinationFeeManagerWithdrawIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type DestinationFeeManagerWithdraw struct { + AdminAddress common.Address + Recipient common.Address + AssetAddress common.Address + Quantity *big.Int + Raw types.Log +} + +func (_DestinationFeeManager *DestinationFeeManagerFilterer) FilterWithdraw(opts *bind.FilterOpts) (*DestinationFeeManagerWithdrawIterator, error) { + + logs, sub, err := _DestinationFeeManager.contract.FilterLogs(opts, "Withdraw") + if err != nil { + return nil, err + } + return &DestinationFeeManagerWithdrawIterator{contract: _DestinationFeeManager.contract, event: "Withdraw", logs: logs, sub: sub}, nil +} + +func (_DestinationFeeManager *DestinationFeeManagerFilterer) WatchWithdraw(opts *bind.WatchOpts, sink chan<- *DestinationFeeManagerWithdraw) (event.Subscription, error) { + + logs, sub, err := _DestinationFeeManager.contract.WatchLogs(opts, "Withdraw") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(DestinationFeeManagerWithdraw) + if err := _DestinationFeeManager.contract.UnpackLog(event, "Withdraw", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_DestinationFeeManager *DestinationFeeManagerFilterer) ParseWithdraw(log types.Log) (*DestinationFeeManagerWithdraw, error) { + event := new(DestinationFeeManagerWithdraw) + if err := _DestinationFeeManager.contract.UnpackLog(event, "Withdraw", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +func (_DestinationFeeManager *DestinationFeeManager) ParseLog(log types.Log) (generated.AbigenLog, error) { + switch log.Topics[0] { + case _DestinationFeeManager.abi.Events["DiscountApplied"].ID: + return _DestinationFeeManager.ParseDiscountApplied(log) + case _DestinationFeeManager.abi.Events["InsufficientLink"].ID: + return _DestinationFeeManager.ParseInsufficientLink(log) + case _DestinationFeeManager.abi.Events["LinkDeficitCleared"].ID: + return _DestinationFeeManager.ParseLinkDeficitCleared(log) + case _DestinationFeeManager.abi.Events["NativeSurchargeUpdated"].ID: + return _DestinationFeeManager.ParseNativeSurchargeUpdated(log) + case _DestinationFeeManager.abi.Events["OwnershipTransferRequested"].ID: + return _DestinationFeeManager.ParseOwnershipTransferRequested(log) + case _DestinationFeeManager.abi.Events["OwnershipTransferred"].ID: + return _DestinationFeeManager.ParseOwnershipTransferred(log) + case _DestinationFeeManager.abi.Events["SubscriberDiscountUpdated"].ID: + return _DestinationFeeManager.ParseSubscriberDiscountUpdated(log) + case _DestinationFeeManager.abi.Events["Withdraw"].ID: + return _DestinationFeeManager.ParseWithdraw(log) + + default: + return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) + } +} + +func (DestinationFeeManagerDiscountApplied) Topic() common.Hash { + return common.HexToHash("0x88b15eb682210089cddf967648e2cb2a4535aeadc8f8f36050922e33c04e7125") +} + +func (DestinationFeeManagerInsufficientLink) Topic() common.Hash { + return common.HexToHash("0xf52e5907b69d97c33392936c12d78b494463b78c5b72df50b4c497eee5720b67") +} + +func (DestinationFeeManagerLinkDeficitCleared) Topic() common.Hash { + return common.HexToHash("0x843f0b103e50b42b08f9d30f12f961845a6d02623730872e24644899c0dd9895") +} + +func (DestinationFeeManagerNativeSurchargeUpdated) Topic() common.Hash { + return common.HexToHash("0x08f7c0d17932ddb8523bc06754d42ff19ebc77d76a8b9bfde02c28ab1ed3d639") +} + +func (DestinationFeeManagerOwnershipTransferRequested) Topic() common.Hash { + return common.HexToHash("0xed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278") +} + +func (DestinationFeeManagerOwnershipTransferred) Topic() common.Hash { + return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") +} + +func (DestinationFeeManagerSubscriberDiscountUpdated) Topic() common.Hash { + return common.HexToHash("0x5eba5a8afa39780f0f99b6cbeb95f3da6a7040ca00abd46bdc91a0a060134139") +} + +func (DestinationFeeManagerWithdraw) Topic() common.Hash { + return common.HexToHash("0x7ff78a71698bdb18dcca96f52ab25e0a1b146fb6a49adf8e6845299e49021f29") +} + +func (_DestinationFeeManager *DestinationFeeManager) Address() common.Address { + return _DestinationFeeManager.address +} + +type DestinationFeeManagerInterface interface { + GetFeeAndReward(opts *bind.CallOpts, subscriber common.Address, report []byte, quoteAddress common.Address) (CommonAsset, CommonAsset, *big.Int, error) + + ILinkAddress(opts *bind.CallOpts) (common.Address, error) + + INativeAddress(opts *bind.CallOpts) (common.Address, error) + + IRewardManager(opts *bind.CallOpts) (common.Address, error) + + LinkAvailableForPayment(opts *bind.CallOpts) (*big.Int, error) + + Owner(opts *bind.CallOpts) (common.Address, error) + + SLinkDeficit(opts *bind.CallOpts, arg0 [32]byte) (*big.Int, error) + + SNativeSurcharge(opts *bind.CallOpts) (*big.Int, error) + + SSubscriberDiscounts(opts *bind.CallOpts, arg0 common.Address, arg1 [32]byte, arg2 common.Address) (*big.Int, error) + + SVerifierAddressList(opts *bind.CallOpts, arg0 common.Address) (common.Address, error) + + SupportsInterface(opts *bind.CallOpts, interfaceId [4]byte) (bool, error) + + TypeAndVersion(opts *bind.CallOpts) (string, error) + + AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) + + AddVerifier(opts *bind.TransactOpts, verifierAddress common.Address) (*types.Transaction, error) + + PayLinkDeficit(opts *bind.TransactOpts, configDigest [32]byte) (*types.Transaction, error) + + ProcessFee(opts *bind.TransactOpts, recipient [32]byte, payload []byte, parameterPayload []byte, subscriber common.Address) (*types.Transaction, error) + + ProcessFeeBulk(opts *bind.TransactOpts, poolIds [][32]byte, payloads [][]byte, parameterPayload []byte, subscriber common.Address) (*types.Transaction, error) + + RemoveVerifier(opts *bind.TransactOpts, verifierAddress common.Address) (*types.Transaction, error) + + SetFeeRecipients(opts *bind.TransactOpts, configDigest [32]byte, rewardRecipientAndWeights []CommonAddressAndWeight) (*types.Transaction, error) + + SetNativeSurcharge(opts *bind.TransactOpts, surcharge uint64) (*types.Transaction, error) + + SetRewardManager(opts *bind.TransactOpts, rewardManagerAddress common.Address) (*types.Transaction, error) + + TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + + UpdateSubscriberDiscount(opts *bind.TransactOpts, subscriber common.Address, feedId [32]byte, token common.Address, discount uint64) (*types.Transaction, error) + + Withdraw(opts *bind.TransactOpts, assetAddress common.Address, recipient common.Address, quantity *big.Int) (*types.Transaction, error) + + FilterDiscountApplied(opts *bind.FilterOpts, configDigest [][32]byte, subscriber []common.Address) (*DestinationFeeManagerDiscountAppliedIterator, error) + + WatchDiscountApplied(opts *bind.WatchOpts, sink chan<- *DestinationFeeManagerDiscountApplied, configDigest [][32]byte, subscriber []common.Address) (event.Subscription, error) + + ParseDiscountApplied(log types.Log) (*DestinationFeeManagerDiscountApplied, error) + + FilterInsufficientLink(opts *bind.FilterOpts) (*DestinationFeeManagerInsufficientLinkIterator, error) + + WatchInsufficientLink(opts *bind.WatchOpts, sink chan<- *DestinationFeeManagerInsufficientLink) (event.Subscription, error) + + ParseInsufficientLink(log types.Log) (*DestinationFeeManagerInsufficientLink, error) + + FilterLinkDeficitCleared(opts *bind.FilterOpts, configDigest [][32]byte) (*DestinationFeeManagerLinkDeficitClearedIterator, error) + + WatchLinkDeficitCleared(opts *bind.WatchOpts, sink chan<- *DestinationFeeManagerLinkDeficitCleared, configDigest [][32]byte) (event.Subscription, error) + + ParseLinkDeficitCleared(log types.Log) (*DestinationFeeManagerLinkDeficitCleared, error) + + FilterNativeSurchargeUpdated(opts *bind.FilterOpts) (*DestinationFeeManagerNativeSurchargeUpdatedIterator, error) + + WatchNativeSurchargeUpdated(opts *bind.WatchOpts, sink chan<- *DestinationFeeManagerNativeSurchargeUpdated) (event.Subscription, error) + + ParseNativeSurchargeUpdated(log types.Log) (*DestinationFeeManagerNativeSurchargeUpdated, error) + + FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*DestinationFeeManagerOwnershipTransferRequestedIterator, error) + + WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *DestinationFeeManagerOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferRequested(log types.Log) (*DestinationFeeManagerOwnershipTransferRequested, error) + + FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*DestinationFeeManagerOwnershipTransferredIterator, error) + + WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *DestinationFeeManagerOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferred(log types.Log) (*DestinationFeeManagerOwnershipTransferred, error) + + FilterSubscriberDiscountUpdated(opts *bind.FilterOpts, subscriber []common.Address, feedId [][32]byte) (*DestinationFeeManagerSubscriberDiscountUpdatedIterator, error) + + WatchSubscriberDiscountUpdated(opts *bind.WatchOpts, sink chan<- *DestinationFeeManagerSubscriberDiscountUpdated, subscriber []common.Address, feedId [][32]byte) (event.Subscription, error) + + ParseSubscriberDiscountUpdated(log types.Log) (*DestinationFeeManagerSubscriberDiscountUpdated, error) + + FilterWithdraw(opts *bind.FilterOpts) (*DestinationFeeManagerWithdrawIterator, error) + + WatchWithdraw(opts *bind.WatchOpts, sink chan<- *DestinationFeeManagerWithdraw) (event.Subscription, error) + + ParseWithdraw(log types.Log) (*DestinationFeeManagerWithdraw, error) + + ParseLog(log types.Log) (generated.AbigenLog, error) + + Address() common.Address +} diff --git a/core/gethwrappers/llo-feeds/generated/destination_reward_manager/destination_reward_manager.go b/core/gethwrappers/llo-feeds/generated/destination_reward_manager/destination_reward_manager.go new file mode 100644 index 0000000000..989482fc0e --- /dev/null +++ b/core/gethwrappers/llo-feeds/generated/destination_reward_manager/destination_reward_manager.go @@ -0,0 +1,1434 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package destination_reward_manager + +import ( + "errors" + "fmt" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" +) + +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +type CommonAddressAndWeight struct { + Addr common.Address + Weight uint64 +} + +type IDestinationRewardManagerFeePayment struct { + PoolId [32]byte + Amount *big.Int +} + +var DestinationRewardManagerMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"linkAddress\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"InvalidAddress\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidPoolId\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidPoolLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidWeights\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Unauthorized\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newFeeManagerAddress\",\"type\":\"address\"}],\"name\":\"FeeManagerUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"poolId\",\"type\":\"bytes32\"},{\"internalType\":\"uint192\",\"name\":\"amount\",\"type\":\"uint192\"}],\"indexed\":false,\"internalType\":\"structIDestinationRewardManager.FeePayment[]\",\"name\":\"payments\",\"type\":\"tuple[]\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"payer\",\"type\":\"address\"}],\"name\":\"FeePaid\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"poolId\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"weight\",\"type\":\"uint64\"}],\"indexed\":false,\"internalType\":\"structCommon.AddressAndWeight[]\",\"name\":\"newRewardRecipients\",\"type\":\"tuple[]\"}],\"name\":\"RewardRecipientsUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"poolId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint192\",\"name\":\"quantity\",\"type\":\"uint192\"}],\"name\":\"RewardsClaimed\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newFeeManagerAddress\",\"type\":\"address\"}],\"name\":\"addFeeManager\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"poolIds\",\"type\":\"bytes32[]\"}],\"name\":\"claimRewards\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"startIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"endIndex\",\"type\":\"uint256\"}],\"name\":\"getAvailableRewardPoolIds\",\"outputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"i_linkAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"poolId\",\"type\":\"bytes32\"},{\"internalType\":\"uint192\",\"name\":\"amount\",\"type\":\"uint192\"}],\"internalType\":\"structIDestinationRewardManager.FeePayment[]\",\"name\":\"payments\",\"type\":\"tuple[]\"},{\"internalType\":\"address\",\"name\":\"payer\",\"type\":\"address\"}],\"name\":\"onFeePaid\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"poolId\",\"type\":\"bytes32\"},{\"internalType\":\"address[]\",\"name\":\"recipients\",\"type\":\"address[]\"}],\"name\":\"payRecipients\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"feeManagerAddress\",\"type\":\"address\"}],\"name\":\"removeFeeManager\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"s_feeManagerAddressList\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"s_registeredPoolIds\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"s_rewardRecipientWeights\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"s_rewardRecipientWeightsSet\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"s_totalRewardRecipientFees\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"s_totalRewardRecipientFeesLastClaimedAmounts\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"poolId\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"weight\",\"type\":\"uint64\"}],\"internalType\":\"structCommon.AddressAndWeight[]\",\"name\":\"rewardRecipientAndWeights\",\"type\":\"tuple[]\"}],\"name\":\"setRewardRecipients\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"poolId\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"weight\",\"type\":\"uint64\"}],\"internalType\":\"structCommon.AddressAndWeight[]\",\"name\":\"newRewardRecipients\",\"type\":\"tuple[]\"}],\"name\":\"updateRewardRecipients\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60a06040523480156200001157600080fd5b506040516200244c3803806200244c8339810160408190526200003491620001a6565b33806000816200008b5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000be57620000be81620000fb565b5050506001600160a01b038116620000e95760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b0316608052620001d8565b336001600160a01b03821603620001555760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000082565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600060208284031215620001b957600080fd5b81516001600160a01b0381168114620001d157600080fd5b9392505050565b60805161224a62000202600039600081816103bb0152818161107601526112b1015261224a6000f3fe608060405234801561001057600080fd5b506004361061016c5760003560e01c806360122608116100cd5780639604d05f11610081578063cd5f729211610066578063cd5f7292146103a3578063ea4b861b146103b6578063f2fde38b146103dd57600080fd5b80639604d05f1461035a578063b0d9fa191461039057600080fd5b80638115c9cc116100b25780638115c9cc146102dd5780638ac85a5c146102f05780638da5cb5b1461031b57600080fd5b806360122608146102aa57806379ba5097146102d557600080fd5b806339ee81e1116101245780634944832f116101095780634944832f146102615780634d32208414610274578063592562011461028757600080fd5b806339ee81e114610213578063472264751461024157600080fd5b806314060f231161015557806314060f23146101ae578063181f5a77146101c15780631f2d32c31461020057600080fd5b806301ffc9a7146101715780630f3c34d114610199575b600080fd5b61018461017f366004611b3d565b6103f0565b60405190151581526020015b60405180910390f35b6101ac6101a7366004611bfd565b610651565b005b6101ac6101bc366004611cef565b61065f565b604080518082018252601381527f5265776172644d616e6167657220312e302e3000000000000000000000000000602082015290516101909190611d5f565b6101ac61020e366004611dd9565b610821565b610233610221366004611df4565b60026020526000908152604090205481565b604051908152602001610190565b61025461024f366004611e0d565b610959565b6040516101909190611e40565b6101ac61026f366004611cef565b610ae3565b6101ac610282366004611e84565b610c2c565b610184610295366004611df4565b60056020526000908152604090205460ff1681565b6102336102b8366004611f03565b600360209081526000928352604080842090915290825290205481565b6101ac610d6b565b6101ac6102eb366004611dd9565b610e6d565b6102336102fe366004611f03565b600460209081526000928352604080842090915290825290205481565b60005473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610190565b610335610368366004611dd9565b60076020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b6101ac61039e366004611f2f565b610f1f565b6102336103b1366004611df4565b6110df565b6103357f000000000000000000000000000000000000000000000000000000000000000081565b6101ac6103eb366004611dd9565b611100565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f0f3c34d100000000000000000000000000000000000000000000000000000000148061048357507fffffffff0000000000000000000000000000000000000000000000000000000082167f14060f2300000000000000000000000000000000000000000000000000000000145b806104cf57507fffffffff0000000000000000000000000000000000000000000000000000000082167f4944832f00000000000000000000000000000000000000000000000000000000145b8061051b57507fffffffff0000000000000000000000000000000000000000000000000000000082167f4d32208400000000000000000000000000000000000000000000000000000000145b8061056757507fffffffff0000000000000000000000000000000000000000000000000000000082167f1f2d32c300000000000000000000000000000000000000000000000000000000145b806105b357507fffffffff0000000000000000000000000000000000000000000000000000000082167f8115c9cc00000000000000000000000000000000000000000000000000000000145b806105ff57507fffffffff0000000000000000000000000000000000000000000000000000000082167f4722647500000000000000000000000000000000000000000000000000000000145b8061064b57507fffffffff0000000000000000000000000000000000000000000000000000000082167fb0d9fa1900000000000000000000000000000000000000000000000000000000145b92915050565b61065b3382611114565b5050565b60005473ffffffffffffffffffffffffffffffffffffffff1633148015906106ab57503360008181526007602052604090205473ffffffffffffffffffffffffffffffffffffffff1614155b156106e2576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600081900361071d576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526005602052604090205460ff1615610766576040517f0afa7ee800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6006805460018181019092557ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01849055600084815260056020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690911790556107e2838383670de0b6b3a76400006112df565b827f8f668d6090683f98b3373a8b83d214da45737f7486cb7de554cc07b54e61cfe68383604051610814929190611f9b565b60405180910390a2505050565b6108296114b6565b73ffffffffffffffffffffffffffffffffffffffff8116610876576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81811660009081526007602052604090205416156108d5576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff811660008181526007602090815260409182902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000168417905590519182527fe45f5e140399b0a7e12971ab020724b828fbed8ac408c420884dc7d1bbe506b4910160405180910390a150565b600654606090600081841161096e5783610970565b815b9050808511156109ac576040517fa22caccc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006109b88683612032565b67ffffffffffffffff8111156109d0576109d0611b7f565b6040519080825280602002602001820160405280156109f9578160200160208202803683370190505b5090506000865b83811015610ad657600060068281548110610a1d57610a1d612045565b600091825260208083209091015480835260048252604080842073ffffffffffffffffffffffffffffffffffffffff8f16855290925291205490915015610ac5576000818152600260209081526040808320546003835281842073ffffffffffffffffffffffffffffffffffffffff8f168552909252909120548114610ac35781858580600101965081518110610ab657610ab6612045565b6020026020010181815250505b505b50610acf81612074565b9050610a00565b5090979650505050505050565b610aeb6114b6565b604080516001808252818301909252600091602080830190803683370190505090508381600081518110610b2157610b21612045565b6020026020010181815250506000805b83811015610bde576000858583818110610b4d57610b4d612045565b610b639260206040909202019081019150611dd9565b600088815260046020908152604080832073ffffffffffffffffffffffffffffffffffffffff85168452909152902054909150610bc7878785818110610bab57610bab612045565b610bc19260206040909202019081019150611dd9565b86611114565b50929092019150610bd781612074565b9050610b31565b50610beb858585846112df565b847f8f668d6090683f98b3373a8b83d214da45737f7486cb7de554cc07b54e61cfe68585604051610c1d929190611f9b565b60405180910390a25050505050565b82610c4c60005473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614158015610c9e57506000818152600460209081526040808320338452909152902054155b15610cd5576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516001808252818301909252600091602080830190803683370190505090508481600081518110610d0b57610d0b612045565b60200260200101818152505060005b83811015610d6357610d52858583818110610d3757610d37612045565b9050602002016020810190610d4c9190611dd9565b83611114565b50610d5c81612074565b9050610d1a565b505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610df1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610e756114b6565b73ffffffffffffffffffffffffffffffffffffffff81811660009081526007602052604090205416610ed3576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff16600090815260076020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b3360008181526007602052604090205473ffffffffffffffffffffffffffffffffffffffff1614610f7c576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000805b8381101561105b57848482818110610f9a57610f9a612045565b9050604002016020016020810190610fb291906120d4565b77ffffffffffffffffffffffffffffffffffffffffffffffff1660026000878785818110610fe257610fe2612045565b604090810292909201358352506020820192909252016000208054909101905584848281811061101457611014612045565b905060400201602001602081019061102c91906120d4565b77ffffffffffffffffffffffffffffffffffffffffffffffff16820191508061105490612074565b9050610f80565b5061109e73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016833084611539565b7fa1cc025ea76bacce5d740ee4bc331899375dc2c5f2ab33933aaacbd9ba001b668484846040516110d1939291906120ef565b60405180910390a150505050565b600681815481106110ef57600080fd5b600091825260209091200154905081565b6111086114b6565b6111118161161b565b50565b60008060005b835181101561129057600084828151811061113757611137612045565b6020026020010151905060006002600083815260200190815260200160002054905080600003611168575050611280565b600082815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff8b16808552908352818420548685526004845282852091855292528220549083039190670de0b6b3a7640000908302049050806000036111d15750505050611280565b600084815260036020908152604080832073ffffffffffffffffffffffffffffffffffffffff8d16808552925290912084905588519682019689908790811061121c5761121c612045565b60200260200101517f989969655bc1d593922527fe85d71347bb8e12fa423cc71f362dd8ef7cb10ef283604051611273919077ffffffffffffffffffffffffffffffffffffffffffffffff91909116815260200190565b60405180910390a3505050505b61128981612074565b905061111a565b5080156112d8576112d873ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168583611710565b9392505050565b61133a8383808060200260200160405190810160405280939291908181526020016000905b828210156113305761132160408302860136819003810190612176565b81526020019060010190611304565b505050505061176b565b15611371576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000805b8381101561147557600085858381811061139157611391612045565b90506040020160200160208101906113a991906121d1565b67ffffffffffffffff16905060008686848181106113c9576113c9612045565b6113df9260206040909202019081019150611dd9565b905073ffffffffffffffffffffffffffffffffffffffff811661142e576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600088815260046020908152604080832073ffffffffffffffffffffffffffffffffffffffff909416835292905220819055919091019061146e81612074565b9050611375565b508181146114af576040517f84677ce800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314611537576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610de8565b565b60405173ffffffffffffffffffffffffffffffffffffffff808516602483015283166044820152606481018290526116159085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152611822565b50505050565b3373ffffffffffffffffffffffffffffffffffffffff82160361169a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610de8565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60405173ffffffffffffffffffffffffffffffffffffffff83166024820152604481018290526117669084907fa9059cbb0000000000000000000000000000000000000000000000000000000090606401611593565b505050565b6000805b82518110156118195760006117858260016121ec565b90505b8351811015611810578381815181106117a3576117a3612045565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff168483815181106117d7576117d7612045565b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff1603611808575060019392505050565b600101611788565b5060010161176f565b50600092915050565b6000611884826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff1661192e9092919063ffffffff16565b80519091501561176657808060200190518101906118a291906121ff565b611766576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610de8565b606061193d8484600085611945565b949350505050565b6060824710156119d7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610de8565b6000808673ffffffffffffffffffffffffffffffffffffffff168587604051611a009190612221565b60006040518083038185875af1925050503d8060008114611a3d576040519150601f19603f3d011682016040523d82523d6000602084013e611a42565b606091505b5091509150611a5387838387611a5e565b979650505050505050565b60608315611af4578251600003611aed5773ffffffffffffffffffffffffffffffffffffffff85163b611aed576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610de8565b508161193d565b61193d8383815115611b095781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610de89190611d5f565b600060208284031215611b4f57600080fd5b81357fffffffff00000000000000000000000000000000000000000000000000000000811681146112d857600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715611bf557611bf5611b7f565b604052919050565b60006020808385031215611c1057600080fd5b823567ffffffffffffffff80821115611c2857600080fd5b818501915085601f830112611c3c57600080fd5b813581811115611c4e57611c4e611b7f565b8060051b9150611c5f848301611bae565b8181529183018401918481019088841115611c7957600080fd5b938501935b83851015611c9757843582529385019390850190611c7e565b98975050505050505050565b60008083601f840112611cb557600080fd5b50813567ffffffffffffffff811115611ccd57600080fd5b6020830191508360208260061b8501011115611ce857600080fd5b9250929050565b600080600060408486031215611d0457600080fd5b83359250602084013567ffffffffffffffff811115611d2257600080fd5b611d2e86828701611ca3565b9497909650939450505050565b60005b83811015611d56578181015183820152602001611d3e565b50506000910152565b6020815260008251806020840152611d7e816040850160208701611d3b565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b803573ffffffffffffffffffffffffffffffffffffffff81168114611dd457600080fd5b919050565b600060208284031215611deb57600080fd5b6112d882611db0565b600060208284031215611e0657600080fd5b5035919050565b600080600060608486031215611e2257600080fd5b611e2b84611db0565b95602085013595506040909401359392505050565b6020808252825182820181905260009190848201906040850190845b81811015611e7857835183529284019291840191600101611e5c565b50909695505050505050565b600080600060408486031215611e9957600080fd5b83359250602084013567ffffffffffffffff80821115611eb857600080fd5b818601915086601f830112611ecc57600080fd5b813581811115611edb57600080fd5b8760208260051b8501011115611ef057600080fd5b6020830194508093505050509250925092565b60008060408385031215611f1657600080fd5b82359150611f2660208401611db0565b90509250929050565b600080600060408486031215611f4457600080fd5b833567ffffffffffffffff811115611f5b57600080fd5b611f6786828701611ca3565b9094509250611f7a905060208501611db0565b90509250925092565b803567ffffffffffffffff81168114611dd457600080fd5b6020808252818101839052600090604080840186845b87811015610ad65773ffffffffffffffffffffffffffffffffffffffff611fd783611db0565b16835267ffffffffffffffff611fee868401611f83565b16838601529183019190830190600101611fb1565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181038181111561064b5761064b612003565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036120a5576120a5612003565b5060010190565b803577ffffffffffffffffffffffffffffffffffffffffffffffff81168114611dd457600080fd5b6000602082840312156120e657600080fd5b6112d8826120ac565b60408082528181018490526000908560608401835b8781101561214b5782358252602077ffffffffffffffffffffffffffffffffffffffffffffffff6121368286016120ac565b16908301529183019190830190600101612104565b5080935050505073ffffffffffffffffffffffffffffffffffffffff83166020830152949350505050565b60006040828403121561218857600080fd5b6040516040810181811067ffffffffffffffff821117156121ab576121ab611b7f565b6040526121b783611db0565b81526121c560208401611f83565b60208201529392505050565b6000602082840312156121e357600080fd5b6112d882611f83565b8082018082111561064b5761064b612003565b60006020828403121561221157600080fd5b815180151581146112d857600080fd5b60008251612233818460208701611d3b565b919091019291505056fea164736f6c6343000813000a", +} + +var DestinationRewardManagerABI = DestinationRewardManagerMetaData.ABI + +var DestinationRewardManagerBin = DestinationRewardManagerMetaData.Bin + +func DeployDestinationRewardManager(auth *bind.TransactOpts, backend bind.ContractBackend, linkAddress common.Address) (common.Address, *types.Transaction, *DestinationRewardManager, error) { + parsed, err := DestinationRewardManagerMetaData.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(DestinationRewardManagerBin), backend, linkAddress) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &DestinationRewardManager{address: address, abi: *parsed, DestinationRewardManagerCaller: DestinationRewardManagerCaller{contract: contract}, DestinationRewardManagerTransactor: DestinationRewardManagerTransactor{contract: contract}, DestinationRewardManagerFilterer: DestinationRewardManagerFilterer{contract: contract}}, nil +} + +type DestinationRewardManager struct { + address common.Address + abi abi.ABI + DestinationRewardManagerCaller + DestinationRewardManagerTransactor + DestinationRewardManagerFilterer +} + +type DestinationRewardManagerCaller struct { + contract *bind.BoundContract +} + +type DestinationRewardManagerTransactor struct { + contract *bind.BoundContract +} + +type DestinationRewardManagerFilterer struct { + contract *bind.BoundContract +} + +type DestinationRewardManagerSession struct { + Contract *DestinationRewardManager + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type DestinationRewardManagerCallerSession struct { + Contract *DestinationRewardManagerCaller + CallOpts bind.CallOpts +} + +type DestinationRewardManagerTransactorSession struct { + Contract *DestinationRewardManagerTransactor + TransactOpts bind.TransactOpts +} + +type DestinationRewardManagerRaw struct { + Contract *DestinationRewardManager +} + +type DestinationRewardManagerCallerRaw struct { + Contract *DestinationRewardManagerCaller +} + +type DestinationRewardManagerTransactorRaw struct { + Contract *DestinationRewardManagerTransactor +} + +func NewDestinationRewardManager(address common.Address, backend bind.ContractBackend) (*DestinationRewardManager, error) { + abi, err := abi.JSON(strings.NewReader(DestinationRewardManagerABI)) + if err != nil { + return nil, err + } + contract, err := bindDestinationRewardManager(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &DestinationRewardManager{address: address, abi: abi, DestinationRewardManagerCaller: DestinationRewardManagerCaller{contract: contract}, DestinationRewardManagerTransactor: DestinationRewardManagerTransactor{contract: contract}, DestinationRewardManagerFilterer: DestinationRewardManagerFilterer{contract: contract}}, nil +} + +func NewDestinationRewardManagerCaller(address common.Address, caller bind.ContractCaller) (*DestinationRewardManagerCaller, error) { + contract, err := bindDestinationRewardManager(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &DestinationRewardManagerCaller{contract: contract}, nil +} + +func NewDestinationRewardManagerTransactor(address common.Address, transactor bind.ContractTransactor) (*DestinationRewardManagerTransactor, error) { + contract, err := bindDestinationRewardManager(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &DestinationRewardManagerTransactor{contract: contract}, nil +} + +func NewDestinationRewardManagerFilterer(address common.Address, filterer bind.ContractFilterer) (*DestinationRewardManagerFilterer, error) { + contract, err := bindDestinationRewardManager(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &DestinationRewardManagerFilterer{contract: contract}, nil +} + +func bindDestinationRewardManager(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := DestinationRewardManagerMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_DestinationRewardManager *DestinationRewardManagerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _DestinationRewardManager.Contract.DestinationRewardManagerCaller.contract.Call(opts, result, method, params...) +} + +func (_DestinationRewardManager *DestinationRewardManagerRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _DestinationRewardManager.Contract.DestinationRewardManagerTransactor.contract.Transfer(opts) +} + +func (_DestinationRewardManager *DestinationRewardManagerRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _DestinationRewardManager.Contract.DestinationRewardManagerTransactor.contract.Transact(opts, method, params...) +} + +func (_DestinationRewardManager *DestinationRewardManagerCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _DestinationRewardManager.Contract.contract.Call(opts, result, method, params...) +} + +func (_DestinationRewardManager *DestinationRewardManagerTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _DestinationRewardManager.Contract.contract.Transfer(opts) +} + +func (_DestinationRewardManager *DestinationRewardManagerTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _DestinationRewardManager.Contract.contract.Transact(opts, method, params...) +} + +func (_DestinationRewardManager *DestinationRewardManagerCaller) GetAvailableRewardPoolIds(opts *bind.CallOpts, recipient common.Address, startIndex *big.Int, endIndex *big.Int) ([][32]byte, error) { + var out []interface{} + err := _DestinationRewardManager.contract.Call(opts, &out, "getAvailableRewardPoolIds", recipient, startIndex, endIndex) + + if err != nil { + return *new([][32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([][32]byte)).(*[][32]byte) + + return out0, err + +} + +func (_DestinationRewardManager *DestinationRewardManagerSession) GetAvailableRewardPoolIds(recipient common.Address, startIndex *big.Int, endIndex *big.Int) ([][32]byte, error) { + return _DestinationRewardManager.Contract.GetAvailableRewardPoolIds(&_DestinationRewardManager.CallOpts, recipient, startIndex, endIndex) +} + +func (_DestinationRewardManager *DestinationRewardManagerCallerSession) GetAvailableRewardPoolIds(recipient common.Address, startIndex *big.Int, endIndex *big.Int) ([][32]byte, error) { + return _DestinationRewardManager.Contract.GetAvailableRewardPoolIds(&_DestinationRewardManager.CallOpts, recipient, startIndex, endIndex) +} + +func (_DestinationRewardManager *DestinationRewardManagerCaller) ILinkAddress(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _DestinationRewardManager.contract.Call(opts, &out, "i_linkAddress") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_DestinationRewardManager *DestinationRewardManagerSession) ILinkAddress() (common.Address, error) { + return _DestinationRewardManager.Contract.ILinkAddress(&_DestinationRewardManager.CallOpts) +} + +func (_DestinationRewardManager *DestinationRewardManagerCallerSession) ILinkAddress() (common.Address, error) { + return _DestinationRewardManager.Contract.ILinkAddress(&_DestinationRewardManager.CallOpts) +} + +func (_DestinationRewardManager *DestinationRewardManagerCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _DestinationRewardManager.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_DestinationRewardManager *DestinationRewardManagerSession) Owner() (common.Address, error) { + return _DestinationRewardManager.Contract.Owner(&_DestinationRewardManager.CallOpts) +} + +func (_DestinationRewardManager *DestinationRewardManagerCallerSession) Owner() (common.Address, error) { + return _DestinationRewardManager.Contract.Owner(&_DestinationRewardManager.CallOpts) +} + +func (_DestinationRewardManager *DestinationRewardManagerCaller) SFeeManagerAddressList(opts *bind.CallOpts, arg0 common.Address) (common.Address, error) { + var out []interface{} + err := _DestinationRewardManager.contract.Call(opts, &out, "s_feeManagerAddressList", arg0) + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_DestinationRewardManager *DestinationRewardManagerSession) SFeeManagerAddressList(arg0 common.Address) (common.Address, error) { + return _DestinationRewardManager.Contract.SFeeManagerAddressList(&_DestinationRewardManager.CallOpts, arg0) +} + +func (_DestinationRewardManager *DestinationRewardManagerCallerSession) SFeeManagerAddressList(arg0 common.Address) (common.Address, error) { + return _DestinationRewardManager.Contract.SFeeManagerAddressList(&_DestinationRewardManager.CallOpts, arg0) +} + +func (_DestinationRewardManager *DestinationRewardManagerCaller) SRegisteredPoolIds(opts *bind.CallOpts, arg0 *big.Int) ([32]byte, error) { + var out []interface{} + err := _DestinationRewardManager.contract.Call(opts, &out, "s_registeredPoolIds", arg0) + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +func (_DestinationRewardManager *DestinationRewardManagerSession) SRegisteredPoolIds(arg0 *big.Int) ([32]byte, error) { + return _DestinationRewardManager.Contract.SRegisteredPoolIds(&_DestinationRewardManager.CallOpts, arg0) +} + +func (_DestinationRewardManager *DestinationRewardManagerCallerSession) SRegisteredPoolIds(arg0 *big.Int) ([32]byte, error) { + return _DestinationRewardManager.Contract.SRegisteredPoolIds(&_DestinationRewardManager.CallOpts, arg0) +} + +func (_DestinationRewardManager *DestinationRewardManagerCaller) SRewardRecipientWeights(opts *bind.CallOpts, arg0 [32]byte, arg1 common.Address) (*big.Int, error) { + var out []interface{} + err := _DestinationRewardManager.contract.Call(opts, &out, "s_rewardRecipientWeights", arg0, arg1) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_DestinationRewardManager *DestinationRewardManagerSession) SRewardRecipientWeights(arg0 [32]byte, arg1 common.Address) (*big.Int, error) { + return _DestinationRewardManager.Contract.SRewardRecipientWeights(&_DestinationRewardManager.CallOpts, arg0, arg1) +} + +func (_DestinationRewardManager *DestinationRewardManagerCallerSession) SRewardRecipientWeights(arg0 [32]byte, arg1 common.Address) (*big.Int, error) { + return _DestinationRewardManager.Contract.SRewardRecipientWeights(&_DestinationRewardManager.CallOpts, arg0, arg1) +} + +func (_DestinationRewardManager *DestinationRewardManagerCaller) SRewardRecipientWeightsSet(opts *bind.CallOpts, arg0 [32]byte) (bool, error) { + var out []interface{} + err := _DestinationRewardManager.contract.Call(opts, &out, "s_rewardRecipientWeightsSet", arg0) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +func (_DestinationRewardManager *DestinationRewardManagerSession) SRewardRecipientWeightsSet(arg0 [32]byte) (bool, error) { + return _DestinationRewardManager.Contract.SRewardRecipientWeightsSet(&_DestinationRewardManager.CallOpts, arg0) +} + +func (_DestinationRewardManager *DestinationRewardManagerCallerSession) SRewardRecipientWeightsSet(arg0 [32]byte) (bool, error) { + return _DestinationRewardManager.Contract.SRewardRecipientWeightsSet(&_DestinationRewardManager.CallOpts, arg0) +} + +func (_DestinationRewardManager *DestinationRewardManagerCaller) STotalRewardRecipientFees(opts *bind.CallOpts, arg0 [32]byte) (*big.Int, error) { + var out []interface{} + err := _DestinationRewardManager.contract.Call(opts, &out, "s_totalRewardRecipientFees", arg0) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_DestinationRewardManager *DestinationRewardManagerSession) STotalRewardRecipientFees(arg0 [32]byte) (*big.Int, error) { + return _DestinationRewardManager.Contract.STotalRewardRecipientFees(&_DestinationRewardManager.CallOpts, arg0) +} + +func (_DestinationRewardManager *DestinationRewardManagerCallerSession) STotalRewardRecipientFees(arg0 [32]byte) (*big.Int, error) { + return _DestinationRewardManager.Contract.STotalRewardRecipientFees(&_DestinationRewardManager.CallOpts, arg0) +} + +func (_DestinationRewardManager *DestinationRewardManagerCaller) STotalRewardRecipientFeesLastClaimedAmounts(opts *bind.CallOpts, arg0 [32]byte, arg1 common.Address) (*big.Int, error) { + var out []interface{} + err := _DestinationRewardManager.contract.Call(opts, &out, "s_totalRewardRecipientFeesLastClaimedAmounts", arg0, arg1) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_DestinationRewardManager *DestinationRewardManagerSession) STotalRewardRecipientFeesLastClaimedAmounts(arg0 [32]byte, arg1 common.Address) (*big.Int, error) { + return _DestinationRewardManager.Contract.STotalRewardRecipientFeesLastClaimedAmounts(&_DestinationRewardManager.CallOpts, arg0, arg1) +} + +func (_DestinationRewardManager *DestinationRewardManagerCallerSession) STotalRewardRecipientFeesLastClaimedAmounts(arg0 [32]byte, arg1 common.Address) (*big.Int, error) { + return _DestinationRewardManager.Contract.STotalRewardRecipientFeesLastClaimedAmounts(&_DestinationRewardManager.CallOpts, arg0, arg1) +} + +func (_DestinationRewardManager *DestinationRewardManagerCaller) SupportsInterface(opts *bind.CallOpts, interfaceId [4]byte) (bool, error) { + var out []interface{} + err := _DestinationRewardManager.contract.Call(opts, &out, "supportsInterface", interfaceId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +func (_DestinationRewardManager *DestinationRewardManagerSession) SupportsInterface(interfaceId [4]byte) (bool, error) { + return _DestinationRewardManager.Contract.SupportsInterface(&_DestinationRewardManager.CallOpts, interfaceId) +} + +func (_DestinationRewardManager *DestinationRewardManagerCallerSession) SupportsInterface(interfaceId [4]byte) (bool, error) { + return _DestinationRewardManager.Contract.SupportsInterface(&_DestinationRewardManager.CallOpts, interfaceId) +} + +func (_DestinationRewardManager *DestinationRewardManagerCaller) TypeAndVersion(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _DestinationRewardManager.contract.Call(opts, &out, "typeAndVersion") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +func (_DestinationRewardManager *DestinationRewardManagerSession) TypeAndVersion() (string, error) { + return _DestinationRewardManager.Contract.TypeAndVersion(&_DestinationRewardManager.CallOpts) +} + +func (_DestinationRewardManager *DestinationRewardManagerCallerSession) TypeAndVersion() (string, error) { + return _DestinationRewardManager.Contract.TypeAndVersion(&_DestinationRewardManager.CallOpts) +} + +func (_DestinationRewardManager *DestinationRewardManagerTransactor) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _DestinationRewardManager.contract.Transact(opts, "acceptOwnership") +} + +func (_DestinationRewardManager *DestinationRewardManagerSession) AcceptOwnership() (*types.Transaction, error) { + return _DestinationRewardManager.Contract.AcceptOwnership(&_DestinationRewardManager.TransactOpts) +} + +func (_DestinationRewardManager *DestinationRewardManagerTransactorSession) AcceptOwnership() (*types.Transaction, error) { + return _DestinationRewardManager.Contract.AcceptOwnership(&_DestinationRewardManager.TransactOpts) +} + +func (_DestinationRewardManager *DestinationRewardManagerTransactor) AddFeeManager(opts *bind.TransactOpts, newFeeManagerAddress common.Address) (*types.Transaction, error) { + return _DestinationRewardManager.contract.Transact(opts, "addFeeManager", newFeeManagerAddress) +} + +func (_DestinationRewardManager *DestinationRewardManagerSession) AddFeeManager(newFeeManagerAddress common.Address) (*types.Transaction, error) { + return _DestinationRewardManager.Contract.AddFeeManager(&_DestinationRewardManager.TransactOpts, newFeeManagerAddress) +} + +func (_DestinationRewardManager *DestinationRewardManagerTransactorSession) AddFeeManager(newFeeManagerAddress common.Address) (*types.Transaction, error) { + return _DestinationRewardManager.Contract.AddFeeManager(&_DestinationRewardManager.TransactOpts, newFeeManagerAddress) +} + +func (_DestinationRewardManager *DestinationRewardManagerTransactor) ClaimRewards(opts *bind.TransactOpts, poolIds [][32]byte) (*types.Transaction, error) { + return _DestinationRewardManager.contract.Transact(opts, "claimRewards", poolIds) +} + +func (_DestinationRewardManager *DestinationRewardManagerSession) ClaimRewards(poolIds [][32]byte) (*types.Transaction, error) { + return _DestinationRewardManager.Contract.ClaimRewards(&_DestinationRewardManager.TransactOpts, poolIds) +} + +func (_DestinationRewardManager *DestinationRewardManagerTransactorSession) ClaimRewards(poolIds [][32]byte) (*types.Transaction, error) { + return _DestinationRewardManager.Contract.ClaimRewards(&_DestinationRewardManager.TransactOpts, poolIds) +} + +func (_DestinationRewardManager *DestinationRewardManagerTransactor) OnFeePaid(opts *bind.TransactOpts, payments []IDestinationRewardManagerFeePayment, payer common.Address) (*types.Transaction, error) { + return _DestinationRewardManager.contract.Transact(opts, "onFeePaid", payments, payer) +} + +func (_DestinationRewardManager *DestinationRewardManagerSession) OnFeePaid(payments []IDestinationRewardManagerFeePayment, payer common.Address) (*types.Transaction, error) { + return _DestinationRewardManager.Contract.OnFeePaid(&_DestinationRewardManager.TransactOpts, payments, payer) +} + +func (_DestinationRewardManager *DestinationRewardManagerTransactorSession) OnFeePaid(payments []IDestinationRewardManagerFeePayment, payer common.Address) (*types.Transaction, error) { + return _DestinationRewardManager.Contract.OnFeePaid(&_DestinationRewardManager.TransactOpts, payments, payer) +} + +func (_DestinationRewardManager *DestinationRewardManagerTransactor) PayRecipients(opts *bind.TransactOpts, poolId [32]byte, recipients []common.Address) (*types.Transaction, error) { + return _DestinationRewardManager.contract.Transact(opts, "payRecipients", poolId, recipients) +} + +func (_DestinationRewardManager *DestinationRewardManagerSession) PayRecipients(poolId [32]byte, recipients []common.Address) (*types.Transaction, error) { + return _DestinationRewardManager.Contract.PayRecipients(&_DestinationRewardManager.TransactOpts, poolId, recipients) +} + +func (_DestinationRewardManager *DestinationRewardManagerTransactorSession) PayRecipients(poolId [32]byte, recipients []common.Address) (*types.Transaction, error) { + return _DestinationRewardManager.Contract.PayRecipients(&_DestinationRewardManager.TransactOpts, poolId, recipients) +} + +func (_DestinationRewardManager *DestinationRewardManagerTransactor) RemoveFeeManager(opts *bind.TransactOpts, feeManagerAddress common.Address) (*types.Transaction, error) { + return _DestinationRewardManager.contract.Transact(opts, "removeFeeManager", feeManagerAddress) +} + +func (_DestinationRewardManager *DestinationRewardManagerSession) RemoveFeeManager(feeManagerAddress common.Address) (*types.Transaction, error) { + return _DestinationRewardManager.Contract.RemoveFeeManager(&_DestinationRewardManager.TransactOpts, feeManagerAddress) +} + +func (_DestinationRewardManager *DestinationRewardManagerTransactorSession) RemoveFeeManager(feeManagerAddress common.Address) (*types.Transaction, error) { + return _DestinationRewardManager.Contract.RemoveFeeManager(&_DestinationRewardManager.TransactOpts, feeManagerAddress) +} + +func (_DestinationRewardManager *DestinationRewardManagerTransactor) SetRewardRecipients(opts *bind.TransactOpts, poolId [32]byte, rewardRecipientAndWeights []CommonAddressAndWeight) (*types.Transaction, error) { + return _DestinationRewardManager.contract.Transact(opts, "setRewardRecipients", poolId, rewardRecipientAndWeights) +} + +func (_DestinationRewardManager *DestinationRewardManagerSession) SetRewardRecipients(poolId [32]byte, rewardRecipientAndWeights []CommonAddressAndWeight) (*types.Transaction, error) { + return _DestinationRewardManager.Contract.SetRewardRecipients(&_DestinationRewardManager.TransactOpts, poolId, rewardRecipientAndWeights) +} + +func (_DestinationRewardManager *DestinationRewardManagerTransactorSession) SetRewardRecipients(poolId [32]byte, rewardRecipientAndWeights []CommonAddressAndWeight) (*types.Transaction, error) { + return _DestinationRewardManager.Contract.SetRewardRecipients(&_DestinationRewardManager.TransactOpts, poolId, rewardRecipientAndWeights) +} + +func (_DestinationRewardManager *DestinationRewardManagerTransactor) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { + return _DestinationRewardManager.contract.Transact(opts, "transferOwnership", to) +} + +func (_DestinationRewardManager *DestinationRewardManagerSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _DestinationRewardManager.Contract.TransferOwnership(&_DestinationRewardManager.TransactOpts, to) +} + +func (_DestinationRewardManager *DestinationRewardManagerTransactorSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _DestinationRewardManager.Contract.TransferOwnership(&_DestinationRewardManager.TransactOpts, to) +} + +func (_DestinationRewardManager *DestinationRewardManagerTransactor) UpdateRewardRecipients(opts *bind.TransactOpts, poolId [32]byte, newRewardRecipients []CommonAddressAndWeight) (*types.Transaction, error) { + return _DestinationRewardManager.contract.Transact(opts, "updateRewardRecipients", poolId, newRewardRecipients) +} + +func (_DestinationRewardManager *DestinationRewardManagerSession) UpdateRewardRecipients(poolId [32]byte, newRewardRecipients []CommonAddressAndWeight) (*types.Transaction, error) { + return _DestinationRewardManager.Contract.UpdateRewardRecipients(&_DestinationRewardManager.TransactOpts, poolId, newRewardRecipients) +} + +func (_DestinationRewardManager *DestinationRewardManagerTransactorSession) UpdateRewardRecipients(poolId [32]byte, newRewardRecipients []CommonAddressAndWeight) (*types.Transaction, error) { + return _DestinationRewardManager.Contract.UpdateRewardRecipients(&_DestinationRewardManager.TransactOpts, poolId, newRewardRecipients) +} + +type DestinationRewardManagerFeeManagerUpdatedIterator struct { + Event *DestinationRewardManagerFeeManagerUpdated + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *DestinationRewardManagerFeeManagerUpdatedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(DestinationRewardManagerFeeManagerUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(DestinationRewardManagerFeeManagerUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *DestinationRewardManagerFeeManagerUpdatedIterator) Error() error { + return it.fail +} + +func (it *DestinationRewardManagerFeeManagerUpdatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type DestinationRewardManagerFeeManagerUpdated struct { + NewFeeManagerAddress common.Address + Raw types.Log +} + +func (_DestinationRewardManager *DestinationRewardManagerFilterer) FilterFeeManagerUpdated(opts *bind.FilterOpts) (*DestinationRewardManagerFeeManagerUpdatedIterator, error) { + + logs, sub, err := _DestinationRewardManager.contract.FilterLogs(opts, "FeeManagerUpdated") + if err != nil { + return nil, err + } + return &DestinationRewardManagerFeeManagerUpdatedIterator{contract: _DestinationRewardManager.contract, event: "FeeManagerUpdated", logs: logs, sub: sub}, nil +} + +func (_DestinationRewardManager *DestinationRewardManagerFilterer) WatchFeeManagerUpdated(opts *bind.WatchOpts, sink chan<- *DestinationRewardManagerFeeManagerUpdated) (event.Subscription, error) { + + logs, sub, err := _DestinationRewardManager.contract.WatchLogs(opts, "FeeManagerUpdated") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(DestinationRewardManagerFeeManagerUpdated) + if err := _DestinationRewardManager.contract.UnpackLog(event, "FeeManagerUpdated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_DestinationRewardManager *DestinationRewardManagerFilterer) ParseFeeManagerUpdated(log types.Log) (*DestinationRewardManagerFeeManagerUpdated, error) { + event := new(DestinationRewardManagerFeeManagerUpdated) + if err := _DestinationRewardManager.contract.UnpackLog(event, "FeeManagerUpdated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type DestinationRewardManagerFeePaidIterator struct { + Event *DestinationRewardManagerFeePaid + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *DestinationRewardManagerFeePaidIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(DestinationRewardManagerFeePaid) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(DestinationRewardManagerFeePaid) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *DestinationRewardManagerFeePaidIterator) Error() error { + return it.fail +} + +func (it *DestinationRewardManagerFeePaidIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type DestinationRewardManagerFeePaid struct { + Payments []IDestinationRewardManagerFeePayment + Payer common.Address + Raw types.Log +} + +func (_DestinationRewardManager *DestinationRewardManagerFilterer) FilterFeePaid(opts *bind.FilterOpts) (*DestinationRewardManagerFeePaidIterator, error) { + + logs, sub, err := _DestinationRewardManager.contract.FilterLogs(opts, "FeePaid") + if err != nil { + return nil, err + } + return &DestinationRewardManagerFeePaidIterator{contract: _DestinationRewardManager.contract, event: "FeePaid", logs: logs, sub: sub}, nil +} + +func (_DestinationRewardManager *DestinationRewardManagerFilterer) WatchFeePaid(opts *bind.WatchOpts, sink chan<- *DestinationRewardManagerFeePaid) (event.Subscription, error) { + + logs, sub, err := _DestinationRewardManager.contract.WatchLogs(opts, "FeePaid") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(DestinationRewardManagerFeePaid) + if err := _DestinationRewardManager.contract.UnpackLog(event, "FeePaid", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_DestinationRewardManager *DestinationRewardManagerFilterer) ParseFeePaid(log types.Log) (*DestinationRewardManagerFeePaid, error) { + event := new(DestinationRewardManagerFeePaid) + if err := _DestinationRewardManager.contract.UnpackLog(event, "FeePaid", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type DestinationRewardManagerOwnershipTransferRequestedIterator struct { + Event *DestinationRewardManagerOwnershipTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *DestinationRewardManagerOwnershipTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(DestinationRewardManagerOwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(DestinationRewardManagerOwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *DestinationRewardManagerOwnershipTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *DestinationRewardManagerOwnershipTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type DestinationRewardManagerOwnershipTransferRequested struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_DestinationRewardManager *DestinationRewardManagerFilterer) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*DestinationRewardManagerOwnershipTransferRequestedIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _DestinationRewardManager.contract.FilterLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return &DestinationRewardManagerOwnershipTransferRequestedIterator{contract: _DestinationRewardManager.contract, event: "OwnershipTransferRequested", logs: logs, sub: sub}, nil +} + +func (_DestinationRewardManager *DestinationRewardManagerFilterer) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *DestinationRewardManagerOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _DestinationRewardManager.contract.WatchLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(DestinationRewardManagerOwnershipTransferRequested) + if err := _DestinationRewardManager.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_DestinationRewardManager *DestinationRewardManagerFilterer) ParseOwnershipTransferRequested(log types.Log) (*DestinationRewardManagerOwnershipTransferRequested, error) { + event := new(DestinationRewardManagerOwnershipTransferRequested) + if err := _DestinationRewardManager.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type DestinationRewardManagerOwnershipTransferredIterator struct { + Event *DestinationRewardManagerOwnershipTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *DestinationRewardManagerOwnershipTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(DestinationRewardManagerOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(DestinationRewardManagerOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *DestinationRewardManagerOwnershipTransferredIterator) Error() error { + return it.fail +} + +func (it *DestinationRewardManagerOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type DestinationRewardManagerOwnershipTransferred struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_DestinationRewardManager *DestinationRewardManagerFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*DestinationRewardManagerOwnershipTransferredIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _DestinationRewardManager.contract.FilterLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return &DestinationRewardManagerOwnershipTransferredIterator{contract: _DestinationRewardManager.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +func (_DestinationRewardManager *DestinationRewardManagerFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *DestinationRewardManagerOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _DestinationRewardManager.contract.WatchLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(DestinationRewardManagerOwnershipTransferred) + if err := _DestinationRewardManager.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_DestinationRewardManager *DestinationRewardManagerFilterer) ParseOwnershipTransferred(log types.Log) (*DestinationRewardManagerOwnershipTransferred, error) { + event := new(DestinationRewardManagerOwnershipTransferred) + if err := _DestinationRewardManager.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type DestinationRewardManagerRewardRecipientsUpdatedIterator struct { + Event *DestinationRewardManagerRewardRecipientsUpdated + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *DestinationRewardManagerRewardRecipientsUpdatedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(DestinationRewardManagerRewardRecipientsUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(DestinationRewardManagerRewardRecipientsUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *DestinationRewardManagerRewardRecipientsUpdatedIterator) Error() error { + return it.fail +} + +func (it *DestinationRewardManagerRewardRecipientsUpdatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type DestinationRewardManagerRewardRecipientsUpdated struct { + PoolId [32]byte + NewRewardRecipients []CommonAddressAndWeight + Raw types.Log +} + +func (_DestinationRewardManager *DestinationRewardManagerFilterer) FilterRewardRecipientsUpdated(opts *bind.FilterOpts, poolId [][32]byte) (*DestinationRewardManagerRewardRecipientsUpdatedIterator, error) { + + var poolIdRule []interface{} + for _, poolIdItem := range poolId { + poolIdRule = append(poolIdRule, poolIdItem) + } + + logs, sub, err := _DestinationRewardManager.contract.FilterLogs(opts, "RewardRecipientsUpdated", poolIdRule) + if err != nil { + return nil, err + } + return &DestinationRewardManagerRewardRecipientsUpdatedIterator{contract: _DestinationRewardManager.contract, event: "RewardRecipientsUpdated", logs: logs, sub: sub}, nil +} + +func (_DestinationRewardManager *DestinationRewardManagerFilterer) WatchRewardRecipientsUpdated(opts *bind.WatchOpts, sink chan<- *DestinationRewardManagerRewardRecipientsUpdated, poolId [][32]byte) (event.Subscription, error) { + + var poolIdRule []interface{} + for _, poolIdItem := range poolId { + poolIdRule = append(poolIdRule, poolIdItem) + } + + logs, sub, err := _DestinationRewardManager.contract.WatchLogs(opts, "RewardRecipientsUpdated", poolIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(DestinationRewardManagerRewardRecipientsUpdated) + if err := _DestinationRewardManager.contract.UnpackLog(event, "RewardRecipientsUpdated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_DestinationRewardManager *DestinationRewardManagerFilterer) ParseRewardRecipientsUpdated(log types.Log) (*DestinationRewardManagerRewardRecipientsUpdated, error) { + event := new(DestinationRewardManagerRewardRecipientsUpdated) + if err := _DestinationRewardManager.contract.UnpackLog(event, "RewardRecipientsUpdated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type DestinationRewardManagerRewardsClaimedIterator struct { + Event *DestinationRewardManagerRewardsClaimed + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *DestinationRewardManagerRewardsClaimedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(DestinationRewardManagerRewardsClaimed) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(DestinationRewardManagerRewardsClaimed) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *DestinationRewardManagerRewardsClaimedIterator) Error() error { + return it.fail +} + +func (it *DestinationRewardManagerRewardsClaimedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type DestinationRewardManagerRewardsClaimed struct { + PoolId [32]byte + Recipient common.Address + Quantity *big.Int + Raw types.Log +} + +func (_DestinationRewardManager *DestinationRewardManagerFilterer) FilterRewardsClaimed(opts *bind.FilterOpts, poolId [][32]byte, recipient []common.Address) (*DestinationRewardManagerRewardsClaimedIterator, error) { + + var poolIdRule []interface{} + for _, poolIdItem := range poolId { + poolIdRule = append(poolIdRule, poolIdItem) + } + var recipientRule []interface{} + for _, recipientItem := range recipient { + recipientRule = append(recipientRule, recipientItem) + } + + logs, sub, err := _DestinationRewardManager.contract.FilterLogs(opts, "RewardsClaimed", poolIdRule, recipientRule) + if err != nil { + return nil, err + } + return &DestinationRewardManagerRewardsClaimedIterator{contract: _DestinationRewardManager.contract, event: "RewardsClaimed", logs: logs, sub: sub}, nil +} + +func (_DestinationRewardManager *DestinationRewardManagerFilterer) WatchRewardsClaimed(opts *bind.WatchOpts, sink chan<- *DestinationRewardManagerRewardsClaimed, poolId [][32]byte, recipient []common.Address) (event.Subscription, error) { + + var poolIdRule []interface{} + for _, poolIdItem := range poolId { + poolIdRule = append(poolIdRule, poolIdItem) + } + var recipientRule []interface{} + for _, recipientItem := range recipient { + recipientRule = append(recipientRule, recipientItem) + } + + logs, sub, err := _DestinationRewardManager.contract.WatchLogs(opts, "RewardsClaimed", poolIdRule, recipientRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(DestinationRewardManagerRewardsClaimed) + if err := _DestinationRewardManager.contract.UnpackLog(event, "RewardsClaimed", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_DestinationRewardManager *DestinationRewardManagerFilterer) ParseRewardsClaimed(log types.Log) (*DestinationRewardManagerRewardsClaimed, error) { + event := new(DestinationRewardManagerRewardsClaimed) + if err := _DestinationRewardManager.contract.UnpackLog(event, "RewardsClaimed", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +func (_DestinationRewardManager *DestinationRewardManager) ParseLog(log types.Log) (generated.AbigenLog, error) { + switch log.Topics[0] { + case _DestinationRewardManager.abi.Events["FeeManagerUpdated"].ID: + return _DestinationRewardManager.ParseFeeManagerUpdated(log) + case _DestinationRewardManager.abi.Events["FeePaid"].ID: + return _DestinationRewardManager.ParseFeePaid(log) + case _DestinationRewardManager.abi.Events["OwnershipTransferRequested"].ID: + return _DestinationRewardManager.ParseOwnershipTransferRequested(log) + case _DestinationRewardManager.abi.Events["OwnershipTransferred"].ID: + return _DestinationRewardManager.ParseOwnershipTransferred(log) + case _DestinationRewardManager.abi.Events["RewardRecipientsUpdated"].ID: + return _DestinationRewardManager.ParseRewardRecipientsUpdated(log) + case _DestinationRewardManager.abi.Events["RewardsClaimed"].ID: + return _DestinationRewardManager.ParseRewardsClaimed(log) + + default: + return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) + } +} + +func (DestinationRewardManagerFeeManagerUpdated) Topic() common.Hash { + return common.HexToHash("0xe45f5e140399b0a7e12971ab020724b828fbed8ac408c420884dc7d1bbe506b4") +} + +func (DestinationRewardManagerFeePaid) Topic() common.Hash { + return common.HexToHash("0xa1cc025ea76bacce5d740ee4bc331899375dc2c5f2ab33933aaacbd9ba001b66") +} + +func (DestinationRewardManagerOwnershipTransferRequested) Topic() common.Hash { + return common.HexToHash("0xed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278") +} + +func (DestinationRewardManagerOwnershipTransferred) Topic() common.Hash { + return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") +} + +func (DestinationRewardManagerRewardRecipientsUpdated) Topic() common.Hash { + return common.HexToHash("0x8f668d6090683f98b3373a8b83d214da45737f7486cb7de554cc07b54e61cfe6") +} + +func (DestinationRewardManagerRewardsClaimed) Topic() common.Hash { + return common.HexToHash("0x989969655bc1d593922527fe85d71347bb8e12fa423cc71f362dd8ef7cb10ef2") +} + +func (_DestinationRewardManager *DestinationRewardManager) Address() common.Address { + return _DestinationRewardManager.address +} + +type DestinationRewardManagerInterface interface { + GetAvailableRewardPoolIds(opts *bind.CallOpts, recipient common.Address, startIndex *big.Int, endIndex *big.Int) ([][32]byte, error) + + ILinkAddress(opts *bind.CallOpts) (common.Address, error) + + Owner(opts *bind.CallOpts) (common.Address, error) + + SFeeManagerAddressList(opts *bind.CallOpts, arg0 common.Address) (common.Address, error) + + SRegisteredPoolIds(opts *bind.CallOpts, arg0 *big.Int) ([32]byte, error) + + SRewardRecipientWeights(opts *bind.CallOpts, arg0 [32]byte, arg1 common.Address) (*big.Int, error) + + SRewardRecipientWeightsSet(opts *bind.CallOpts, arg0 [32]byte) (bool, error) + + STotalRewardRecipientFees(opts *bind.CallOpts, arg0 [32]byte) (*big.Int, error) + + STotalRewardRecipientFeesLastClaimedAmounts(opts *bind.CallOpts, arg0 [32]byte, arg1 common.Address) (*big.Int, error) + + SupportsInterface(opts *bind.CallOpts, interfaceId [4]byte) (bool, error) + + TypeAndVersion(opts *bind.CallOpts) (string, error) + + AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) + + AddFeeManager(opts *bind.TransactOpts, newFeeManagerAddress common.Address) (*types.Transaction, error) + + ClaimRewards(opts *bind.TransactOpts, poolIds [][32]byte) (*types.Transaction, error) + + OnFeePaid(opts *bind.TransactOpts, payments []IDestinationRewardManagerFeePayment, payer common.Address) (*types.Transaction, error) + + PayRecipients(opts *bind.TransactOpts, poolId [32]byte, recipients []common.Address) (*types.Transaction, error) + + RemoveFeeManager(opts *bind.TransactOpts, feeManagerAddress common.Address) (*types.Transaction, error) + + SetRewardRecipients(opts *bind.TransactOpts, poolId [32]byte, rewardRecipientAndWeights []CommonAddressAndWeight) (*types.Transaction, error) + + TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + + UpdateRewardRecipients(opts *bind.TransactOpts, poolId [32]byte, newRewardRecipients []CommonAddressAndWeight) (*types.Transaction, error) + + FilterFeeManagerUpdated(opts *bind.FilterOpts) (*DestinationRewardManagerFeeManagerUpdatedIterator, error) + + WatchFeeManagerUpdated(opts *bind.WatchOpts, sink chan<- *DestinationRewardManagerFeeManagerUpdated) (event.Subscription, error) + + ParseFeeManagerUpdated(log types.Log) (*DestinationRewardManagerFeeManagerUpdated, error) + + FilterFeePaid(opts *bind.FilterOpts) (*DestinationRewardManagerFeePaidIterator, error) + + WatchFeePaid(opts *bind.WatchOpts, sink chan<- *DestinationRewardManagerFeePaid) (event.Subscription, error) + + ParseFeePaid(log types.Log) (*DestinationRewardManagerFeePaid, error) + + FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*DestinationRewardManagerOwnershipTransferRequestedIterator, error) + + WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *DestinationRewardManagerOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferRequested(log types.Log) (*DestinationRewardManagerOwnershipTransferRequested, error) + + FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*DestinationRewardManagerOwnershipTransferredIterator, error) + + WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *DestinationRewardManagerOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferred(log types.Log) (*DestinationRewardManagerOwnershipTransferred, error) + + FilterRewardRecipientsUpdated(opts *bind.FilterOpts, poolId [][32]byte) (*DestinationRewardManagerRewardRecipientsUpdatedIterator, error) + + WatchRewardRecipientsUpdated(opts *bind.WatchOpts, sink chan<- *DestinationRewardManagerRewardRecipientsUpdated, poolId [][32]byte) (event.Subscription, error) + + ParseRewardRecipientsUpdated(log types.Log) (*DestinationRewardManagerRewardRecipientsUpdated, error) + + FilterRewardsClaimed(opts *bind.FilterOpts, poolId [][32]byte, recipient []common.Address) (*DestinationRewardManagerRewardsClaimedIterator, error) + + WatchRewardsClaimed(opts *bind.WatchOpts, sink chan<- *DestinationRewardManagerRewardsClaimed, poolId [][32]byte, recipient []common.Address) (event.Subscription, error) + + ParseRewardsClaimed(log types.Log) (*DestinationRewardManagerRewardsClaimed, error) + + ParseLog(log types.Log) (generated.AbigenLog, error) + + Address() common.Address +} diff --git a/core/gethwrappers/llo-feeds/generated/destination_verifier/destination_verifier.go b/core/gethwrappers/llo-feeds/generated/destination_verifier/destination_verifier.go new file mode 100644 index 0000000000..2fa48b7249 --- /dev/null +++ b/core/gethwrappers/llo-feeds/generated/destination_verifier/destination_verifier.go @@ -0,0 +1,1576 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package destination_verifier + +import ( + "errors" + "fmt" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" +) + +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +type CommonAddressAndWeight struct { + Addr common.Address + Weight uint64 +} + +var DestinationVerifierMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"verifierProxy\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AccessForbidden\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BadActivationTime\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BadVerification\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes24\",\"name\":\"donConfigId\",\"type\":\"bytes24\"}],\"name\":\"DonConfigAlreadyExists\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"DonConfigDoesNotExist\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"numSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxSigners\",\"type\":\"uint256\"}],\"name\":\"ExcessSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FaultToleranceMustBePositive\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FeeManagerInvalid\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"numSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"minSigners\",\"type\":\"uint256\"}],\"name\":\"InsufficientSigners\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"rsLength\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"ssLength\",\"type\":\"uint256\"}],\"name\":\"MismatchedSignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoSigners\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NonUniqueSignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"VerifierProxyInvalid\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddress\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldAccessController\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAccessController\",\"type\":\"address\"}],\"name\":\"AccessControllerSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes24\",\"name\":\"donConfigId\",\"type\":\"bytes24\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isActive\",\"type\":\"bool\"}],\"name\":\"ConfigActivated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes24\",\"name\":\"donConfigId\",\"type\":\"bytes24\"}],\"name\":\"ConfigRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes24\",\"name\":\"donConfigId\",\"type\":\"bytes24\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"weight\",\"type\":\"uint64\"}],\"indexed\":false,\"internalType\":\"structCommon.AddressAndWeight[]\",\"name\":\"recipientAddressesAndWeights\",\"type\":\"tuple[]\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"oldFeeManager\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newFeeManager\",\"type\":\"address\"}],\"name\":\"FeeManagerSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"feedId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"requester\",\"type\":\"address\"}],\"name\":\"ReportVerified\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"i_verifierProxy\",\"outputs\":[{\"internalType\":\"contractIDestinationVerifierProxy\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"removeLatestConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_accessController\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_feeManager\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"accessController\",\"type\":\"address\"}],\"name\":\"setAccessController\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"weight\",\"type\":\"uint64\"}],\"internalType\":\"structCommon.AddressAndWeight[]\",\"name\":\"recipientAddressesAndWeights\",\"type\":\"tuple[]\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"donConfigIndex\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isActive\",\"type\":\"bool\"}],\"name\":\"setConfigActive\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"weight\",\"type\":\"uint64\"}],\"internalType\":\"structCommon.AddressAndWeight[]\",\"name\":\"recipientAddressesAndWeights\",\"type\":\"tuple[]\"},{\"internalType\":\"uint32\",\"name\":\"activationTime\",\"type\":\"uint32\"}],\"name\":\"setConfigWithActivationTime\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"feeManager\",\"type\":\"address\"}],\"name\":\"setFeeManager\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"signedReport\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"parameterPayload\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"verify\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes[]\",\"name\":\"signedReports\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes\",\"name\":\"parameterPayload\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"verifyBulk\",\"outputs\":[{\"internalType\":\"bytes[]\",\"name\":\"\",\"type\":\"bytes[]\"}],\"stateMutability\":\"payable\",\"type\":\"function\"}]", + Bin: "0x60a06040523480156200001157600080fd5b506040516200379d3803806200379d8339810160408190526200003491620001a6565b33806000816200008b5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000be57620000be81620000fb565b5050506001600160a01b038116620000e95760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b0316608052620001d8565b336001600160a01b03821603620001555760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000082565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600060208284031215620001b957600080fd5b81516001600160a01b0381168114620001d157600080fd5b9392505050565b60805161359b62000202600039600081816102d70152818161062a0152611064015261359b6000f3fe6080604052600436106100f35760003560e01c80638da5cb5b1161008a578063d7c72e4e11610059578063d7c72e4e146102f9578063f08391d814610319578063f2fde38b14610339578063f9c7bf771461035957600080fd5b80638da5cb5b1461025857806394ba284614610283578063af4fed24146102b0578063b97455c7146102c557600080fd5b8063453ec61b116100c6578063453ec61b146101e1578063472d35b9146102035780635ad72fae1461022357806379ba50971461024357600080fd5b806301ffc9a7146100f8578063181f5a771461012d578063294d2bb11461017c57806338416b5b1461018f575b600080fd5b34801561010457600080fd5b50610118610113366004612622565b610379565b60405190151581526020015b60405180910390f35b34801561013957600080fd5b5060408051808201909152601981527f44657374696e6174696f6e566572696669657220312e302e300000000000000060208201525b60405161012491906126cf565b61016f61018a366004612754565b610626565b34801561019b57600080fd5b506004546101bc9073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610124565b3480156101ed57600080fd5b506102016101fc3660046129e3565b610873565b005b34801561020f57600080fd5b5061020161021e366004612a57565b61097b565b34801561022f57600080fd5b5061020161023e366004612a80565b610c5c565b34801561024f57600080fd5b50610201610d72565b34801561026457600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff166101bc565b34801561028f57600080fd5b506005546101bc9073ffffffffffffffffffffffffffffffffffffffff1681565b3480156102bc57600080fd5b50610201610e6f565b3480156102d157600080fd5b506101bc7f000000000000000000000000000000000000000000000000000000000000000081565b61030c610307366004612ab0565b611060565b6040516101249190612b33565b34801561032557600080fd5b50610201610334366004612a57565b611352565b34801561034557600080fd5b50610201610354366004612a57565b6113d9565b34801561036557600080fd5b50610201610374366004612bc5565b6113ed565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f294d2bb100000000000000000000000000000000000000000000000000000000148061040c57507fffffffff0000000000000000000000000000000000000000000000000000000082167fd7c72e4e00000000000000000000000000000000000000000000000000000000145b8061045857507fffffffff0000000000000000000000000000000000000000000000000000000082167f94ba284600000000000000000000000000000000000000000000000000000000145b806104a457507fffffffff0000000000000000000000000000000000000000000000000000000082167f38416b5b00000000000000000000000000000000000000000000000000000000145b806104f057507fffffffff0000000000000000000000000000000000000000000000000000000082167f453ec61b00000000000000000000000000000000000000000000000000000000145b8061053c57507fffffffff0000000000000000000000000000000000000000000000000000000082167ff9c7bf7700000000000000000000000000000000000000000000000000000000145b8061058857507fffffffff0000000000000000000000000000000000000000000000000000000082167f472d35b900000000000000000000000000000000000000000000000000000000145b806105d457507fffffffff0000000000000000000000000000000000000000000000000000000082167ff08391d800000000000000000000000000000000000000000000000000000000145b8061062057507fffffffff0000000000000000000000000000000000000000000000000000000082167f5ad72fae00000000000000000000000000000000000000000000000000000000145b92915050565b60607f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163314610697576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600554829073ffffffffffffffffffffffffffffffffffffffff16801580159061075657506040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821690636b14daf8906107139085906000903690600401612c95565b602060405180830381865afa158015610730573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107549190612cce565b155b1561078d576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008061079b8a8a886114ab565b600454919350915073ffffffffffffffffffffffffffffffffffffffff168015610864578073ffffffffffffffffffffffffffffffffffffffff166386968cfd34848e8e8e8e8e6040518863ffffffff1660e01b815260040161080396959493929190612ceb565b6000604051808303818588803b15801561081c57600080fd5b505af19350505050801561082e575060015b610864576040517f4df18f0700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50909998505050505050505050565b82518260ff16806000036108b3576040517f0743bae600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601f8211156108fd576040517f61750f4000000000000000000000000000000000000000000000000000000000815260048101839052601f60248201526044015b60405180910390fd5b610908816003612d71565b8211610960578161091a826003612d71565b610925906001612d88565b6040517f9dd9e6d8000000000000000000000000000000000000000000000000000000008152600481019290925260248201526044016108f4565b6109686119b0565b61097485858542611a33565b5050505050565b6109836119b0565b6040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527f86968cfd00000000000000000000000000000000000000000000000000000000600482015273ffffffffffffffffffffffffffffffffffffffff8216906301ffc9a790602401602060405180830381865afa158015610a0d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a319190612cce565b1580610ae857506040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527f3690750900000000000000000000000000000000000000000000000000000000600482015273ffffffffffffffffffffffffffffffffffffffff8216906301ffc9a790602401602060405180830381865afa158015610ac2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ae69190612cce565b155b80610b9e57506040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527ff65df96200000000000000000000000000000000000000000000000000000000600482015273ffffffffffffffffffffffffffffffffffffffff8216906301ffc9a790602401602060405180830381865afa158015610b78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b9c9190612cce565b155b15610bd5576040517f8238941900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527f04628abcaa6b1674651352125cb94b65b289145bc2bc4d67720bb7d966372f0391015b60405180910390a15050565b610c646119b0565b6003548210610c9f576040517f5a0d6fe200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600060038381548110610cb457610cb4612d9b565b600091825260209182902001805484151579010000000000000000000000000000000000000000000000000081027fffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffff909216919091178083556040805191811b7fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000168252938101919091529092507f90186a1e77b498ec417ea88bd026cae00d7043c357cc45221777623bda582dd4910160405180910390a1505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610df3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016108f4565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610e776119b0565b600354600003610eb3576040517f5a0d6fe200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003805460009190610ec790600190612dca565b81548110610ed757610ed7612d9b565b600091825260209182902060408051608081018252929091015480821b7fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001683527801000000000000000000000000000000000000000000000000810460ff9081169484019490945279010000000000000000000000000000000000000000000000000081049093161515908201527a01000000000000000000000000000000000000000000000000000090910463ffffffff166060820152600380549192509080610fa557610fa5612ddd565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffff00000000000000000000000000000000000000000000000000000000000016905501905580516040517f970fd8f3ebdd9a271080aacf9807a5c709be0b448e4047a6fc212b8cc165368d91611055917fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000091909116815260200190565b60405180910390a150565b60607f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1633146110d1576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600554829073ffffffffffffffffffffffffffffffffffffffff16801580159061119057506040517f6b14daf800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821690636b14daf89061114d9085906000903690600401612c95565b602060405180830381865afa15801561116a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061118e9190612cce565b155b156111c7576040517fef67f5d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008767ffffffffffffffff8111156111e2576111e26127d5565b60405190808252806020026020018201604052801561121557816020015b60608152602001906001900390816112005790505b50905060008867ffffffffffffffff811115611233576112336127d5565b60405190808252806020026020018201604052801561125c578160200160208202803683370190505b50905060005b898110156112ee5760008061129a8d8d8581811061128257611282612d9b565b90506020028101906112949190612e0c565b8b6114ab565b91509150818584815181106112b1576112b1612d9b565b6020026020010181905250808484815181106112cf576112cf612d9b565b6020026020010181815250505050806112e790612e71565b9050611262565b5060045473ffffffffffffffffffffffffffffffffffffffff168015610864578073ffffffffffffffffffffffffffffffffffffffff16633690750934848e8e8e8e8e6040518863ffffffff1660e01b815260040161080396959493929190612ea9565b61135a6119b0565b6005805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527f953e92b1a6442e9c3242531154a3f6f6eb00b4e9c719ba8118fa6235e4ce89b69101610c50565b6113e16119b0565b6113ea816120eb565b50565b83518360ff168060000361142d576040517f0743bae600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601f821115611472576040517f61750f4000000000000000000000000000000000000000000000000000000000815260048101839052601f60248201526044016108f4565b61147d816003612d71565b821161148f578161091a826003612d71565b6114976119b0565b6114a386868686611a33565b505050505050565b6060600080808080806114c0898b018b6130db565b94509450945094509450815183511461151257825182516040517ff0d31408000000000000000000000000000000000000000000000000000000008152600481019290925260248201526044016108f4565b825160000361154d576040517fc7af40f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008480519060200120866040516020016115699291906131b6565b6040516020818303038152906040528051906020012090506000845167ffffffffffffffff81111561159d5761159d6127d5565b6040519080825280602002602001820160405280156115c6578160200160208202803683370190505b50905060005b85518110156116d4576001838583602081106115ea576115ea612d9b565b6115f791901a601b6131f2565b88848151811061160957611609612d9b565b602002602001015188858151811061162357611623612d9b565b602002602001015160405160008152602001604052604051611661949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa158015611683573d6000803e3d6000fd5b5050506020604051035182828151811061169f5761169f612d9b565b73ffffffffffffffffffffffffffffffffffffffff909216602092830291909101909101526116cd81612e71565b90506115cc565b506116de816121e0565b15611715576040517f4df18f0700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006117208761228f565b9050600061172d826122b5565b80519091507fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001661178a576040517f4df18f0700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80604001516117c5576040517f4df18f0700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806020015160ff16835111611806576040517f4df18f0700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000805b84518110156119245784818151811061182557611825612d9b565b6020026020010151836000015160405160200161189592919060609290921b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001682527fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000166014820152602c0190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291815281516020928301206000818152600290935291205490925060ff16611914576040517f4df18f0700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61191d81612e71565b905061180a565b5061192e8961320b565b60405173ffffffffffffffffffffffffffffffffffffffff8f1681527f58ca9502e98a536e06e72d680fcc251e5d10b72291a281665a2c2dc0ac30fcc59060200160405180910390a25051969d7fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000009097169c50959a5050505050505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314611a31576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016108f4565b565b83518360ff1680600003611a73576040517f0743bae600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601f821115611ab8576040517f61750f4000000000000000000000000000000000000000000000000000000000815260048101839052601f60248201526044016108f4565b611ac3816003612d71565b8211611ad5578161091a826003612d71565b611add6119b0565b611ae6866121e0565b15611b1d576040517ff67bc7c400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b428363ffffffff161115611b5d576040517f0114c7e600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611b7686600060018951611b719190612dca565b61243a565b60008686604051602001611b8b929190613250565b60405160208183030381529060405280519060200120905060005b8751811015611d3257600073ffffffffffffffffffffffffffffffffffffffff16888281518110611bd957611bd9612d9b565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1603611c2e576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600260008a8481518110611c4657611c46612d9b565b602002602001015185604051602001611cb292919060609290921b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001682527fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000166014820152602c0190565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815291815281516020928301208352908201929092520160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055611d2b81612e71565b9050611ba6565b506003548015801590611d96575063ffffffff85166003611d54600184612dca565b81548110611d6457611d64612d9b565b6000918252602090912001547a010000000000000000000000000000000000000000000000000000900463ffffffff16115b15611dcd576040517f0114c7e600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600081118015611e4b57507fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000082166003611e08600184612dca565b81548110611e1857611e18612d9b565b60009182526020909120015460401b7fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000016145b15611ea6576040517fc0178c860000000000000000000000000000000000000000000000000000000081527fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000831660048201526024016108f4565b855115611f3757600480546040517ff65df96200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091169163f65df96291611f049186918b910161332e565b600060405180830381600087803b158015611f1e57600080fd5b505af1158015611f32573d6000803e3d6000fd5b505050505b604080516080810182527fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000841680825260ff808b1660208401908152600184860181815263ffffffff808d166060880190815260038054948501815560005296517fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b90930180549451925197519091167a010000000000000000000000000000000000000000000000000000027fffff00000000ffffffffffffffffffffffffffffffffffffffffffffffffffff97151579010000000000000000000000000000000000000000000000000002979097167fffff0000000000ffffffffffffffffffffffffffffffffffffffffffffffffff929095167801000000000000000000000000000000000000000000000000027fffffffffffffff0000000000000000000000000000000000000000000000000090941692881c929092179290921791909116919091179290921790915590517f2d763a674a99583454a287d792819ffb9ff7e791c23e7745a082701136ce336c906120d9908b908b908b90613371565b60405180910390a25050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff82160361216a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016108f4565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000805b82518110156122865760006121fa826001612d88565b90505b835181101561227d5783818151811061221857612218612d9b565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1684838151811061224857612248612d9b565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1603612275575060019392505050565b6001016121fd565b506001016121e4565b50600092915050565b600080828060200190518101906122a691906133dd565b63ffffffff1695945050505050565b6040805160808101825260008082526020820181905291810182905260608101919091526040805160808101825260008082526020820181905291810182905260608101919091526003545b80156124335761231081613420565b9050836003828154811061232657612326612d9b565b6000918252602090912001547a010000000000000000000000000000000000000000000000000000900463ffffffff161161242e576003818154811061236e5761236e612d9b565b600091825260209182902060408051608081018252929091015480821b7fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001683527801000000000000000000000000000000000000000000000000810460ff9081169484019490945279010000000000000000000000000000000000000000000000000081049093161515908201527a01000000000000000000000000000000000000000000000000000090910463ffffffff1660608201529150612433565b612301565b5092915050565b818180820361244a575050505050565b60008560026124598787613455565b6124639190613475565b61246d9087613504565b8151811061247d5761247d612d9b565b602002602001015190505b8183136125fc575b8073ffffffffffffffffffffffffffffffffffffffff168684815181106124b9576124b9612d9b565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1610156124ef57826124e78161352c565b935050612490565b85828151811061250157612501612d9b565b602002602001015173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16101561254e57816125468161355d565b9250506124ef565b8183136125f75785828151811061256757612567612d9b565b602002602001015186848151811061258157612581612d9b565b602002602001015187858151811061259b5761259b612d9b565b602002602001018885815181106125b4576125b4612d9b565b73ffffffffffffffffffffffffffffffffffffffff938416602091820292909201015291169052826125e58161352c565b93505081806125f39061355d565b9250505b612488565b8185121561260f5761260f86868461243a565b838312156114a3576114a386848661243a565b60006020828403121561263457600080fd5b81357fffffffff000000000000000000000000000000000000000000000000000000008116811461266457600080fd5b9392505050565b6000815180845260005b8181101561269157602081850181015186830182015201612675565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081526000612664602083018461266b565b60008083601f8401126126f457600080fd5b50813567ffffffffffffffff81111561270c57600080fd5b60208301915083602082850101111561272457600080fd5b9250929050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461274f57600080fd5b919050565b60008060008060006060868803121561276c57600080fd5b853567ffffffffffffffff8082111561278457600080fd5b61279089838a016126e2565b909750955060208801359150808211156127a957600080fd5b506127b6888289016126e2565b90945092506127c990506040870161272b565b90509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715612827576128276127d5565b60405290565b6040516060810167ffffffffffffffff81118282101715612827576128276127d5565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715612897576128976127d5565b604052919050565b600067ffffffffffffffff8211156128b9576128b96127d5565b5060051b60200190565b600082601f8301126128d457600080fd5b813560206128e96128e48361289f565b612850565b82815260059290921b8401810191818101908684111561290857600080fd5b8286015b8481101561292a5761291d8161272b565b835291830191830161290c565b509695505050505050565b803560ff8116811461274f57600080fd5b600082601f83011261295757600080fd5b813560206129676128e48361289f565b82815260069290921b8401810191818101908684111561298657600080fd5b8286015b8481101561292a57604081890312156129a35760008081fd5b6129ab612804565b6129b48261272b565b81528482013567ffffffffffffffff811681146129d15760008081fd5b8186015283529183019160400161298a565b6000806000606084860312156129f857600080fd5b833567ffffffffffffffff80821115612a1057600080fd5b612a1c878388016128c3565b9450612a2a60208701612935565b93506040860135915080821115612a4057600080fd5b50612a4d86828701612946565b9150509250925092565b600060208284031215612a6957600080fd5b6126648261272b565b80151581146113ea57600080fd5b60008060408385031215612a9357600080fd5b823591506020830135612aa581612a72565b809150509250929050565b600080600080600060608688031215612ac857600080fd5b853567ffffffffffffffff80821115612ae057600080fd5b818801915088601f830112612af457600080fd5b813581811115612b0357600080fd5b8960208260051b8501011115612b1857600080fd5b6020928301975095509087013590808211156127a957600080fd5b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015612ba6577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0888603018452612b9485835161266b565b94509285019290850190600101612b5a565b5092979650505050505050565b63ffffffff811681146113ea57600080fd5b60008060008060808587031215612bdb57600080fd5b843567ffffffffffffffff80821115612bf357600080fd5b612bff888389016128c3565b9550612c0d60208801612935565b94506040870135915080821115612c2357600080fd5b50612c3087828801612946565b9250506060850135612c4181612bb3565b939692955090935050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b73ffffffffffffffffffffffffffffffffffffffff84168152604060208201526000612cc5604083018486612c4c565b95945050505050565b600060208284031215612ce057600080fd5b815161266481612a72565b868152608060208201526000612d05608083018789612c4c565b8281036040840152612d18818688612c4c565b91505073ffffffffffffffffffffffffffffffffffffffff83166060830152979650505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808202811582820484141761062057610620612d42565b8082018082111561062057610620612d42565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b8181038181111561062057610620612d42565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112612e4157600080fd5b83018035915067ffffffffffffffff821115612e5c57600080fd5b60200191503681900382131561272457600080fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203612ea257612ea2612d42565b5060010190565b6080808252875190820181905260009060209060a0840190828b01845b82811015612ee257815184529284019290840190600101612ec6565b50505083810382850152878152818101600589901b820183018a60005b8b811015612faa577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe085840301845281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18e3603018112612f6057600080fd5b8d01868101903567ffffffffffffffff811115612f7c57600080fd5b803603821315612f8b57600080fd5b612f96858284612c4c565b958801959450505090850190600101612eff565b50508581036040870152612fbf81898b612c4c565b945050505050612fe7606083018473ffffffffffffffffffffffffffffffffffffffff169052565b979650505050505050565b600082601f83011261300357600080fd5b813567ffffffffffffffff81111561301d5761301d6127d5565b61304e60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601612850565b81815284602083860101111561306357600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f83011261309157600080fd5b813560206130a16128e48361289f565b82815260059290921b840181019181810190868411156130c057600080fd5b8286015b8481101561292a57803583529183019183016130c4565b600080600080600060e086880312156130f357600080fd5b86601f87011261310257600080fd5b61310a61282d565b80606088018981111561311c57600080fd5b885b8181101561313657803584526020938401930161311e565b5090965035905067ffffffffffffffff8082111561315357600080fd5b61315f89838a01612ff2565b9550608088013591508082111561317557600080fd5b61318189838a01613080565b945060a088013591508082111561319757600080fd5b506131a488828901613080565b9598949750929560c001359392505050565b828152600060208083018460005b60038110156131e1578151835291830191908301906001016131c4565b505050506080820190509392505050565b60ff818116838216019081111561062057610620612d42565b8051602080830151919081101561324a577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8160200360031b1b821691505b50919050565b825160009082906020808701845b8381101561329057815173ffffffffffffffffffffffffffffffffffffffff168552938201939082019060010161325e565b5050505060f89390931b7fff000000000000000000000000000000000000000000000000000000000000001683525050600101919050565b600081518084526020808501945080840160005b83811015613323578151805173ffffffffffffffffffffffffffffffffffffffff16885283015167ffffffffffffffff1683880152604090960195908201906001016132dc565b509495945050505050565b7fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000008316815260406020820152600061336960408301846132c8565b949350505050565b606080825284519082018190526000906020906080840190828801845b828110156133c057815173ffffffffffffffffffffffffffffffffffffffff168452928401929084019060010161338e565b50505060ff8616828501528381036040850152612fe781866132c8565b6000806000606084860312156133f257600080fd5b83519250602084015161340481612bb3565b604085015190925061341581612bb3565b809150509250925092565b60008161342f5761342f612d42565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b818103600083128015838313168383128216171561243357612433612d42565b6000826134ab577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f8000000000000000000000000000000000000000000000000000000000000000831416156134ff576134ff612d42565b500590565b808201828112600083128015821682158216171561352457613524612d42565b505092915050565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203612ea257612ea2612d42565b60007f8000000000000000000000000000000000000000000000000000000000000000820361342f5761342f612d4256fea164736f6c6343000813000a", +} + +var DestinationVerifierABI = DestinationVerifierMetaData.ABI + +var DestinationVerifierBin = DestinationVerifierMetaData.Bin + +func DeployDestinationVerifier(auth *bind.TransactOpts, backend bind.ContractBackend, verifierProxy common.Address) (common.Address, *types.Transaction, *DestinationVerifier, error) { + parsed, err := DestinationVerifierMetaData.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(DestinationVerifierBin), backend, verifierProxy) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &DestinationVerifier{address: address, abi: *parsed, DestinationVerifierCaller: DestinationVerifierCaller{contract: contract}, DestinationVerifierTransactor: DestinationVerifierTransactor{contract: contract}, DestinationVerifierFilterer: DestinationVerifierFilterer{contract: contract}}, nil +} + +type DestinationVerifier struct { + address common.Address + abi abi.ABI + DestinationVerifierCaller + DestinationVerifierTransactor + DestinationVerifierFilterer +} + +type DestinationVerifierCaller struct { + contract *bind.BoundContract +} + +type DestinationVerifierTransactor struct { + contract *bind.BoundContract +} + +type DestinationVerifierFilterer struct { + contract *bind.BoundContract +} + +type DestinationVerifierSession struct { + Contract *DestinationVerifier + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type DestinationVerifierCallerSession struct { + Contract *DestinationVerifierCaller + CallOpts bind.CallOpts +} + +type DestinationVerifierTransactorSession struct { + Contract *DestinationVerifierTransactor + TransactOpts bind.TransactOpts +} + +type DestinationVerifierRaw struct { + Contract *DestinationVerifier +} + +type DestinationVerifierCallerRaw struct { + Contract *DestinationVerifierCaller +} + +type DestinationVerifierTransactorRaw struct { + Contract *DestinationVerifierTransactor +} + +func NewDestinationVerifier(address common.Address, backend bind.ContractBackend) (*DestinationVerifier, error) { + abi, err := abi.JSON(strings.NewReader(DestinationVerifierABI)) + if err != nil { + return nil, err + } + contract, err := bindDestinationVerifier(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &DestinationVerifier{address: address, abi: abi, DestinationVerifierCaller: DestinationVerifierCaller{contract: contract}, DestinationVerifierTransactor: DestinationVerifierTransactor{contract: contract}, DestinationVerifierFilterer: DestinationVerifierFilterer{contract: contract}}, nil +} + +func NewDestinationVerifierCaller(address common.Address, caller bind.ContractCaller) (*DestinationVerifierCaller, error) { + contract, err := bindDestinationVerifier(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &DestinationVerifierCaller{contract: contract}, nil +} + +func NewDestinationVerifierTransactor(address common.Address, transactor bind.ContractTransactor) (*DestinationVerifierTransactor, error) { + contract, err := bindDestinationVerifier(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &DestinationVerifierTransactor{contract: contract}, nil +} + +func NewDestinationVerifierFilterer(address common.Address, filterer bind.ContractFilterer) (*DestinationVerifierFilterer, error) { + contract, err := bindDestinationVerifier(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &DestinationVerifierFilterer{contract: contract}, nil +} + +func bindDestinationVerifier(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := DestinationVerifierMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_DestinationVerifier *DestinationVerifierRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _DestinationVerifier.Contract.DestinationVerifierCaller.contract.Call(opts, result, method, params...) +} + +func (_DestinationVerifier *DestinationVerifierRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _DestinationVerifier.Contract.DestinationVerifierTransactor.contract.Transfer(opts) +} + +func (_DestinationVerifier *DestinationVerifierRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _DestinationVerifier.Contract.DestinationVerifierTransactor.contract.Transact(opts, method, params...) +} + +func (_DestinationVerifier *DestinationVerifierCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _DestinationVerifier.Contract.contract.Call(opts, result, method, params...) +} + +func (_DestinationVerifier *DestinationVerifierTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _DestinationVerifier.Contract.contract.Transfer(opts) +} + +func (_DestinationVerifier *DestinationVerifierTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _DestinationVerifier.Contract.contract.Transact(opts, method, params...) +} + +func (_DestinationVerifier *DestinationVerifierCaller) IVerifierProxy(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _DestinationVerifier.contract.Call(opts, &out, "i_verifierProxy") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_DestinationVerifier *DestinationVerifierSession) IVerifierProxy() (common.Address, error) { + return _DestinationVerifier.Contract.IVerifierProxy(&_DestinationVerifier.CallOpts) +} + +func (_DestinationVerifier *DestinationVerifierCallerSession) IVerifierProxy() (common.Address, error) { + return _DestinationVerifier.Contract.IVerifierProxy(&_DestinationVerifier.CallOpts) +} + +func (_DestinationVerifier *DestinationVerifierCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _DestinationVerifier.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_DestinationVerifier *DestinationVerifierSession) Owner() (common.Address, error) { + return _DestinationVerifier.Contract.Owner(&_DestinationVerifier.CallOpts) +} + +func (_DestinationVerifier *DestinationVerifierCallerSession) Owner() (common.Address, error) { + return _DestinationVerifier.Contract.Owner(&_DestinationVerifier.CallOpts) +} + +func (_DestinationVerifier *DestinationVerifierCaller) SAccessController(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _DestinationVerifier.contract.Call(opts, &out, "s_accessController") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_DestinationVerifier *DestinationVerifierSession) SAccessController() (common.Address, error) { + return _DestinationVerifier.Contract.SAccessController(&_DestinationVerifier.CallOpts) +} + +func (_DestinationVerifier *DestinationVerifierCallerSession) SAccessController() (common.Address, error) { + return _DestinationVerifier.Contract.SAccessController(&_DestinationVerifier.CallOpts) +} + +func (_DestinationVerifier *DestinationVerifierCaller) SFeeManager(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _DestinationVerifier.contract.Call(opts, &out, "s_feeManager") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_DestinationVerifier *DestinationVerifierSession) SFeeManager() (common.Address, error) { + return _DestinationVerifier.Contract.SFeeManager(&_DestinationVerifier.CallOpts) +} + +func (_DestinationVerifier *DestinationVerifierCallerSession) SFeeManager() (common.Address, error) { + return _DestinationVerifier.Contract.SFeeManager(&_DestinationVerifier.CallOpts) +} + +func (_DestinationVerifier *DestinationVerifierCaller) SupportsInterface(opts *bind.CallOpts, interfaceId [4]byte) (bool, error) { + var out []interface{} + err := _DestinationVerifier.contract.Call(opts, &out, "supportsInterface", interfaceId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +func (_DestinationVerifier *DestinationVerifierSession) SupportsInterface(interfaceId [4]byte) (bool, error) { + return _DestinationVerifier.Contract.SupportsInterface(&_DestinationVerifier.CallOpts, interfaceId) +} + +func (_DestinationVerifier *DestinationVerifierCallerSession) SupportsInterface(interfaceId [4]byte) (bool, error) { + return _DestinationVerifier.Contract.SupportsInterface(&_DestinationVerifier.CallOpts, interfaceId) +} + +func (_DestinationVerifier *DestinationVerifierCaller) TypeAndVersion(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _DestinationVerifier.contract.Call(opts, &out, "typeAndVersion") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +func (_DestinationVerifier *DestinationVerifierSession) TypeAndVersion() (string, error) { + return _DestinationVerifier.Contract.TypeAndVersion(&_DestinationVerifier.CallOpts) +} + +func (_DestinationVerifier *DestinationVerifierCallerSession) TypeAndVersion() (string, error) { + return _DestinationVerifier.Contract.TypeAndVersion(&_DestinationVerifier.CallOpts) +} + +func (_DestinationVerifier *DestinationVerifierTransactor) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _DestinationVerifier.contract.Transact(opts, "acceptOwnership") +} + +func (_DestinationVerifier *DestinationVerifierSession) AcceptOwnership() (*types.Transaction, error) { + return _DestinationVerifier.Contract.AcceptOwnership(&_DestinationVerifier.TransactOpts) +} + +func (_DestinationVerifier *DestinationVerifierTransactorSession) AcceptOwnership() (*types.Transaction, error) { + return _DestinationVerifier.Contract.AcceptOwnership(&_DestinationVerifier.TransactOpts) +} + +func (_DestinationVerifier *DestinationVerifierTransactor) RemoveLatestConfig(opts *bind.TransactOpts) (*types.Transaction, error) { + return _DestinationVerifier.contract.Transact(opts, "removeLatestConfig") +} + +func (_DestinationVerifier *DestinationVerifierSession) RemoveLatestConfig() (*types.Transaction, error) { + return _DestinationVerifier.Contract.RemoveLatestConfig(&_DestinationVerifier.TransactOpts) +} + +func (_DestinationVerifier *DestinationVerifierTransactorSession) RemoveLatestConfig() (*types.Transaction, error) { + return _DestinationVerifier.Contract.RemoveLatestConfig(&_DestinationVerifier.TransactOpts) +} + +func (_DestinationVerifier *DestinationVerifierTransactor) SetAccessController(opts *bind.TransactOpts, accessController common.Address) (*types.Transaction, error) { + return _DestinationVerifier.contract.Transact(opts, "setAccessController", accessController) +} + +func (_DestinationVerifier *DestinationVerifierSession) SetAccessController(accessController common.Address) (*types.Transaction, error) { + return _DestinationVerifier.Contract.SetAccessController(&_DestinationVerifier.TransactOpts, accessController) +} + +func (_DestinationVerifier *DestinationVerifierTransactorSession) SetAccessController(accessController common.Address) (*types.Transaction, error) { + return _DestinationVerifier.Contract.SetAccessController(&_DestinationVerifier.TransactOpts, accessController) +} + +func (_DestinationVerifier *DestinationVerifierTransactor) SetConfig(opts *bind.TransactOpts, signers []common.Address, f uint8, recipientAddressesAndWeights []CommonAddressAndWeight) (*types.Transaction, error) { + return _DestinationVerifier.contract.Transact(opts, "setConfig", signers, f, recipientAddressesAndWeights) +} + +func (_DestinationVerifier *DestinationVerifierSession) SetConfig(signers []common.Address, f uint8, recipientAddressesAndWeights []CommonAddressAndWeight) (*types.Transaction, error) { + return _DestinationVerifier.Contract.SetConfig(&_DestinationVerifier.TransactOpts, signers, f, recipientAddressesAndWeights) +} + +func (_DestinationVerifier *DestinationVerifierTransactorSession) SetConfig(signers []common.Address, f uint8, recipientAddressesAndWeights []CommonAddressAndWeight) (*types.Transaction, error) { + return _DestinationVerifier.Contract.SetConfig(&_DestinationVerifier.TransactOpts, signers, f, recipientAddressesAndWeights) +} + +func (_DestinationVerifier *DestinationVerifierTransactor) SetConfigActive(opts *bind.TransactOpts, donConfigIndex *big.Int, isActive bool) (*types.Transaction, error) { + return _DestinationVerifier.contract.Transact(opts, "setConfigActive", donConfigIndex, isActive) +} + +func (_DestinationVerifier *DestinationVerifierSession) SetConfigActive(donConfigIndex *big.Int, isActive bool) (*types.Transaction, error) { + return _DestinationVerifier.Contract.SetConfigActive(&_DestinationVerifier.TransactOpts, donConfigIndex, isActive) +} + +func (_DestinationVerifier *DestinationVerifierTransactorSession) SetConfigActive(donConfigIndex *big.Int, isActive bool) (*types.Transaction, error) { + return _DestinationVerifier.Contract.SetConfigActive(&_DestinationVerifier.TransactOpts, donConfigIndex, isActive) +} + +func (_DestinationVerifier *DestinationVerifierTransactor) SetConfigWithActivationTime(opts *bind.TransactOpts, signers []common.Address, f uint8, recipientAddressesAndWeights []CommonAddressAndWeight, activationTime uint32) (*types.Transaction, error) { + return _DestinationVerifier.contract.Transact(opts, "setConfigWithActivationTime", signers, f, recipientAddressesAndWeights, activationTime) +} + +func (_DestinationVerifier *DestinationVerifierSession) SetConfigWithActivationTime(signers []common.Address, f uint8, recipientAddressesAndWeights []CommonAddressAndWeight, activationTime uint32) (*types.Transaction, error) { + return _DestinationVerifier.Contract.SetConfigWithActivationTime(&_DestinationVerifier.TransactOpts, signers, f, recipientAddressesAndWeights, activationTime) +} + +func (_DestinationVerifier *DestinationVerifierTransactorSession) SetConfigWithActivationTime(signers []common.Address, f uint8, recipientAddressesAndWeights []CommonAddressAndWeight, activationTime uint32) (*types.Transaction, error) { + return _DestinationVerifier.Contract.SetConfigWithActivationTime(&_DestinationVerifier.TransactOpts, signers, f, recipientAddressesAndWeights, activationTime) +} + +func (_DestinationVerifier *DestinationVerifierTransactor) SetFeeManager(opts *bind.TransactOpts, feeManager common.Address) (*types.Transaction, error) { + return _DestinationVerifier.contract.Transact(opts, "setFeeManager", feeManager) +} + +func (_DestinationVerifier *DestinationVerifierSession) SetFeeManager(feeManager common.Address) (*types.Transaction, error) { + return _DestinationVerifier.Contract.SetFeeManager(&_DestinationVerifier.TransactOpts, feeManager) +} + +func (_DestinationVerifier *DestinationVerifierTransactorSession) SetFeeManager(feeManager common.Address) (*types.Transaction, error) { + return _DestinationVerifier.Contract.SetFeeManager(&_DestinationVerifier.TransactOpts, feeManager) +} + +func (_DestinationVerifier *DestinationVerifierTransactor) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { + return _DestinationVerifier.contract.Transact(opts, "transferOwnership", to) +} + +func (_DestinationVerifier *DestinationVerifierSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _DestinationVerifier.Contract.TransferOwnership(&_DestinationVerifier.TransactOpts, to) +} + +func (_DestinationVerifier *DestinationVerifierTransactorSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _DestinationVerifier.Contract.TransferOwnership(&_DestinationVerifier.TransactOpts, to) +} + +func (_DestinationVerifier *DestinationVerifierTransactor) Verify(opts *bind.TransactOpts, signedReport []byte, parameterPayload []byte, sender common.Address) (*types.Transaction, error) { + return _DestinationVerifier.contract.Transact(opts, "verify", signedReport, parameterPayload, sender) +} + +func (_DestinationVerifier *DestinationVerifierSession) Verify(signedReport []byte, parameterPayload []byte, sender common.Address) (*types.Transaction, error) { + return _DestinationVerifier.Contract.Verify(&_DestinationVerifier.TransactOpts, signedReport, parameterPayload, sender) +} + +func (_DestinationVerifier *DestinationVerifierTransactorSession) Verify(signedReport []byte, parameterPayload []byte, sender common.Address) (*types.Transaction, error) { + return _DestinationVerifier.Contract.Verify(&_DestinationVerifier.TransactOpts, signedReport, parameterPayload, sender) +} + +func (_DestinationVerifier *DestinationVerifierTransactor) VerifyBulk(opts *bind.TransactOpts, signedReports [][]byte, parameterPayload []byte, sender common.Address) (*types.Transaction, error) { + return _DestinationVerifier.contract.Transact(opts, "verifyBulk", signedReports, parameterPayload, sender) +} + +func (_DestinationVerifier *DestinationVerifierSession) VerifyBulk(signedReports [][]byte, parameterPayload []byte, sender common.Address) (*types.Transaction, error) { + return _DestinationVerifier.Contract.VerifyBulk(&_DestinationVerifier.TransactOpts, signedReports, parameterPayload, sender) +} + +func (_DestinationVerifier *DestinationVerifierTransactorSession) VerifyBulk(signedReports [][]byte, parameterPayload []byte, sender common.Address) (*types.Transaction, error) { + return _DestinationVerifier.Contract.VerifyBulk(&_DestinationVerifier.TransactOpts, signedReports, parameterPayload, sender) +} + +type DestinationVerifierAccessControllerSetIterator struct { + Event *DestinationVerifierAccessControllerSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *DestinationVerifierAccessControllerSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(DestinationVerifierAccessControllerSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(DestinationVerifierAccessControllerSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *DestinationVerifierAccessControllerSetIterator) Error() error { + return it.fail +} + +func (it *DestinationVerifierAccessControllerSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type DestinationVerifierAccessControllerSet struct { + OldAccessController common.Address + NewAccessController common.Address + Raw types.Log +} + +func (_DestinationVerifier *DestinationVerifierFilterer) FilterAccessControllerSet(opts *bind.FilterOpts) (*DestinationVerifierAccessControllerSetIterator, error) { + + logs, sub, err := _DestinationVerifier.contract.FilterLogs(opts, "AccessControllerSet") + if err != nil { + return nil, err + } + return &DestinationVerifierAccessControllerSetIterator{contract: _DestinationVerifier.contract, event: "AccessControllerSet", logs: logs, sub: sub}, nil +} + +func (_DestinationVerifier *DestinationVerifierFilterer) WatchAccessControllerSet(opts *bind.WatchOpts, sink chan<- *DestinationVerifierAccessControllerSet) (event.Subscription, error) { + + logs, sub, err := _DestinationVerifier.contract.WatchLogs(opts, "AccessControllerSet") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(DestinationVerifierAccessControllerSet) + if err := _DestinationVerifier.contract.UnpackLog(event, "AccessControllerSet", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_DestinationVerifier *DestinationVerifierFilterer) ParseAccessControllerSet(log types.Log) (*DestinationVerifierAccessControllerSet, error) { + event := new(DestinationVerifierAccessControllerSet) + if err := _DestinationVerifier.contract.UnpackLog(event, "AccessControllerSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type DestinationVerifierConfigActivatedIterator struct { + Event *DestinationVerifierConfigActivated + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *DestinationVerifierConfigActivatedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(DestinationVerifierConfigActivated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(DestinationVerifierConfigActivated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *DestinationVerifierConfigActivatedIterator) Error() error { + return it.fail +} + +func (it *DestinationVerifierConfigActivatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type DestinationVerifierConfigActivated struct { + DonConfigId [24]byte + IsActive bool + Raw types.Log +} + +func (_DestinationVerifier *DestinationVerifierFilterer) FilterConfigActivated(opts *bind.FilterOpts) (*DestinationVerifierConfigActivatedIterator, error) { + + logs, sub, err := _DestinationVerifier.contract.FilterLogs(opts, "ConfigActivated") + if err != nil { + return nil, err + } + return &DestinationVerifierConfigActivatedIterator{contract: _DestinationVerifier.contract, event: "ConfigActivated", logs: logs, sub: sub}, nil +} + +func (_DestinationVerifier *DestinationVerifierFilterer) WatchConfigActivated(opts *bind.WatchOpts, sink chan<- *DestinationVerifierConfigActivated) (event.Subscription, error) { + + logs, sub, err := _DestinationVerifier.contract.WatchLogs(opts, "ConfigActivated") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(DestinationVerifierConfigActivated) + if err := _DestinationVerifier.contract.UnpackLog(event, "ConfigActivated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_DestinationVerifier *DestinationVerifierFilterer) ParseConfigActivated(log types.Log) (*DestinationVerifierConfigActivated, error) { + event := new(DestinationVerifierConfigActivated) + if err := _DestinationVerifier.contract.UnpackLog(event, "ConfigActivated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type DestinationVerifierConfigRemovedIterator struct { + Event *DestinationVerifierConfigRemoved + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *DestinationVerifierConfigRemovedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(DestinationVerifierConfigRemoved) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(DestinationVerifierConfigRemoved) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *DestinationVerifierConfigRemovedIterator) Error() error { + return it.fail +} + +func (it *DestinationVerifierConfigRemovedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type DestinationVerifierConfigRemoved struct { + DonConfigId [24]byte + Raw types.Log +} + +func (_DestinationVerifier *DestinationVerifierFilterer) FilterConfigRemoved(opts *bind.FilterOpts) (*DestinationVerifierConfigRemovedIterator, error) { + + logs, sub, err := _DestinationVerifier.contract.FilterLogs(opts, "ConfigRemoved") + if err != nil { + return nil, err + } + return &DestinationVerifierConfigRemovedIterator{contract: _DestinationVerifier.contract, event: "ConfigRemoved", logs: logs, sub: sub}, nil +} + +func (_DestinationVerifier *DestinationVerifierFilterer) WatchConfigRemoved(opts *bind.WatchOpts, sink chan<- *DestinationVerifierConfigRemoved) (event.Subscription, error) { + + logs, sub, err := _DestinationVerifier.contract.WatchLogs(opts, "ConfigRemoved") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(DestinationVerifierConfigRemoved) + if err := _DestinationVerifier.contract.UnpackLog(event, "ConfigRemoved", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_DestinationVerifier *DestinationVerifierFilterer) ParseConfigRemoved(log types.Log) (*DestinationVerifierConfigRemoved, error) { + event := new(DestinationVerifierConfigRemoved) + if err := _DestinationVerifier.contract.UnpackLog(event, "ConfigRemoved", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type DestinationVerifierConfigSetIterator struct { + Event *DestinationVerifierConfigSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *DestinationVerifierConfigSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(DestinationVerifierConfigSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(DestinationVerifierConfigSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *DestinationVerifierConfigSetIterator) Error() error { + return it.fail +} + +func (it *DestinationVerifierConfigSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type DestinationVerifierConfigSet struct { + DonConfigId [24]byte + Signers []common.Address + F uint8 + RecipientAddressesAndWeights []CommonAddressAndWeight + Raw types.Log +} + +func (_DestinationVerifier *DestinationVerifierFilterer) FilterConfigSet(opts *bind.FilterOpts, donConfigId [][24]byte) (*DestinationVerifierConfigSetIterator, error) { + + var donConfigIdRule []interface{} + for _, donConfigIdItem := range donConfigId { + donConfigIdRule = append(donConfigIdRule, donConfigIdItem) + } + + logs, sub, err := _DestinationVerifier.contract.FilterLogs(opts, "ConfigSet", donConfigIdRule) + if err != nil { + return nil, err + } + return &DestinationVerifierConfigSetIterator{contract: _DestinationVerifier.contract, event: "ConfigSet", logs: logs, sub: sub}, nil +} + +func (_DestinationVerifier *DestinationVerifierFilterer) WatchConfigSet(opts *bind.WatchOpts, sink chan<- *DestinationVerifierConfigSet, donConfigId [][24]byte) (event.Subscription, error) { + + var donConfigIdRule []interface{} + for _, donConfigIdItem := range donConfigId { + donConfigIdRule = append(donConfigIdRule, donConfigIdItem) + } + + logs, sub, err := _DestinationVerifier.contract.WatchLogs(opts, "ConfigSet", donConfigIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(DestinationVerifierConfigSet) + if err := _DestinationVerifier.contract.UnpackLog(event, "ConfigSet", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_DestinationVerifier *DestinationVerifierFilterer) ParseConfigSet(log types.Log) (*DestinationVerifierConfigSet, error) { + event := new(DestinationVerifierConfigSet) + if err := _DestinationVerifier.contract.UnpackLog(event, "ConfigSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type DestinationVerifierFeeManagerSetIterator struct { + Event *DestinationVerifierFeeManagerSet + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *DestinationVerifierFeeManagerSetIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(DestinationVerifierFeeManagerSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(DestinationVerifierFeeManagerSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *DestinationVerifierFeeManagerSetIterator) Error() error { + return it.fail +} + +func (it *DestinationVerifierFeeManagerSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type DestinationVerifierFeeManagerSet struct { + OldFeeManager common.Address + NewFeeManager common.Address + Raw types.Log +} + +func (_DestinationVerifier *DestinationVerifierFilterer) FilterFeeManagerSet(opts *bind.FilterOpts) (*DestinationVerifierFeeManagerSetIterator, error) { + + logs, sub, err := _DestinationVerifier.contract.FilterLogs(opts, "FeeManagerSet") + if err != nil { + return nil, err + } + return &DestinationVerifierFeeManagerSetIterator{contract: _DestinationVerifier.contract, event: "FeeManagerSet", logs: logs, sub: sub}, nil +} + +func (_DestinationVerifier *DestinationVerifierFilterer) WatchFeeManagerSet(opts *bind.WatchOpts, sink chan<- *DestinationVerifierFeeManagerSet) (event.Subscription, error) { + + logs, sub, err := _DestinationVerifier.contract.WatchLogs(opts, "FeeManagerSet") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(DestinationVerifierFeeManagerSet) + if err := _DestinationVerifier.contract.UnpackLog(event, "FeeManagerSet", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_DestinationVerifier *DestinationVerifierFilterer) ParseFeeManagerSet(log types.Log) (*DestinationVerifierFeeManagerSet, error) { + event := new(DestinationVerifierFeeManagerSet) + if err := _DestinationVerifier.contract.UnpackLog(event, "FeeManagerSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type DestinationVerifierOwnershipTransferRequestedIterator struct { + Event *DestinationVerifierOwnershipTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *DestinationVerifierOwnershipTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(DestinationVerifierOwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(DestinationVerifierOwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *DestinationVerifierOwnershipTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *DestinationVerifierOwnershipTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type DestinationVerifierOwnershipTransferRequested struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_DestinationVerifier *DestinationVerifierFilterer) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*DestinationVerifierOwnershipTransferRequestedIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _DestinationVerifier.contract.FilterLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return &DestinationVerifierOwnershipTransferRequestedIterator{contract: _DestinationVerifier.contract, event: "OwnershipTransferRequested", logs: logs, sub: sub}, nil +} + +func (_DestinationVerifier *DestinationVerifierFilterer) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *DestinationVerifierOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _DestinationVerifier.contract.WatchLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(DestinationVerifierOwnershipTransferRequested) + if err := _DestinationVerifier.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_DestinationVerifier *DestinationVerifierFilterer) ParseOwnershipTransferRequested(log types.Log) (*DestinationVerifierOwnershipTransferRequested, error) { + event := new(DestinationVerifierOwnershipTransferRequested) + if err := _DestinationVerifier.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type DestinationVerifierOwnershipTransferredIterator struct { + Event *DestinationVerifierOwnershipTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *DestinationVerifierOwnershipTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(DestinationVerifierOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(DestinationVerifierOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *DestinationVerifierOwnershipTransferredIterator) Error() error { + return it.fail +} + +func (it *DestinationVerifierOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type DestinationVerifierOwnershipTransferred struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_DestinationVerifier *DestinationVerifierFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*DestinationVerifierOwnershipTransferredIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _DestinationVerifier.contract.FilterLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return &DestinationVerifierOwnershipTransferredIterator{contract: _DestinationVerifier.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +func (_DestinationVerifier *DestinationVerifierFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *DestinationVerifierOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _DestinationVerifier.contract.WatchLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(DestinationVerifierOwnershipTransferred) + if err := _DestinationVerifier.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_DestinationVerifier *DestinationVerifierFilterer) ParseOwnershipTransferred(log types.Log) (*DestinationVerifierOwnershipTransferred, error) { + event := new(DestinationVerifierOwnershipTransferred) + if err := _DestinationVerifier.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type DestinationVerifierReportVerifiedIterator struct { + Event *DestinationVerifierReportVerified + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *DestinationVerifierReportVerifiedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(DestinationVerifierReportVerified) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(DestinationVerifierReportVerified) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *DestinationVerifierReportVerifiedIterator) Error() error { + return it.fail +} + +func (it *DestinationVerifierReportVerifiedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type DestinationVerifierReportVerified struct { + FeedId [32]byte + Requester common.Address + Raw types.Log +} + +func (_DestinationVerifier *DestinationVerifierFilterer) FilterReportVerified(opts *bind.FilterOpts, feedId [][32]byte) (*DestinationVerifierReportVerifiedIterator, error) { + + var feedIdRule []interface{} + for _, feedIdItem := range feedId { + feedIdRule = append(feedIdRule, feedIdItem) + } + + logs, sub, err := _DestinationVerifier.contract.FilterLogs(opts, "ReportVerified", feedIdRule) + if err != nil { + return nil, err + } + return &DestinationVerifierReportVerifiedIterator{contract: _DestinationVerifier.contract, event: "ReportVerified", logs: logs, sub: sub}, nil +} + +func (_DestinationVerifier *DestinationVerifierFilterer) WatchReportVerified(opts *bind.WatchOpts, sink chan<- *DestinationVerifierReportVerified, feedId [][32]byte) (event.Subscription, error) { + + var feedIdRule []interface{} + for _, feedIdItem := range feedId { + feedIdRule = append(feedIdRule, feedIdItem) + } + + logs, sub, err := _DestinationVerifier.contract.WatchLogs(opts, "ReportVerified", feedIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(DestinationVerifierReportVerified) + if err := _DestinationVerifier.contract.UnpackLog(event, "ReportVerified", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_DestinationVerifier *DestinationVerifierFilterer) ParseReportVerified(log types.Log) (*DestinationVerifierReportVerified, error) { + event := new(DestinationVerifierReportVerified) + if err := _DestinationVerifier.contract.UnpackLog(event, "ReportVerified", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +func (_DestinationVerifier *DestinationVerifier) ParseLog(log types.Log) (generated.AbigenLog, error) { + switch log.Topics[0] { + case _DestinationVerifier.abi.Events["AccessControllerSet"].ID: + return _DestinationVerifier.ParseAccessControllerSet(log) + case _DestinationVerifier.abi.Events["ConfigActivated"].ID: + return _DestinationVerifier.ParseConfigActivated(log) + case _DestinationVerifier.abi.Events["ConfigRemoved"].ID: + return _DestinationVerifier.ParseConfigRemoved(log) + case _DestinationVerifier.abi.Events["ConfigSet"].ID: + return _DestinationVerifier.ParseConfigSet(log) + case _DestinationVerifier.abi.Events["FeeManagerSet"].ID: + return _DestinationVerifier.ParseFeeManagerSet(log) + case _DestinationVerifier.abi.Events["OwnershipTransferRequested"].ID: + return _DestinationVerifier.ParseOwnershipTransferRequested(log) + case _DestinationVerifier.abi.Events["OwnershipTransferred"].ID: + return _DestinationVerifier.ParseOwnershipTransferred(log) + case _DestinationVerifier.abi.Events["ReportVerified"].ID: + return _DestinationVerifier.ParseReportVerified(log) + + default: + return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) + } +} + +func (DestinationVerifierAccessControllerSet) Topic() common.Hash { + return common.HexToHash("0x953e92b1a6442e9c3242531154a3f6f6eb00b4e9c719ba8118fa6235e4ce89b6") +} + +func (DestinationVerifierConfigActivated) Topic() common.Hash { + return common.HexToHash("0x90186a1e77b498ec417ea88bd026cae00d7043c357cc45221777623bda582dd4") +} + +func (DestinationVerifierConfigRemoved) Topic() common.Hash { + return common.HexToHash("0x970fd8f3ebdd9a271080aacf9807a5c709be0b448e4047a6fc212b8cc165368d") +} + +func (DestinationVerifierConfigSet) Topic() common.Hash { + return common.HexToHash("0x2d763a674a99583454a287d792819ffb9ff7e791c23e7745a082701136ce336c") +} + +func (DestinationVerifierFeeManagerSet) Topic() common.Hash { + return common.HexToHash("0x04628abcaa6b1674651352125cb94b65b289145bc2bc4d67720bb7d966372f03") +} + +func (DestinationVerifierOwnershipTransferRequested) Topic() common.Hash { + return common.HexToHash("0xed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278") +} + +func (DestinationVerifierOwnershipTransferred) Topic() common.Hash { + return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") +} + +func (DestinationVerifierReportVerified) Topic() common.Hash { + return common.HexToHash("0x58ca9502e98a536e06e72d680fcc251e5d10b72291a281665a2c2dc0ac30fcc5") +} + +func (_DestinationVerifier *DestinationVerifier) Address() common.Address { + return _DestinationVerifier.address +} + +type DestinationVerifierInterface interface { + IVerifierProxy(opts *bind.CallOpts) (common.Address, error) + + Owner(opts *bind.CallOpts) (common.Address, error) + + SAccessController(opts *bind.CallOpts) (common.Address, error) + + SFeeManager(opts *bind.CallOpts) (common.Address, error) + + SupportsInterface(opts *bind.CallOpts, interfaceId [4]byte) (bool, error) + + TypeAndVersion(opts *bind.CallOpts) (string, error) + + AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) + + RemoveLatestConfig(opts *bind.TransactOpts) (*types.Transaction, error) + + SetAccessController(opts *bind.TransactOpts, accessController common.Address) (*types.Transaction, error) + + SetConfig(opts *bind.TransactOpts, signers []common.Address, f uint8, recipientAddressesAndWeights []CommonAddressAndWeight) (*types.Transaction, error) + + SetConfigActive(opts *bind.TransactOpts, donConfigIndex *big.Int, isActive bool) (*types.Transaction, error) + + SetConfigWithActivationTime(opts *bind.TransactOpts, signers []common.Address, f uint8, recipientAddressesAndWeights []CommonAddressAndWeight, activationTime uint32) (*types.Transaction, error) + + SetFeeManager(opts *bind.TransactOpts, feeManager common.Address) (*types.Transaction, error) + + TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + + Verify(opts *bind.TransactOpts, signedReport []byte, parameterPayload []byte, sender common.Address) (*types.Transaction, error) + + VerifyBulk(opts *bind.TransactOpts, signedReports [][]byte, parameterPayload []byte, sender common.Address) (*types.Transaction, error) + + FilterAccessControllerSet(opts *bind.FilterOpts) (*DestinationVerifierAccessControllerSetIterator, error) + + WatchAccessControllerSet(opts *bind.WatchOpts, sink chan<- *DestinationVerifierAccessControllerSet) (event.Subscription, error) + + ParseAccessControllerSet(log types.Log) (*DestinationVerifierAccessControllerSet, error) + + FilterConfigActivated(opts *bind.FilterOpts) (*DestinationVerifierConfigActivatedIterator, error) + + WatchConfigActivated(opts *bind.WatchOpts, sink chan<- *DestinationVerifierConfigActivated) (event.Subscription, error) + + ParseConfigActivated(log types.Log) (*DestinationVerifierConfigActivated, error) + + FilterConfigRemoved(opts *bind.FilterOpts) (*DestinationVerifierConfigRemovedIterator, error) + + WatchConfigRemoved(opts *bind.WatchOpts, sink chan<- *DestinationVerifierConfigRemoved) (event.Subscription, error) + + ParseConfigRemoved(log types.Log) (*DestinationVerifierConfigRemoved, error) + + FilterConfigSet(opts *bind.FilterOpts, donConfigId [][24]byte) (*DestinationVerifierConfigSetIterator, error) + + WatchConfigSet(opts *bind.WatchOpts, sink chan<- *DestinationVerifierConfigSet, donConfigId [][24]byte) (event.Subscription, error) + + ParseConfigSet(log types.Log) (*DestinationVerifierConfigSet, error) + + FilterFeeManagerSet(opts *bind.FilterOpts) (*DestinationVerifierFeeManagerSetIterator, error) + + WatchFeeManagerSet(opts *bind.WatchOpts, sink chan<- *DestinationVerifierFeeManagerSet) (event.Subscription, error) + + ParseFeeManagerSet(log types.Log) (*DestinationVerifierFeeManagerSet, error) + + FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*DestinationVerifierOwnershipTransferRequestedIterator, error) + + WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *DestinationVerifierOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferRequested(log types.Log) (*DestinationVerifierOwnershipTransferRequested, error) + + FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*DestinationVerifierOwnershipTransferredIterator, error) + + WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *DestinationVerifierOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferred(log types.Log) (*DestinationVerifierOwnershipTransferred, error) + + FilterReportVerified(opts *bind.FilterOpts, feedId [][32]byte) (*DestinationVerifierReportVerifiedIterator, error) + + WatchReportVerified(opts *bind.WatchOpts, sink chan<- *DestinationVerifierReportVerified, feedId [][32]byte) (event.Subscription, error) + + ParseReportVerified(log types.Log) (*DestinationVerifierReportVerified, error) + + ParseLog(log types.Log) (generated.AbigenLog, error) + + Address() common.Address +} diff --git a/core/gethwrappers/llo-feeds/generated/destination_verifier_proxy/destination_verifier_proxy.go b/core/gethwrappers/llo-feeds/generated/destination_verifier_proxy/destination_verifier_proxy.go new file mode 100644 index 0000000000..a2ae546d9b --- /dev/null +++ b/core/gethwrappers/llo-feeds/generated/destination_verifier_proxy/destination_verifier_proxy.go @@ -0,0 +1,676 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package destination_verifier_proxy + +import ( + "errors" + "fmt" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" +) + +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +var DestinationVerifierProxyMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"verifierAddress\",\"type\":\"address\"}],\"name\":\"VerifierInvalid\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddress\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_accessController\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"s_feeManager\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"verifierAddress\",\"type\":\"address\"}],\"name\":\"setVerifier\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"payload\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"parameterPayload\",\"type\":\"bytes\"}],\"name\":\"verify\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes[]\",\"name\":\"payloads\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes\",\"name\":\"parameterPayload\",\"type\":\"bytes\"}],\"name\":\"verifyBulk\",\"outputs\":[{\"internalType\":\"bytes[]\",\"name\":\"verifiedReports\",\"type\":\"bytes[]\"}],\"stateMutability\":\"payable\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b5033806000816100675760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615610097576100978161009f565b505050610148565b336001600160a01b038216036100f75760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640161005e565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b61130b806101576000396000f3fe6080604052600436106100b15760003560e01c80638da5cb5b11610069578063f2fde38b1161004e578063f2fde38b146101eb578063f7e83aee1461020b578063f873a61c1461021e57600080fd5b80638da5cb5b146101ab57806394ba2846146101d657600080fd5b806338416b5b1161009a57806338416b5b1461013a5780635437988d1461017457806379ba50971461019657600080fd5b806301ffc9a7146100b6578063181f5a77146100eb575b600080fd5b3480156100c257600080fd5b506100d66100d1366004610c56565b61023e565b60405190151581526020015b60405180910390f35b3480156100f757600080fd5b5060408051808201909152601e81527f44657374696e6174696f6e566572696669657250726f787920312e302e30000060208201525b6040516100e29190610d0d565b34801561014657600080fd5b5061014f6103bb565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100e2565b34801561018057600080fd5b5061019461018f366004610d42565b610454565b005b3480156101a257600080fd5b506101946107c8565b3480156101b757600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff1661014f565b3480156101e257600080fd5b5061014f6108c5565b3480156101f757600080fd5b50610194610206366004610d42565b610935565b61012d610219366004610da8565b610949565b61023161022c366004610e14565b610a18565b6040516100e29190610e95565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f5437988d0000000000000000000000000000000000000000000000000000000014806102d157507fffffffff0000000000000000000000000000000000000000000000000000000082167ff7e83aee00000000000000000000000000000000000000000000000000000000145b8061031d57507fffffffff0000000000000000000000000000000000000000000000000000000082167ff873a61c00000000000000000000000000000000000000000000000000000000145b8061036957507fffffffff0000000000000000000000000000000000000000000000000000000082167f38416b5b00000000000000000000000000000000000000000000000000000000145b806103b557507fffffffff0000000000000000000000000000000000000000000000000000000082167f94ba284600000000000000000000000000000000000000000000000000000000145b92915050565b600254604080517f38416b5b000000000000000000000000000000000000000000000000000000008152905160009273ffffffffffffffffffffffffffffffffffffffff16916338416b5b9160048083019260209291908290030181865afa15801561042b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061044f9190610f15565b905090565b61045c610ade565b6040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527f94ba284600000000000000000000000000000000000000000000000000000000600482015273ffffffffffffffffffffffffffffffffffffffff8216906301ffc9a790602401602060405180830381865afa1580156104e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061050a9190610f32565b15806105c157506040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527f38416b5b00000000000000000000000000000000000000000000000000000000600482015273ffffffffffffffffffffffffffffffffffffffff8216906301ffc9a790602401602060405180830381865afa15801561059b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105bf9190610f32565b155b8061067757506040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527f294d2bb100000000000000000000000000000000000000000000000000000000600482015273ffffffffffffffffffffffffffffffffffffffff8216906301ffc9a790602401602060405180830381865afa158015610651573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106759190610f32565b155b8061072d57506040517f01ffc9a70000000000000000000000000000000000000000000000000000000081527fd7c72e4e00000000000000000000000000000000000000000000000000000000600482015273ffffffffffffffffffffffffffffffffffffffff8216906301ffc9a790602401602060405180830381865afa158015610707573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061072b9190610f32565b155b15610781576040517f96ac86f300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024015b60405180910390fd5b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60015473ffffffffffffffffffffffffffffffffffffffff163314610849576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610778565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b600254604080517f94ba2846000000000000000000000000000000000000000000000000000000008152905160009273ffffffffffffffffffffffffffffffffffffffff16916394ba28469160048083019260209291908290030181865afa15801561042b573d6000803e3d6000fd5b61093d610ade565b61094681610b61565b50565b6002546040517f294d2bb100000000000000000000000000000000000000000000000000000000815260609173ffffffffffffffffffffffffffffffffffffffff169063294d2bb19034906109aa9089908990899089903390600401610f9d565b60006040518083038185885af11580156109c8573d6000803e3d6000fd5b50505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610a0f91908101906110f5565b95945050505050565b6002546040517fd7c72e4e00000000000000000000000000000000000000000000000000000000815260609173ffffffffffffffffffffffffffffffffffffffff169063d7c72e4e903490610a79908990899089908990339060040161112a565b60006040518083038185885af1158015610a97573d6000803e3d6000fd5b50505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610a0f919081019061123b565b60005473ffffffffffffffffffffffffffffffffffffffff163314610b5f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610778565b565b3373ffffffffffffffffffffffffffffffffffffffff821603610be0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610778565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600060208284031215610c6857600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610c9857600080fd5b9392505050565b60005b83811015610cba578181015183820152602001610ca2565b50506000910152565b60008151808452610cdb816020860160208601610c9f565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610c986020830184610cc3565b73ffffffffffffffffffffffffffffffffffffffff8116811461094657600080fd5b600060208284031215610d5457600080fd5b8135610c9881610d20565b60008083601f840112610d7157600080fd5b50813567ffffffffffffffff811115610d8957600080fd5b602083019150836020828501011115610da157600080fd5b9250929050565b60008060008060408587031215610dbe57600080fd5b843567ffffffffffffffff80821115610dd657600080fd5b610de288838901610d5f565b90965094506020870135915080821115610dfb57600080fd5b50610e0887828801610d5f565b95989497509550505050565b60008060008060408587031215610e2a57600080fd5b843567ffffffffffffffff80821115610e4257600080fd5b818701915087601f830112610e5657600080fd5b813581811115610e6557600080fd5b8860208260051b8501011115610e7a57600080fd5b602092830196509450908601359080821115610dfb57600080fd5b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015610f08577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0888603018452610ef6858351610cc3565b94509285019290850190600101610ebc565b5092979650505050505050565b600060208284031215610f2757600080fd5b8151610c9881610d20565b600060208284031215610f4457600080fd5b81518015158114610c9857600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b606081526000610fb1606083018789610f54565b8281036020840152610fc4818688610f54565b91505073ffffffffffffffffffffffffffffffffffffffff831660408301529695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561106357611063610fed565b604052919050565b600082601f83011261107c57600080fd5b815167ffffffffffffffff81111561109657611096610fed565b6110c760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161101c565b8181528460208386010111156110dc57600080fd5b6110ed826020830160208701610c9f565b949350505050565b60006020828403121561110757600080fd5b815167ffffffffffffffff81111561111e57600080fd5b6110ed8482850161106b565b6060808252810185905260006080600587901b8301810190830188835b898110156111f6577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8086850301835281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18c36030181126111a857600080fd5b8b01602081810191359067ffffffffffffffff8211156111c757600080fd5b8136038313156111d657600080fd5b6111e1878385610f54565b96509485019493909301925050600101611147565b505050828103602084015261120c818688610f54565b915050611231604083018473ffffffffffffffffffffffffffffffffffffffff169052565b9695505050505050565b6000602080838503121561124e57600080fd5b825167ffffffffffffffff8082111561126657600080fd5b818501915085601f83011261127a57600080fd5b81518181111561128c5761128c610fed565b8060051b61129b85820161101c565b91825283810185019185810190898411156112b557600080fd5b86860192505b838310156112f1578251858111156112d35760008081fd5b6112e18b89838a010161106b565b83525091860191908601906112bb565b999850505050505050505056fea164736f6c6343000813000a", +} + +var DestinationVerifierProxyABI = DestinationVerifierProxyMetaData.ABI + +var DestinationVerifierProxyBin = DestinationVerifierProxyMetaData.Bin + +func DeployDestinationVerifierProxy(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *DestinationVerifierProxy, error) { + parsed, err := DestinationVerifierProxyMetaData.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(DestinationVerifierProxyBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &DestinationVerifierProxy{address: address, abi: *parsed, DestinationVerifierProxyCaller: DestinationVerifierProxyCaller{contract: contract}, DestinationVerifierProxyTransactor: DestinationVerifierProxyTransactor{contract: contract}, DestinationVerifierProxyFilterer: DestinationVerifierProxyFilterer{contract: contract}}, nil +} + +type DestinationVerifierProxy struct { + address common.Address + abi abi.ABI + DestinationVerifierProxyCaller + DestinationVerifierProxyTransactor + DestinationVerifierProxyFilterer +} + +type DestinationVerifierProxyCaller struct { + contract *bind.BoundContract +} + +type DestinationVerifierProxyTransactor struct { + contract *bind.BoundContract +} + +type DestinationVerifierProxyFilterer struct { + contract *bind.BoundContract +} + +type DestinationVerifierProxySession struct { + Contract *DestinationVerifierProxy + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type DestinationVerifierProxyCallerSession struct { + Contract *DestinationVerifierProxyCaller + CallOpts bind.CallOpts +} + +type DestinationVerifierProxyTransactorSession struct { + Contract *DestinationVerifierProxyTransactor + TransactOpts bind.TransactOpts +} + +type DestinationVerifierProxyRaw struct { + Contract *DestinationVerifierProxy +} + +type DestinationVerifierProxyCallerRaw struct { + Contract *DestinationVerifierProxyCaller +} + +type DestinationVerifierProxyTransactorRaw struct { + Contract *DestinationVerifierProxyTransactor +} + +func NewDestinationVerifierProxy(address common.Address, backend bind.ContractBackend) (*DestinationVerifierProxy, error) { + abi, err := abi.JSON(strings.NewReader(DestinationVerifierProxyABI)) + if err != nil { + return nil, err + } + contract, err := bindDestinationVerifierProxy(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &DestinationVerifierProxy{address: address, abi: abi, DestinationVerifierProxyCaller: DestinationVerifierProxyCaller{contract: contract}, DestinationVerifierProxyTransactor: DestinationVerifierProxyTransactor{contract: contract}, DestinationVerifierProxyFilterer: DestinationVerifierProxyFilterer{contract: contract}}, nil +} + +func NewDestinationVerifierProxyCaller(address common.Address, caller bind.ContractCaller) (*DestinationVerifierProxyCaller, error) { + contract, err := bindDestinationVerifierProxy(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &DestinationVerifierProxyCaller{contract: contract}, nil +} + +func NewDestinationVerifierProxyTransactor(address common.Address, transactor bind.ContractTransactor) (*DestinationVerifierProxyTransactor, error) { + contract, err := bindDestinationVerifierProxy(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &DestinationVerifierProxyTransactor{contract: contract}, nil +} + +func NewDestinationVerifierProxyFilterer(address common.Address, filterer bind.ContractFilterer) (*DestinationVerifierProxyFilterer, error) { + contract, err := bindDestinationVerifierProxy(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &DestinationVerifierProxyFilterer{contract: contract}, nil +} + +func bindDestinationVerifierProxy(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := DestinationVerifierProxyMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _DestinationVerifierProxy.Contract.DestinationVerifierProxyCaller.contract.Call(opts, result, method, params...) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _DestinationVerifierProxy.Contract.DestinationVerifierProxyTransactor.contract.Transfer(opts) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _DestinationVerifierProxy.Contract.DestinationVerifierProxyTransactor.contract.Transact(opts, method, params...) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _DestinationVerifierProxy.Contract.contract.Call(opts, result, method, params...) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _DestinationVerifierProxy.Contract.contract.Transfer(opts) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _DestinationVerifierProxy.Contract.contract.Transact(opts, method, params...) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _DestinationVerifierProxy.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_DestinationVerifierProxy *DestinationVerifierProxySession) Owner() (common.Address, error) { + return _DestinationVerifierProxy.Contract.Owner(&_DestinationVerifierProxy.CallOpts) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyCallerSession) Owner() (common.Address, error) { + return _DestinationVerifierProxy.Contract.Owner(&_DestinationVerifierProxy.CallOpts) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyCaller) SAccessController(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _DestinationVerifierProxy.contract.Call(opts, &out, "s_accessController") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_DestinationVerifierProxy *DestinationVerifierProxySession) SAccessController() (common.Address, error) { + return _DestinationVerifierProxy.Contract.SAccessController(&_DestinationVerifierProxy.CallOpts) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyCallerSession) SAccessController() (common.Address, error) { + return _DestinationVerifierProxy.Contract.SAccessController(&_DestinationVerifierProxy.CallOpts) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyCaller) SFeeManager(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _DestinationVerifierProxy.contract.Call(opts, &out, "s_feeManager") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_DestinationVerifierProxy *DestinationVerifierProxySession) SFeeManager() (common.Address, error) { + return _DestinationVerifierProxy.Contract.SFeeManager(&_DestinationVerifierProxy.CallOpts) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyCallerSession) SFeeManager() (common.Address, error) { + return _DestinationVerifierProxy.Contract.SFeeManager(&_DestinationVerifierProxy.CallOpts) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyCaller) SupportsInterface(opts *bind.CallOpts, interfaceId [4]byte) (bool, error) { + var out []interface{} + err := _DestinationVerifierProxy.contract.Call(opts, &out, "supportsInterface", interfaceId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +func (_DestinationVerifierProxy *DestinationVerifierProxySession) SupportsInterface(interfaceId [4]byte) (bool, error) { + return _DestinationVerifierProxy.Contract.SupportsInterface(&_DestinationVerifierProxy.CallOpts, interfaceId) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyCallerSession) SupportsInterface(interfaceId [4]byte) (bool, error) { + return _DestinationVerifierProxy.Contract.SupportsInterface(&_DestinationVerifierProxy.CallOpts, interfaceId) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyCaller) TypeAndVersion(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _DestinationVerifierProxy.contract.Call(opts, &out, "typeAndVersion") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +func (_DestinationVerifierProxy *DestinationVerifierProxySession) TypeAndVersion() (string, error) { + return _DestinationVerifierProxy.Contract.TypeAndVersion(&_DestinationVerifierProxy.CallOpts) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyCallerSession) TypeAndVersion() (string, error) { + return _DestinationVerifierProxy.Contract.TypeAndVersion(&_DestinationVerifierProxy.CallOpts) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyTransactor) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _DestinationVerifierProxy.contract.Transact(opts, "acceptOwnership") +} + +func (_DestinationVerifierProxy *DestinationVerifierProxySession) AcceptOwnership() (*types.Transaction, error) { + return _DestinationVerifierProxy.Contract.AcceptOwnership(&_DestinationVerifierProxy.TransactOpts) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyTransactorSession) AcceptOwnership() (*types.Transaction, error) { + return _DestinationVerifierProxy.Contract.AcceptOwnership(&_DestinationVerifierProxy.TransactOpts) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyTransactor) SetVerifier(opts *bind.TransactOpts, verifierAddress common.Address) (*types.Transaction, error) { + return _DestinationVerifierProxy.contract.Transact(opts, "setVerifier", verifierAddress) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxySession) SetVerifier(verifierAddress common.Address) (*types.Transaction, error) { + return _DestinationVerifierProxy.Contract.SetVerifier(&_DestinationVerifierProxy.TransactOpts, verifierAddress) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyTransactorSession) SetVerifier(verifierAddress common.Address) (*types.Transaction, error) { + return _DestinationVerifierProxy.Contract.SetVerifier(&_DestinationVerifierProxy.TransactOpts, verifierAddress) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyTransactor) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { + return _DestinationVerifierProxy.contract.Transact(opts, "transferOwnership", to) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxySession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _DestinationVerifierProxy.Contract.TransferOwnership(&_DestinationVerifierProxy.TransactOpts, to) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyTransactorSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _DestinationVerifierProxy.Contract.TransferOwnership(&_DestinationVerifierProxy.TransactOpts, to) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyTransactor) Verify(opts *bind.TransactOpts, payload []byte, parameterPayload []byte) (*types.Transaction, error) { + return _DestinationVerifierProxy.contract.Transact(opts, "verify", payload, parameterPayload) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxySession) Verify(payload []byte, parameterPayload []byte) (*types.Transaction, error) { + return _DestinationVerifierProxy.Contract.Verify(&_DestinationVerifierProxy.TransactOpts, payload, parameterPayload) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyTransactorSession) Verify(payload []byte, parameterPayload []byte) (*types.Transaction, error) { + return _DestinationVerifierProxy.Contract.Verify(&_DestinationVerifierProxy.TransactOpts, payload, parameterPayload) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyTransactor) VerifyBulk(opts *bind.TransactOpts, payloads [][]byte, parameterPayload []byte) (*types.Transaction, error) { + return _DestinationVerifierProxy.contract.Transact(opts, "verifyBulk", payloads, parameterPayload) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxySession) VerifyBulk(payloads [][]byte, parameterPayload []byte) (*types.Transaction, error) { + return _DestinationVerifierProxy.Contract.VerifyBulk(&_DestinationVerifierProxy.TransactOpts, payloads, parameterPayload) +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyTransactorSession) VerifyBulk(payloads [][]byte, parameterPayload []byte) (*types.Transaction, error) { + return _DestinationVerifierProxy.Contract.VerifyBulk(&_DestinationVerifierProxy.TransactOpts, payloads, parameterPayload) +} + +type DestinationVerifierProxyOwnershipTransferRequestedIterator struct { + Event *DestinationVerifierProxyOwnershipTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *DestinationVerifierProxyOwnershipTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(DestinationVerifierProxyOwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(DestinationVerifierProxyOwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *DestinationVerifierProxyOwnershipTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *DestinationVerifierProxyOwnershipTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type DestinationVerifierProxyOwnershipTransferRequested struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyFilterer) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*DestinationVerifierProxyOwnershipTransferRequestedIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _DestinationVerifierProxy.contract.FilterLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return &DestinationVerifierProxyOwnershipTransferRequestedIterator{contract: _DestinationVerifierProxy.contract, event: "OwnershipTransferRequested", logs: logs, sub: sub}, nil +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyFilterer) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *DestinationVerifierProxyOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _DestinationVerifierProxy.contract.WatchLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(DestinationVerifierProxyOwnershipTransferRequested) + if err := _DestinationVerifierProxy.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyFilterer) ParseOwnershipTransferRequested(log types.Log) (*DestinationVerifierProxyOwnershipTransferRequested, error) { + event := new(DestinationVerifierProxyOwnershipTransferRequested) + if err := _DestinationVerifierProxy.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type DestinationVerifierProxyOwnershipTransferredIterator struct { + Event *DestinationVerifierProxyOwnershipTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *DestinationVerifierProxyOwnershipTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(DestinationVerifierProxyOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(DestinationVerifierProxyOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *DestinationVerifierProxyOwnershipTransferredIterator) Error() error { + return it.fail +} + +func (it *DestinationVerifierProxyOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type DestinationVerifierProxyOwnershipTransferred struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*DestinationVerifierProxyOwnershipTransferredIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _DestinationVerifierProxy.contract.FilterLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return &DestinationVerifierProxyOwnershipTransferredIterator{contract: _DestinationVerifierProxy.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *DestinationVerifierProxyOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _DestinationVerifierProxy.contract.WatchLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(DestinationVerifierProxyOwnershipTransferred) + if err := _DestinationVerifierProxy.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_DestinationVerifierProxy *DestinationVerifierProxyFilterer) ParseOwnershipTransferred(log types.Log) (*DestinationVerifierProxyOwnershipTransferred, error) { + event := new(DestinationVerifierProxyOwnershipTransferred) + if err := _DestinationVerifierProxy.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +func (_DestinationVerifierProxy *DestinationVerifierProxy) ParseLog(log types.Log) (generated.AbigenLog, error) { + switch log.Topics[0] { + case _DestinationVerifierProxy.abi.Events["OwnershipTransferRequested"].ID: + return _DestinationVerifierProxy.ParseOwnershipTransferRequested(log) + case _DestinationVerifierProxy.abi.Events["OwnershipTransferred"].ID: + return _DestinationVerifierProxy.ParseOwnershipTransferred(log) + + default: + return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) + } +} + +func (DestinationVerifierProxyOwnershipTransferRequested) Topic() common.Hash { + return common.HexToHash("0xed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278") +} + +func (DestinationVerifierProxyOwnershipTransferred) Topic() common.Hash { + return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") +} + +func (_DestinationVerifierProxy *DestinationVerifierProxy) Address() common.Address { + return _DestinationVerifierProxy.address +} + +type DestinationVerifierProxyInterface interface { + Owner(opts *bind.CallOpts) (common.Address, error) + + SAccessController(opts *bind.CallOpts) (common.Address, error) + + SFeeManager(opts *bind.CallOpts) (common.Address, error) + + SupportsInterface(opts *bind.CallOpts, interfaceId [4]byte) (bool, error) + + TypeAndVersion(opts *bind.CallOpts) (string, error) + + AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) + + SetVerifier(opts *bind.TransactOpts, verifierAddress common.Address) (*types.Transaction, error) + + TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + + Verify(opts *bind.TransactOpts, payload []byte, parameterPayload []byte) (*types.Transaction, error) + + VerifyBulk(opts *bind.TransactOpts, payloads [][]byte, parameterPayload []byte) (*types.Transaction, error) + + FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*DestinationVerifierProxyOwnershipTransferRequestedIterator, error) + + WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *DestinationVerifierProxyOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferRequested(log types.Log) (*DestinationVerifierProxyOwnershipTransferRequested, error) + + FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*DestinationVerifierProxyOwnershipTransferredIterator, error) + + WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *DestinationVerifierProxyOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferred(log types.Log) (*DestinationVerifierProxyOwnershipTransferred, error) + + ParseLog(log types.Log) (generated.AbigenLog, error) + + Address() common.Address +} diff --git a/core/gethwrappers/llo-feeds/generated/errored_verifier/errored_verifier.go b/core/gethwrappers/llo-feeds/generated/errored_verifier/errored_verifier.go index 4d140ea064..f834686dfe 100644 --- a/core/gethwrappers/llo-feeds/generated/errored_verifier/errored_verifier.go +++ b/core/gethwrappers/llo-feeds/generated/errored_verifier/errored_verifier.go @@ -34,8 +34,8 @@ type CommonAddressAndWeight struct { } var ErroredVerifierMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"activateConfig\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"activateFeed\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"deactivateConfig\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"deactivateFeed\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"latestConfigDetails\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"latestConfigDigestAndEpoch\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"weight\",\"type\":\"uint64\"}],\"internalType\":\"structCommon.AddressAndWeight[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"},{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"weight\",\"type\":\"uint64\"}],\"internalType\":\"structCommon.AddressAndWeight[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"name\":\"setConfigFromSource\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"verify\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", - Bin: "0x608060405234801561001057600080fd5b50610c2e806100206000396000f3fe608060405234801561001057600080fd5b50600436106100be5760003560e01c8063b70d929d11610076578063e7db9c2a1161005b578063e7db9c2a146101d1578063e84f128e146101e4578063f01072211461021a57600080fd5b8063b70d929d14610188578063ded6307c146101be57600080fd5b80633dd86430116100a75780633dd864301461014d578063564a0a7a1461016257806394d959801461017557600080fd5b806301ffc9a7146100c35780633d3ac1b51461012d575b600080fd5b6101186100d136600461059a565b7fffffffff00000000000000000000000000000000000000000000000000000000167f3d3ac1b5000000000000000000000000000000000000000000000000000000001490565b60405190151581526020015b60405180910390f35b61014061013b366004610741565b610228565b604051610124919061078f565b61016061015b3660046107fb565b610292565b005b6101606101703660046107fb565b6102f4565b610160610183366004610814565b610356565b61019b6101963660046107fb565b6103b8565b604080519315158452602084019290925263ffffffff1690820152606001610124565b6101606101cc366004610814565b610447565b6101606101df3660046109f1565b6104a9565b6101f76101f23660046107fb565b61050b565b6040805163ffffffff948516815293909216602084015290820152606001610124565b6101606101df366004610b24565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f4661696c656420746f207665726966790000000000000000000000000000000060448201526060906064015b60405180910390fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4661696c656420746f20616374697661746520666565640000000000000000006044820152606401610289565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f4661696c656420746f20646561637469766174652066656564000000000000006044820152606401610289565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4661696c656420746f206465616374697661746520636f6e66696700000000006044820152606401610289565b60008060006040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610289906020808252602c908201527f4661696c656420746f20676574206c617465737420636f6e666967206469676560408201527f737420616e642065706f63680000000000000000000000000000000000000000606082015260800190565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f4661696c656420746f20616374697661746520636f6e666967000000000000006044820152606401610289565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f4661696c656420746f2073657420636f6e6669670000000000000000000000006044820152606401610289565b60008060006040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102899060208082526023908201527f4661696c656420746f20676574206c617465737420636f6e666967206465746160408201527f696c730000000000000000000000000000000000000000000000000000000000606082015260800190565b6000602082840312156105ac57600080fd5b81357fffffffff00000000000000000000000000000000000000000000000000000000811681146105dc57600080fd5b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715610635576106356105e3565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715610682576106826105e3565b604052919050565b600082601f83011261069b57600080fd5b813567ffffffffffffffff8111156106b5576106b56105e3565b6106e660207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161063b565b8181528460208386010111156106fb57600080fd5b816020850160208301376000918101602001919091529392505050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461073c57600080fd5b919050565b6000806040838503121561075457600080fd5b823567ffffffffffffffff81111561076b57600080fd5b6107778582860161068a565b92505061078660208401610718565b90509250929050565b600060208083528351808285015260005b818110156107bc578581018301518582016040015282016107a0565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b60006020828403121561080d57600080fd5b5035919050565b6000806040838503121561082757600080fd5b50508035926020909101359150565b803563ffffffff8116811461073c57600080fd5b600067ffffffffffffffff821115610864576108646105e3565b5060051b60200190565b600082601f83011261087f57600080fd5b8135602061089461088f8361084a565b61063b565b82815260059290921b840181019181810190868411156108b357600080fd5b8286015b848110156108d5576108c881610718565b83529183019183016108b7565b509695505050505050565b600082601f8301126108f157600080fd5b8135602061090161088f8361084a565b82815260059290921b8401810191818101908684111561092057600080fd5b8286015b848110156108d55780358352918301918301610924565b803560ff8116811461073c57600080fd5b803567ffffffffffffffff8116811461073c57600080fd5b600082601f83011261097557600080fd5b8135602061098561088f8361084a565b82815260069290921b840181019181810190868411156109a457600080fd5b8286015b848110156108d557604081890312156109c15760008081fd5b6109c9610612565b6109d282610718565b81526109df85830161094c565b818601528352918301916040016109a8565b60008060008060008060008060008060006101608c8e031215610a1357600080fd5b8b359a5060208c01359950610a2a60408d01610718565b9850610a3860608d01610836565b975067ffffffffffffffff8060808e01351115610a5457600080fd5b610a648e60808f01358f0161086e565b97508060a08e01351115610a7757600080fd5b610a878e60a08f01358f016108e0565b9650610a9560c08e0161093b565b95508060e08e01351115610aa857600080fd5b610ab88e60e08f01358f0161068a565b9450610ac76101008e0161094c565b9350806101208e01351115610adb57600080fd5b610aec8e6101208f01358f0161068a565b9250806101408e01351115610b0057600080fd5b50610b128d6101408e01358e01610964565b90509295989b509295989b9093969950565b600080600080600080600080610100898b031215610b4157600080fd5b88359750602089013567ffffffffffffffff80821115610b6057600080fd5b610b6c8c838d0161086e565b985060408b0135915080821115610b8257600080fd5b610b8e8c838d016108e0565b9750610b9c60608c0161093b565b965060808b0135915080821115610bb257600080fd5b610bbe8c838d0161068a565b9550610bcc60a08c0161094c565b945060c08b0135915080821115610be257600080fd5b610bee8c838d0161068a565b935060e08b0135915080821115610c0457600080fd5b50610c118b828c01610964565b915050929598509295989093965056fea164736f6c6343000813000a", + ABI: "[{\"inputs\":[],\"name\":\"FailedToActivateConfig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FailedToActivateFeed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FailedToDeactivateConfig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FailedToDeactivateFeed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FailedToGetLatestConfigDetails\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FailedToGetLatestConfigDigestAndEpoch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FailedToSetConfig\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FailedToVerify\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"activateConfig\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"activateFeed\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"deactivateConfig\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"deactivateFeed\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"latestConfigDetails\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"latestConfigDigestAndEpoch\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"weight\",\"type\":\"uint64\"}],\"internalType\":\"structCommon.AddressAndWeight[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"},{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"},{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"weight\",\"type\":\"uint64\"}],\"internalType\":\"structCommon.AddressAndWeight[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"name\":\"setConfigFromSource\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"verify\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b50610a58806100206000396000f3fe608060405234801561001057600080fd5b50600436106100be5760003560e01c8063b70d929d11610076578063e7db9c2a1161005b578063e7db9c2a146101d1578063e84f128e146101e4578063f01072211461021a57600080fd5b8063b70d929d14610188578063ded6307c146101be57600080fd5b80633dd86430116100a75780633dd864301461014d578063564a0a7a1461016257806394d959801461017557600080fd5b806301ffc9a7146100c35780633d3ac1b51461012d575b600080fd5b6101186100d13660046103c4565b7fffffffff00000000000000000000000000000000000000000000000000000000167f3d3ac1b5000000000000000000000000000000000000000000000000000000001490565b60405190151581526020015b60405180910390f35b61014061013b36600461056b565b610228565b60405161012491906105b9565b61016061015b366004610625565b61025c565b005b610160610170366004610625565b61028e565b61016061018336600461063e565b6102c0565b61019b610196366004610625565b6102f2565b604080519315158452602084019290925263ffffffff1690820152606001610124565b6101606101cc36600461063e565b610329565b6101606101df36600461081b565b61035b565b6101f76101f2366004610625565b61038d565b6040805163ffffffff948516815293909216602084015290820152606001610124565b6101606101df36600461094e565b60606040517fcf2e344600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f9601b68300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fa03564b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f8a406e4600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060006040517fbbc0083000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f7adb7c9600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f35e91bf100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060006040517fa06d64a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000602082840312156103d657600080fd5b81357fffffffff000000000000000000000000000000000000000000000000000000008116811461040657600080fd5b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff8111828210171561045f5761045f61040d565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156104ac576104ac61040d565b604052919050565b600082601f8301126104c557600080fd5b813567ffffffffffffffff8111156104df576104df61040d565b61051060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601610465565b81815284602083860101111561052557600080fd5b816020850160208301376000918101602001919091529392505050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461056657600080fd5b919050565b6000806040838503121561057e57600080fd5b823567ffffffffffffffff81111561059557600080fd5b6105a1858286016104b4565b9250506105b060208401610542565b90509250929050565b600060208083528351808285015260005b818110156105e6578581018301518582016040015282016105ca565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b60006020828403121561063757600080fd5b5035919050565b6000806040838503121561065157600080fd5b50508035926020909101359150565b803563ffffffff8116811461056657600080fd5b600067ffffffffffffffff82111561068e5761068e61040d565b5060051b60200190565b600082601f8301126106a957600080fd5b813560206106be6106b983610674565b610465565b82815260059290921b840181019181810190868411156106dd57600080fd5b8286015b848110156106ff576106f281610542565b83529183019183016106e1565b509695505050505050565b600082601f83011261071b57600080fd5b8135602061072b6106b983610674565b82815260059290921b8401810191818101908684111561074a57600080fd5b8286015b848110156106ff578035835291830191830161074e565b803560ff8116811461056657600080fd5b803567ffffffffffffffff8116811461056657600080fd5b600082601f83011261079f57600080fd5b813560206107af6106b983610674565b82815260069290921b840181019181810190868411156107ce57600080fd5b8286015b848110156106ff57604081890312156107eb5760008081fd5b6107f361043c565b6107fc82610542565b8152610809858301610776565b818601528352918301916040016107d2565b60008060008060008060008060008060006101608c8e03121561083d57600080fd5b8b359a5060208c0135995061085460408d01610542565b985061086260608d01610660565b975067ffffffffffffffff8060808e0135111561087e57600080fd5b61088e8e60808f01358f01610698565b97508060a08e013511156108a157600080fd5b6108b18e60a08f01358f0161070a565b96506108bf60c08e01610765565b95508060e08e013511156108d257600080fd5b6108e28e60e08f01358f016104b4565b94506108f16101008e01610776565b9350806101208e0135111561090557600080fd5b6109168e6101208f01358f016104b4565b9250806101408e0135111561092a57600080fd5b5061093c8d6101408e01358e0161078e565b90509295989b509295989b9093969950565b600080600080600080600080610100898b03121561096b57600080fd5b88359750602089013567ffffffffffffffff8082111561098a57600080fd5b6109968c838d01610698565b985060408b01359150808211156109ac57600080fd5b6109b88c838d0161070a565b97506109c660608c01610765565b965060808b01359150808211156109dc57600080fd5b6109e88c838d016104b4565b95506109f660a08c01610776565b945060c08b0135915080821115610a0c57600080fd5b610a188c838d016104b4565b935060e08b0135915080821115610a2e57600080fd5b50610a3b8b828c0161078e565b915050929598509295989093965056fea164736f6c6343000813000a", } var ErroredVerifierABI = ErroredVerifierMetaData.ABI diff --git a/core/gethwrappers/llo-feeds/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/llo-feeds/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 729d3a295c..ef04a38f87 100644 --- a/core/gethwrappers/llo-feeds/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/llo-feeds/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,8 +1,12 @@ GETH_VERSION: 1.13.8 -channel_config_store: ../../../contracts/solc/v0.8.19/ChannelConfigStore/ChannelConfigStore.abi ../../../contracts/solc/v0.8.19/ChannelConfigStore/ChannelConfigStore.bin c90e29d9f1a885098982b6175e0447416431b28c605273c807694ac7141e9167 +channel_config_store: ../../../contracts/solc/v0.8.19/ChannelConfigStore/ChannelConfigStore.abi ../../../contracts/solc/v0.8.19/ChannelConfigStore/ChannelConfigStore.bin 3fafe83ea21d50488f5533962f62683988ffa6fd1476dccbbb9040be2369cb37 channel_config_verifier_proxy: ../../../contracts/solc/v0.8.19/ChannelVerifierProxy/ChannelVerifierProxy.abi ../../../contracts/solc/v0.8.19/ChannelVerifierProxy/ChannelVerifierProxy.bin 655658e5f61dfadfe3268de04f948b7e690ad03ca45676e645d6cd6018154661 channel_verifier: ../../../contracts/solc/v0.8.19/ChannelVerifier/ChannelVerifier.abi ../../../contracts/solc/v0.8.19/ChannelVerifier/ChannelVerifier.bin e6020553bd8e3e6b250fcaffe7efd22aea955c8c1a0eb05d282fdeb0ab6550b7 -errored_verifier: ../../../contracts/solc/v0.8.19/ErroredVerifier/ErroredVerifier.abi ../../../contracts/solc/v0.8.19/ErroredVerifier/ErroredVerifier.bin a3e5a77262e13ee30fe8d35551b32a3452d71929e43fd780bbfefeaf4aa62e43 +destination_fee_manager: ../../../contracts/solc/v0.8.19/DestinationFeeManager/DestinationFeeManager.abi ../../../contracts/solc/v0.8.19/DestinationFeeManager/DestinationFeeManager.bin c581af84832b8fd886685f59518bcdb11bd1c9b508d88b07c04d6226e6a2789e +destination_reward_manager: ../../../contracts/solc/v0.8.19/DestinationRewardManager/DestinationRewardManager.abi ../../../contracts/solc/v0.8.19/DestinationRewardManager/DestinationRewardManager.bin 6aed4313578f74ede71bcb60674391103d265d96d56d4736a79ef4128f0590f4 +destination_verifier: ../../../contracts/solc/v0.8.19/DestinationVerifier/DestinationVerifier.abi ../../../contracts/solc/v0.8.19/DestinationVerifier/DestinationVerifier.bin 2dc118aecd5c30d34a69354a9fb603beb98d46215a18d31c59f0f7902fd8f4c2 +destination_verifier_proxy: ../../../contracts/solc/v0.8.19/DestinationVerifierProxy/DestinationVerifierProxy.abi ../../../contracts/solc/v0.8.19/DestinationVerifierProxy/DestinationVerifierProxy.bin a4bf230bbba8a7b8e32a85a6161ca1343f7472b257c358a73ac37996809ce1c0 +errored_verifier: ../../../contracts/solc/v0.8.19/ErroredVerifier/ErroredVerifier.abi ../../../contracts/solc/v0.8.19/ErroredVerifier/ErroredVerifier.bin ad8ac8d6b99890081725e2304d79d1ba7dd5212b89d130aa9689f4269eed4691 exposed_channel_verifier: ../../../contracts/solc/v0.8.19/ExposedChannelVerifier/ExposedChannelVerifier.abi ../../../contracts/solc/v0.8.19/ExposedChannelVerifier/ExposedChannelVerifier.bin c21cde078900241c06de69e2bc5d906c5ef558b52db66caa68bed065940a2253 exposed_verifier: ../../../contracts/solc/v0.8.19/ExposedVerifier/ExposedVerifier.abi ../../../contracts/solc/v0.8.19/ExposedVerifier/ExposedVerifier.bin 00816ab345f768e522c79abadeadf9155c2c688067e18f8f73e5d6ab71037663 fee_manager: ../../../contracts/solc/v0.8.19/FeeManager/FeeManager.abi ../../../contracts/solc/v0.8.19/FeeManager/FeeManager.bin edc85f34294ae7c90d45c4c71eb5c105c60a4842dfbbf700c692870ffcc403a1 diff --git a/core/gethwrappers/llo-feeds/go_generate.go b/core/gethwrappers/llo-feeds/go_generate.go index 5e5b841b72..688b503cc1 100644 --- a/core/gethwrappers/llo-feeds/go_generate.go +++ b/core/gethwrappers/llo-feeds/go_generate.go @@ -12,3 +12,8 @@ package gethwrappers //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.19/ChannelConfigStore/ChannelConfigStore.abi ../../../contracts/solc/v0.8.19/ChannelConfigStore/ChannelConfigStore.bin ChannelConfigStore channel_config_store //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.19/ChannelVerifier/ChannelVerifier.abi ../../../contracts/solc/v0.8.19/ChannelVerifier/ChannelVerifier.bin ChannelVerifier channel_verifier //go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.19/ExposedChannelVerifier/ExposedChannelVerifier.abi ../../../contracts/solc/v0.8.19/ExposedChannelVerifier/ExposedChannelVerifier.bin ExposedChannelVerifier exposed_channel_verifier + +//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.19/DestinationVerifier/DestinationVerifier.abi ../../../contracts/solc/v0.8.19/DestinationVerifier/DestinationVerifier.bin DestinationVerifier destination_verifier +//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.19/DestinationVerifierProxy/DestinationVerifierProxy.abi ../../../contracts/solc/v0.8.19/DestinationVerifierProxy/DestinationVerifierProxy.bin DestinationVerifierProxy destination_verifier_proxy +//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.19/DestinationFeeManager/DestinationFeeManager.abi ../../../contracts/solc/v0.8.19/DestinationFeeManager/DestinationFeeManager.bin DestinationFeeManager destination_fee_manager +//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.19/DestinationRewardManager/DestinationRewardManager.abi ../../../contracts/solc/v0.8.19/DestinationRewardManager/DestinationRewardManager.bin DestinationRewardManager destination_reward_manager diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index 12491300bf..47a54b4fd8 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -212,7 +212,12 @@ type TestApplication struct { func NewApplicationEVMDisabled(t *testing.T) *TestApplication { t.Helper() - c := configtest.NewGeneralConfig(t, nil) + c := configtest.NewGeneralConfig(t, func(config *chainlink.Config, secrets *chainlink.Secrets) { + f := false + for _, c := range config.EVM { + c.Enabled = &f + } + }) return NewApplicationWithConfig(t, c) } @@ -439,6 +444,13 @@ func NewApplicationWithConfig(t testing.TB, cfg chainlink.GeneralConfig, flagsAn } initOps = append(initOps, chainlink.InitStarknet(testCtx, relayerFactory, starkCfg)) } + if cfg.AptosEnabled() { + aptosCfg := chainlink.AptosFactoryConfig{ + Keystore: keyStore.Aptos(), + TOMLConfigs: cfg.AptosConfigs(), + } + initOps = append(initOps, chainlink.InitAptos(testCtx, relayerFactory, aptosCfg)) + } relayChainInterops, err := chainlink.NewCoreRelayerChainInteroperators(initOps...) if err != nil { diff --git a/core/internal/cltest/mocks.go b/core/internal/cltest/mocks.go index 9e0ee2f3f2..fd01f72c13 100644 --- a/core/internal/cltest/mocks.go +++ b/core/internal/cltest/mocks.go @@ -392,6 +392,7 @@ func NewLegacyChainsWithMockChain(t testing.TB, ethClient evmclient.Client, cfg scopedCfg := evmtest.NewChainScopedConfig(t, cfg) ch.On("ID").Return(scopedCfg.EVM().ChainID()) ch.On("Config").Return(scopedCfg) + ch.On("HeadTracker").Return(nil) return NewLegacyChainsWithChain(ch, cfg) } diff --git a/core/internal/features/features_test.go b/core/internal/features/features_test.go index 7be0307798..a2086d1cb8 100644 --- a/core/internal/features/features_test.go +++ b/core/internal/features/features_test.go @@ -41,6 +41,7 @@ import ( commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" + "github.com/smartcontractkit/chainlink/v2/core/auth" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" diff --git a/core/internal/mocks/prometheus_backend.go b/core/internal/mocks/prometheus_backend.go deleted file mode 100644 index d02f7062cb..0000000000 --- a/core/internal/mocks/prometheus_backend.go +++ /dev/null @@ -1,204 +0,0 @@ -// Code generated by mockery v2.43.2. DO NOT EDIT. - -package mocks - -import ( - big "math/big" - - mock "github.com/stretchr/testify/mock" -) - -// PrometheusBackend is an autogenerated mock type for the PrometheusBackend type -type PrometheusBackend struct { - mock.Mock -} - -type PrometheusBackend_Expecter struct { - mock *mock.Mock -} - -func (_m *PrometheusBackend) EXPECT() *PrometheusBackend_Expecter { - return &PrometheusBackend_Expecter{mock: &_m.Mock} -} - -// SetMaxUnconfirmedAge provides a mock function with given fields: _a0, _a1 -func (_m *PrometheusBackend) SetMaxUnconfirmedAge(_a0 *big.Int, _a1 float64) { - _m.Called(_a0, _a1) -} - -// PrometheusBackend_SetMaxUnconfirmedAge_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetMaxUnconfirmedAge' -type PrometheusBackend_SetMaxUnconfirmedAge_Call struct { - *mock.Call -} - -// SetMaxUnconfirmedAge is a helper method to define mock.On call -// - _a0 *big.Int -// - _a1 float64 -func (_e *PrometheusBackend_Expecter) SetMaxUnconfirmedAge(_a0 interface{}, _a1 interface{}) *PrometheusBackend_SetMaxUnconfirmedAge_Call { - return &PrometheusBackend_SetMaxUnconfirmedAge_Call{Call: _e.mock.On("SetMaxUnconfirmedAge", _a0, _a1)} -} - -func (_c *PrometheusBackend_SetMaxUnconfirmedAge_Call) Run(run func(_a0 *big.Int, _a1 float64)) *PrometheusBackend_SetMaxUnconfirmedAge_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(*big.Int), args[1].(float64)) - }) - return _c -} - -func (_c *PrometheusBackend_SetMaxUnconfirmedAge_Call) Return() *PrometheusBackend_SetMaxUnconfirmedAge_Call { - _c.Call.Return() - return _c -} - -func (_c *PrometheusBackend_SetMaxUnconfirmedAge_Call) RunAndReturn(run func(*big.Int, float64)) *PrometheusBackend_SetMaxUnconfirmedAge_Call { - _c.Call.Return(run) - return _c -} - -// SetMaxUnconfirmedBlocks provides a mock function with given fields: _a0, _a1 -func (_m *PrometheusBackend) SetMaxUnconfirmedBlocks(_a0 *big.Int, _a1 int64) { - _m.Called(_a0, _a1) -} - -// PrometheusBackend_SetMaxUnconfirmedBlocks_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetMaxUnconfirmedBlocks' -type PrometheusBackend_SetMaxUnconfirmedBlocks_Call struct { - *mock.Call -} - -// SetMaxUnconfirmedBlocks is a helper method to define mock.On call -// - _a0 *big.Int -// - _a1 int64 -func (_e *PrometheusBackend_Expecter) SetMaxUnconfirmedBlocks(_a0 interface{}, _a1 interface{}) *PrometheusBackend_SetMaxUnconfirmedBlocks_Call { - return &PrometheusBackend_SetMaxUnconfirmedBlocks_Call{Call: _e.mock.On("SetMaxUnconfirmedBlocks", _a0, _a1)} -} - -func (_c *PrometheusBackend_SetMaxUnconfirmedBlocks_Call) Run(run func(_a0 *big.Int, _a1 int64)) *PrometheusBackend_SetMaxUnconfirmedBlocks_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(*big.Int), args[1].(int64)) - }) - return _c -} - -func (_c *PrometheusBackend_SetMaxUnconfirmedBlocks_Call) Return() *PrometheusBackend_SetMaxUnconfirmedBlocks_Call { - _c.Call.Return() - return _c -} - -func (_c *PrometheusBackend_SetMaxUnconfirmedBlocks_Call) RunAndReturn(run func(*big.Int, int64)) *PrometheusBackend_SetMaxUnconfirmedBlocks_Call { - _c.Call.Return(run) - return _c -} - -// SetPipelineRunsQueued provides a mock function with given fields: n -func (_m *PrometheusBackend) SetPipelineRunsQueued(n int) { - _m.Called(n) -} - -// PrometheusBackend_SetPipelineRunsQueued_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPipelineRunsQueued' -type PrometheusBackend_SetPipelineRunsQueued_Call struct { - *mock.Call -} - -// SetPipelineRunsQueued is a helper method to define mock.On call -// - n int -func (_e *PrometheusBackend_Expecter) SetPipelineRunsQueued(n interface{}) *PrometheusBackend_SetPipelineRunsQueued_Call { - return &PrometheusBackend_SetPipelineRunsQueued_Call{Call: _e.mock.On("SetPipelineRunsQueued", n)} -} - -func (_c *PrometheusBackend_SetPipelineRunsQueued_Call) Run(run func(n int)) *PrometheusBackend_SetPipelineRunsQueued_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(int)) - }) - return _c -} - -func (_c *PrometheusBackend_SetPipelineRunsQueued_Call) Return() *PrometheusBackend_SetPipelineRunsQueued_Call { - _c.Call.Return() - return _c -} - -func (_c *PrometheusBackend_SetPipelineRunsQueued_Call) RunAndReturn(run func(int)) *PrometheusBackend_SetPipelineRunsQueued_Call { - _c.Call.Return(run) - return _c -} - -// SetPipelineTaskRunsQueued provides a mock function with given fields: n -func (_m *PrometheusBackend) SetPipelineTaskRunsQueued(n int) { - _m.Called(n) -} - -// PrometheusBackend_SetPipelineTaskRunsQueued_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPipelineTaskRunsQueued' -type PrometheusBackend_SetPipelineTaskRunsQueued_Call struct { - *mock.Call -} - -// SetPipelineTaskRunsQueued is a helper method to define mock.On call -// - n int -func (_e *PrometheusBackend_Expecter) SetPipelineTaskRunsQueued(n interface{}) *PrometheusBackend_SetPipelineTaskRunsQueued_Call { - return &PrometheusBackend_SetPipelineTaskRunsQueued_Call{Call: _e.mock.On("SetPipelineTaskRunsQueued", n)} -} - -func (_c *PrometheusBackend_SetPipelineTaskRunsQueued_Call) Run(run func(n int)) *PrometheusBackend_SetPipelineTaskRunsQueued_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(int)) - }) - return _c -} - -func (_c *PrometheusBackend_SetPipelineTaskRunsQueued_Call) Return() *PrometheusBackend_SetPipelineTaskRunsQueued_Call { - _c.Call.Return() - return _c -} - -func (_c *PrometheusBackend_SetPipelineTaskRunsQueued_Call) RunAndReturn(run func(int)) *PrometheusBackend_SetPipelineTaskRunsQueued_Call { - _c.Call.Return(run) - return _c -} - -// SetUnconfirmedTransactions provides a mock function with given fields: _a0, _a1 -func (_m *PrometheusBackend) SetUnconfirmedTransactions(_a0 *big.Int, _a1 int64) { - _m.Called(_a0, _a1) -} - -// PrometheusBackend_SetUnconfirmedTransactions_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetUnconfirmedTransactions' -type PrometheusBackend_SetUnconfirmedTransactions_Call struct { - *mock.Call -} - -// SetUnconfirmedTransactions is a helper method to define mock.On call -// - _a0 *big.Int -// - _a1 int64 -func (_e *PrometheusBackend_Expecter) SetUnconfirmedTransactions(_a0 interface{}, _a1 interface{}) *PrometheusBackend_SetUnconfirmedTransactions_Call { - return &PrometheusBackend_SetUnconfirmedTransactions_Call{Call: _e.mock.On("SetUnconfirmedTransactions", _a0, _a1)} -} - -func (_c *PrometheusBackend_SetUnconfirmedTransactions_Call) Run(run func(_a0 *big.Int, _a1 int64)) *PrometheusBackend_SetUnconfirmedTransactions_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(*big.Int), args[1].(int64)) - }) - return _c -} - -func (_c *PrometheusBackend_SetUnconfirmedTransactions_Call) Return() *PrometheusBackend_SetUnconfirmedTransactions_Call { - _c.Call.Return() - return _c -} - -func (_c *PrometheusBackend_SetUnconfirmedTransactions_Call) RunAndReturn(run func(*big.Int, int64)) *PrometheusBackend_SetUnconfirmedTransactions_Call { - _c.Call.Return(run) - return _c -} - -// NewPrometheusBackend creates a new instance of PrometheusBackend. 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 NewPrometheusBackend(t interface { - mock.TestingT - Cleanup(func()) -}) *PrometheusBackend { - mock := &PrometheusBackend{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/core/internal/testutils/testutils.go b/core/internal/testutils/testutils.go index 45609488b4..4e7f9b05c8 100644 --- a/core/internal/testutils/testutils.go +++ b/core/internal/testutils/testutils.go @@ -380,6 +380,19 @@ func WaitForLogMessage(t *testing.T, observedLogs *observer.ObservedLogs, msg st return } +func WaitForLogMessageWithField(t *testing.T, observedLogs *observer.ObservedLogs, msg, field, value string) (le observer.LoggedEntry) { + AssertEventually(t, func() bool { + for _, l := range observedLogs.All() { + if strings.Contains(l.Message, msg) && strings.Contains(l.ContextMap()[field].(string), value) { + le = l + return true + } + } + return false + }) + return +} + // WaitForLogMessageCount waits until at least count log message containing the // specified msg is emitted func WaitForLogMessageCount(t *testing.T, observedLogs *observer.ObservedLogs, msg string, count int) { diff --git a/core/recovery/recover.go b/core/recovery/recover.go index 8e485abc55..61315defa9 100644 --- a/core/recovery/recover.go +++ b/core/recovery/recover.go @@ -3,38 +3,38 @@ package recovery import ( "github.com/getsentry/sentry-go" - "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + + corelogger "github.com/smartcontractkit/chainlink/v2/core/logger" ) func ReportPanics(fn func()) { - defer func() { - if err := recover(); err != nil { - sentry.CurrentHub().Recover(err) - sentry.Flush(logger.SentryFlushDeadline) + HandleFn(fn, func(err any) { + sentry.CurrentHub().Recover(err) + sentry.Flush(corelogger.SentryFlushDeadline) - panic(err) - } - }() - fn() + panic(err) + }) } func WrapRecover(lggr logger.Logger, fn func()) { - defer func() { - if err := recover(); err != nil { - lggr.Recover(err) + WrapRecoverHandle(lggr, fn, nil) +} + +func WrapRecoverHandle(lggr logger.Logger, fn func(), onPanic func(recovered any)) { + HandleFn(fn, func(recovered any) { + logger.Sugared(lggr).Criticalw("Recovered goroutine panic", "panic", recovered) + + if onPanic != nil { + onPanic(recovered) } - }() - fn() + }) } -func WrapRecoverHandle(lggr logger.Logger, fn func(), onPanic func(interface{})) { +func HandleFn(fn func(), onPanic func(recovered any)) { defer func() { - if err := recover(); err != nil { - lggr.Recover(err) - - if onPanic != nil { - onPanic(err) - } + if recovered := recover(); recovered != nil { + onPanic(recovered) } }() fn() diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 3d263a92be..468484f00c 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -24,7 +24,7 @@ require ( github.com/shopspring/decimal v1.4.0 github.com/smartcontractkit/chain-selectors v1.0.24 github.com/smartcontractkit/chainlink-automation v1.0.4 - github.com/smartcontractkit/chainlink-common v0.2.2-0.20240723123524-e407ecd120b1 + github.com/smartcontractkit/chainlink-common v0.2.2-0.20240828121637-da5837469949 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20240717100443-f6226e09bee7 github.com/spf13/cobra v1.8.0 @@ -174,14 +174,14 @@ require ( github.com/gorilla/sessions v1.2.2 // indirect github.com/gorilla/websocket v1.5.1 // indirect github.com/grafana/pyroscope-go v1.1.1 // indirect - github.com/grafana/pyroscope-go/godeltaprof v0.1.6 // indirect + github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect github.com/graph-gophers/dataloader v5.0.0+incompatible // indirect github.com/graph-gophers/graphql-go v1.3.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/gtank/merlin v0.1.1 // indirect github.com/gtank/ristretto255 v0.1.2 // indirect @@ -216,7 +216,7 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.3 // indirect + github.com/klauspost/compress v1.17.8 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect @@ -274,10 +274,11 @@ require ( github.com/sethvargo/go-retry v0.2.4 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/shirou/gopsutil/v3 v3.24.3 // indirect + github.com/smartcontractkit/chainlink-ccip v0.0.0-20240806144315-04ac101e9c95 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240710121324-3ed288aa9b45 // indirect - github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240718160222-2dc0c8136bfa // indirect + github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240820130645-cf4b159fbba2 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20240710170203-5b41615da827 // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240712132946-267a37c5ac6e // indirect + github.com/smartcontractkit/chainlink-solana v1.1.1-0.20240821170223-a2f5c39f457f // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240709043547-03612098f799 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 // indirect @@ -331,21 +332,21 @@ require ( go.uber.org/ratelimit v0.3.0 // indirect go.uber.org/zap v1.27.0 // indirect golang.org/x/arch v0.8.0 // indirect - golang.org/x/crypto v0.25.0 // indirect - golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect - golang.org/x/mod v0.19.0 // indirect - golang.org/x/net v0.27.0 // indirect - golang.org/x/oauth2 v0.21.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.22.0 // indirect - golang.org/x/term v0.22.0 // indirect - golang.org/x/text v0.16.0 // indirect + golang.org/x/crypto v0.26.0 // indirect + golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa // indirect + golang.org/x/mod v0.20.0 // indirect + golang.org/x/net v0.28.0 // indirect + golang.org/x/oauth2 v0.22.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.24.0 // indirect + golang.org/x/term v0.23.0 // indirect + golang.org/x/text v0.17.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.23.0 // indirect + golang.org/x/tools v0.24.0 // indirect gonum.org/v1/gonum v0.15.0 // indirect google.golang.org/genproto v0.0.0-20240711142825-46eb208f015d // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd // indirect google.golang.org/grpc v1.65.0 // indirect gopkg.in/guregu/null.v4 v4.0.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index f180e2a21d..c0d6389591 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -591,8 +591,8 @@ github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/ github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/grafana/pyroscope-go v1.1.1 h1:PQoUU9oWtO3ve/fgIiklYuGilvsm8qaGhlY4Vw6MAcQ= github.com/grafana/pyroscope-go v1.1.1/go.mod h1:Mw26jU7jsL/KStNSGGuuVYdUq7Qghem5P8aXYXSXG88= -github.com/grafana/pyroscope-go/godeltaprof v0.1.6 h1:nEdZ8louGAplSvIJi1HVp7kWvFvdiiYg3COLlTwJiFo= -github.com/grafana/pyroscope-go/godeltaprof v0.1.6/go.mod h1:Tk376Nbldo4Cha9RgiU7ik8WKFkNpfds98aUzS8omLE= +github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg= +github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= github.com/graph-gophers/dataloader v5.0.0+incompatible h1:R+yjsbrNq1Mo3aPG+Z/EKYrXrXXUNJHOgbRt+U6jOug= github.com/graph-gophers/dataloader v5.0.0+incompatible/go.mod h1:jk4jk0c5ZISbKaMe8WsVopGB5/15GvGHMdMdPtwlRp4= github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0= @@ -608,8 +608,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= @@ -780,8 +780,8 @@ github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6 github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.17.3 h1:qkRjuerhUU1EmXLYGkSH6EZL+vPSxIrYjLNAK4slzwA= -github.com/klauspost/compress v1.17.3/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= +github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= @@ -1074,16 +1074,18 @@ github.com/smartcontractkit/chain-selectors v1.0.24 h1:day+2l0vBErgVIs2Y9ifSz+T7 github.com/smartcontractkit/chain-selectors v1.0.24/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.4 h1:iyW181JjKHLNMnDleI8umfIfVVlwC7+n5izbLSFgjw8= github.com/smartcontractkit/chainlink-automation v1.0.4/go.mod h1:u4NbPZKJ5XiayfKHD/v3z3iflQWqvtdhj13jVZXj/cM= -github.com/smartcontractkit/chainlink-common v0.2.2-0.20240723123524-e407ecd120b1 h1:pdEpjgbZ5w/Sd5lzg/XiuC5gVyrmSovOo+3nUD46SP8= -github.com/smartcontractkit/chainlink-common v0.2.2-0.20240723123524-e407ecd120b1/go.mod h1:Jg1sCTsbxg76YByI8ifpFby3FvVqISStHT8ypy9ocmY= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20240806144315-04ac101e9c95 h1:LAgJTg9Yr/uCo2g7Krp88Dco2U45Y6sbJVl8uKoLkys= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20240806144315-04ac101e9c95/go.mod h1:/ZWraCBaDDgaIN1prixYcbVvIk/6HeED9+8zbWQ+TMo= +github.com/smartcontractkit/chainlink-common v0.2.2-0.20240828121637-da5837469949 h1:9YHYswxhxMAgdJhb+APrf57ZEPsxML8H71oxxvU36eU= +github.com/smartcontractkit/chainlink-common v0.2.2-0.20240828121637-da5837469949/go.mod h1:bE6E7KwB8dkFUWKxJTTTtrNAl9xFPGlurKpDVhRz1tk= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240710121324-3ed288aa9b45 h1:NBQLtqk8zsyY4qTJs+NElI3aDFTcAo83JHvqD04EvB0= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240710121324-3ed288aa9b45/go.mod h1:LV0h7QBQUpoC2UUi6TcUvcIFm1xjP/DtEcqV8+qeLUs= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240718160222-2dc0c8136bfa h1:g75H8oh2ws52s8BekwvGQ9XvBVu3E7WM1rfiA0PN0zk= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240718160222-2dc0c8136bfa/go.mod h1:wZvLHX/Sd9hskN51016cTFcT3G62KXVa6xbVDS7tRjc= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240820130645-cf4b159fbba2 h1:KH6tpCw5hu8u6UTtgll7a8mE4sIbHCbmtzHJdKuRwBw= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240820130645-cf4b159fbba2/go.mod h1:V/86loaFSH0dqqUEHqyXVbyNqDRSjvcf9BRomWFTljU= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240710170203-5b41615da827 h1:BCHu4pNP6arrcHLEWx61XjLaonOd2coQNyL0NTUcaMc= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240710170203-5b41615da827/go.mod h1:OPX+wC2TWQsyLNpR7daMt2vMpmsNcoBxbZyGTHr6tiA= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240712132946-267a37c5ac6e h1:PzwzlHNv1YbJ6ZIdl/pIFRoOuOS4V4WLvjZvFUnZFL4= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240712132946-267a37c5ac6e/go.mod h1:hsFhop+SlQHKD+DEFjZrMJmbauT1A/wvtZIeeo4PxFU= +github.com/smartcontractkit/chainlink-solana v1.1.1-0.20240821170223-a2f5c39f457f h1:b0Ifwk7eK3fwJ0R69Ovhv5XvZ1/TUAfHkU5Jp7wbNZ0= +github.com/smartcontractkit/chainlink-solana v1.1.1-0.20240821170223-a2f5c39f457f/go.mod h1:Ml88TJTwZCj6yHDkAEN/EhxVutzSlk+kDZgfibRIqF0= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240709043547-03612098f799 h1:HyLTySm7BR+oNfZqDTkVJ25wnmcTtxBBD31UkFL+kEM= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240709043547-03612098f799/go.mod h1:UVFRacRkP7O7TQAzFmR52v5mUlxf+G1ovMlCQAB/cHU= github.com/smartcontractkit/go-plugin v0.0.0-20240208201424-b3b91517de16 h1:TFe+FvzxClblt6qRfqEhUfa4kFQx5UobuoFGO2W4mMo= @@ -1341,8 +1343,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= -golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1353,8 +1355,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI= +golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1379,8 +1381,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= -golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= +golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1423,15 +1425,15 @@ golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= -golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= -golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= +golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1443,8 +1445,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1515,8 +1517,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1524,8 +1526,8 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= -golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1538,8 +1540,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1591,8 +1593,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= -golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= +golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1649,10 +1651,10 @@ google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEY google.golang.org/genproto v0.0.0-20210401141331-865547bb08e2/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20240711142825-46eb208f015d h1:/hmn0Ku5kWij/kjGsrcJeC1T/MrJi2iNWwgAqrihFwc= google.golang.org/genproto v0.0.0-20240711142825-46eb208f015d/go.mod h1:FfBgJBJg9GcpPvKIuHSZ/aE1g2ecGL74upMzGZjiGEY= -google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d h1:kHjw/5UfflP/L5EbledDrcG4C2597RtymmGRZvHiCuY= -google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d/go.mod h1:mw8MG/Qz5wfgYr6VqVCiZcHe/GJEfI+oGGDCohaVgB0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d h1:JU0iKnSg02Gmb5ZdV8nYsKEKsP6o/FGVWTrw4i1DA9A= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd h1:BBOTEWLuuEGQy9n1y9MhVJ9Qt0BDu21X8qZs71/uPZo= +google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:fO8wJzT2zbQbAjbIoos1285VfEIYKDDY+Dt+WpTkh6g= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd h1:6TEm2ZxXoQmFWFlt1vNxvVOa1Q0dXFQD1m/rYjXmS0E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= diff --git a/core/scripts/keystone/.gitignore b/core/scripts/keystone/.gitignore index 4af4a42a01..92bf9aabc5 100644 --- a/core/scripts/keystone/.gitignore +++ b/core/scripts/keystone/.gitignore @@ -3,3 +3,4 @@ !*-sample.sh keystone .cache/ +artefacts/ diff --git a/core/scripts/keystone/main.go b/core/scripts/keystone/main.go index 571623578a..3486830ca3 100644 --- a/core/scripts/keystone/main.go +++ b/core/scripts/keystone/main.go @@ -20,6 +20,8 @@ func main() { src.NewGenerateCribClusterOverridesCommand(), src.NewDeleteJobsCommand(), src.NewDeployAndInitializeCapabilitiesRegistryCommand(), + src.NewDeployWorkflowsCommand(), + src.NewDeleteWorkflowsCommand(), } commandsList := func(commands []command) string { diff --git a/core/scripts/keystone/src/01_deploy_contracts_cmd.go b/core/scripts/keystone/src/01_deploy_contracts_cmd.go index 2ca60bdfaf..b304973795 100644 --- a/core/scripts/keystone/src/01_deploy_contracts_cmd.go +++ b/core/scripts/keystone/src/01_deploy_contracts_cmd.go @@ -52,8 +52,12 @@ func (g *deployContracts) Run(args []string) { skipFunding := fs.Bool("skipfunding", false, "skip funding the transmitters") onlySetConfig := fs.Bool("onlysetconfig", false, "set the config on the OCR3 contract without deploying the contracts or funding transmitters") dryRun := fs.Bool("dryrun", false, "dry run, don't actually deploy the contracts and do not fund transmitters") + publicKeys := fs.String("publickeys", "", "Custom public keys json location") + nodeList := fs.String("nodes", "", "Custom node list location") + artefactsDir := fs.String("artefacts", "", "Custom artefacts directory location") err := fs.Parse(args) + if err != nil || *ocrConfigFile == "" || ocrConfigFile == nil || *ethUrl == "" || ethUrl == nil || @@ -63,11 +67,21 @@ func (g *deployContracts) Run(args []string) { os.Exit(1) } + if *artefactsDir == "" { + *artefactsDir = defaultArtefactsDir + } + if *publicKeys == "" { + *publicKeys = defaultPublicKeys + } + if *nodeList == "" { + *nodeList = defaultNodeList + } + os.Setenv("ETH_URL", *ethUrl) os.Setenv("ETH_CHAIN_ID", fmt.Sprintf("%d", *chainID)) os.Setenv("ACCOUNT_KEY", *accountKey) - deploy(*ocrConfigFile, *skipFunding, *dryRun, *onlySetConfig) + deploy(*nodeList, *publicKeys, *ocrConfigFile, *skipFunding, *dryRun, *onlySetConfig, *artefactsDir) } // deploy does the following: @@ -77,16 +91,20 @@ func (g *deployContracts) Run(args []string) { // 4. Writes the deployed contract addresses to a file // 5. Funds the transmitters func deploy( + nodeList string, + publicKeys string, configFile string, skipFunding bool, dryRun bool, onlySetConfig bool, + artefacts string, ) { env := helpers.SetupEnv(false) ocrConfig := generateOCR3Config( + nodeList, configFile, env.ChainID, - ".cache/PublicKeys.json", + publicKeys, ) if dryRun { @@ -96,11 +114,11 @@ func deploy( if onlySetConfig { fmt.Println("Skipping deployment of contracts and skipping funding transmitters, only setting config") - setOCR3Config(env, ocrConfig) + setOCR3Config(env, ocrConfig, artefacts) return } - if ContractsAlreadyDeployed() { + if ContractsAlreadyDeployed(artefacts) { fmt.Println("Contracts already deployed") return } @@ -118,10 +136,10 @@ func deploy( jsonBytes, err := json.Marshal(contracts) PanicErr(err) - err = os.WriteFile(DeployedContractsFilePath(), jsonBytes, 0600) + err = os.WriteFile(DeployedContractsFilePath(artefacts), jsonBytes, 0600) PanicErr(err) - setOCR3Config(env, ocrConfig) + setOCR3Config(env, ocrConfig, artefacts) if skipFunding { fmt.Println("Skipping funding transmitters") @@ -139,8 +157,9 @@ func deploy( func setOCR3Config( env helpers.Environment, ocrConfig orc2drOracleConfig, + artefacts string, ) { - loadedContracts, err := LoadDeployedContracts() + loadedContracts, err := LoadDeployedContracts(artefacts) PanicErr(err) ocrContract, err := ocr3_capability.NewOCR3Capability(loadedContracts.OCRContract, env.Ec) @@ -161,16 +180,16 @@ func setOCR3Config( loadedContracts.SetConfigTxBlock = receipt.BlockNumber.Uint64() jsonBytes, err := json.Marshal(loadedContracts) PanicErr(err) - err = os.WriteFile(DeployedContractsFilePath(), jsonBytes, 0600) + err = os.WriteFile(DeployedContractsFilePath(artefacts), jsonBytes, 0600) PanicErr(err) } -func LoadDeployedContracts() (deployedContracts, error) { - if !ContractsAlreadyDeployed() { +func LoadDeployedContracts(artefacts string) (deployedContracts, error) { + if !ContractsAlreadyDeployed(artefacts) { return deployedContracts{}, fmt.Errorf("no deployed contracts found, run deploy first") } - jsonBytes, err := os.ReadFile(DeployedContractsFilePath()) + jsonBytes, err := os.ReadFile(DeployedContractsFilePath(artefacts)) if err != nil { return deployedContracts{}, err } @@ -180,13 +199,13 @@ func LoadDeployedContracts() (deployedContracts, error) { return contracts, err } -func ContractsAlreadyDeployed() bool { - _, err := os.Stat(DeployedContractsFilePath()) +func ContractsAlreadyDeployed(artefacts string) bool { + _, err := os.Stat(DeployedContractsFilePath(artefacts)) return err == nil } -func DeployedContractsFilePath() string { - return filepath.Join(artefactsDir, deployedContractsJSON) +func DeployedContractsFilePath(artefacts string) string { + return filepath.Join(artefacts, deployedContractsJSON) } func DeployForwarder(e helpers.Environment) *forwarder.KeystoneForwarder { diff --git a/core/scripts/keystone/src/02_deploy_jobspecs_cmd.go b/core/scripts/keystone/src/02_deploy_jobspecs_cmd.go index 5918650cf8..275943d638 100644 --- a/core/scripts/keystone/src/02_deploy_jobspecs_cmd.go +++ b/core/scripts/keystone/src/02_deploy_jobspecs_cmd.go @@ -16,8 +16,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/cmd" ) -type deployJobSpecs struct { -} +type deployJobSpecs struct{} func NewDeployJobSpecsCommand() *deployJobSpecs { return &deployJobSpecs{} @@ -32,6 +31,11 @@ func (g *deployJobSpecs) Run(args []string) { chainID := fs.Int64("chainid", 11155111, "chain id") p2pPort := fs.Int64("p2pport", 6690, "p2p port") onlyReplay := fs.Bool("onlyreplay", false, "only replay the block from the OCR3 contract setConfig transaction") + templatesLocation := fs.String("templates", "", "Custom templates location") + nodeList := fs.String("nodes", "", "Custom node list location") + publicKeys := fs.String("publickeys", "", "Custom public keys json location") + artefactsDir := fs.String("artefacts", "", "Custom artefacts directory location") + err := fs.Parse(args) if err != nil || chainID == nil || *chainID == 0 || p2pPort == nil || *p2pPort == 0 || onlyReplay == nil { fs.Usage() @@ -43,12 +47,27 @@ func (g *deployJobSpecs) Run(args []string) { fmt.Println("Deploying OCR3 job specs") } - nodes := downloadNodeAPICredentialsDefault() - deployedContracts, err := LoadDeployedContracts() + if *artefactsDir == "" { + *artefactsDir = defaultArtefactsDir + } + if *publicKeys == "" { + *publicKeys = defaultPublicKeys + } + if *nodeList == "" { + *nodeList = defaultNodeList + } + if *templatesLocation == "" { + *templatesLocation = "templates" + } + + nodes := downloadNodeAPICredentials(*nodeList) + deployedContracts, err := LoadDeployedContracts(*artefactsDir) PanicErr(err) jobspecs := genSpecs( - ".cache/PublicKeys.json", ".cache/NodeList.txt", "templates", + *publicKeys, + *nodeList, + *templatesLocation, *chainID, *p2pPort, deployedContracts.OCRContract.Hex(), ) flattenedSpecs := []hostSpec{jobspecs.bootstrap} diff --git a/core/scripts/keystone/src/03_gen_crib_cluster_overrides_cmd.go b/core/scripts/keystone/src/03_gen_crib_cluster_overrides_cmd.go index cb3acf903b..6b98951459 100644 --- a/core/scripts/keystone/src/03_gen_crib_cluster_overrides_cmd.go +++ b/core/scripts/keystone/src/03_gen_crib_cluster_overrides_cmd.go @@ -9,8 +9,7 @@ import ( helpers "github.com/smartcontractkit/chainlink/core/scripts/common" ) -type generateCribClusterOverrides struct { -} +type generateCribClusterOverrides struct{} func NewGenerateCribClusterOverridesCommand() *generateCribClusterOverrides { return &generateCribClusterOverrides{} @@ -24,25 +23,39 @@ func (g *generateCribClusterOverrides) Run(args []string) { fs := flag.NewFlagSet(g.Name(), flag.ContinueOnError) chainID := fs.Int64("chainid", 11155111, "chain id") outputPath := fs.String("outpath", "../crib", "the path to output the generated overrides") + publicKeys := fs.String("publickeys", "", "Custom public keys json location") + nodeList := fs.String("nodes", "", "Custom node list location") + artefactsDir := fs.String("artefacts", "", "Custom artefacts directory location") - deployedContracts, err := LoadDeployedContracts() - helpers.PanicErr(err) templatesDir := "templates" - err = fs.Parse(args) + err := fs.Parse(args) if err != nil || outputPath == nil || *outputPath == "" || chainID == nil || *chainID == 0 { fs.Usage() os.Exit(1) } - lines := generateCribConfig(".cache/PublicKeys.json", chainID, templatesDir, deployedContracts.ForwarderContract.Hex()) + if *artefactsDir == "" { + *artefactsDir = defaultArtefactsDir + } + if *publicKeys == "" { + *publicKeys = defaultPublicKeys + } + if *nodeList == "" { + *nodeList = defaultNodeList + } + + deployedContracts, err := LoadDeployedContracts(*artefactsDir) + helpers.PanicErr(err) + + lines := generateCribConfig(*nodeList, *publicKeys, chainID, templatesDir, deployedContracts.ForwarderContract.Hex()) cribOverridesStr := strings.Join(lines, "\n") err = os.WriteFile(filepath.Join(*outputPath, "crib-cluster-overrides.yaml"), []byte(cribOverridesStr), 0600) helpers.PanicErr(err) } -func generateCribConfig(pubKeysPath string, chainID *int64, templatesDir string, forwarderAddress string) []string { - nca := downloadNodePubKeys(*chainID, pubKeysPath) +func generateCribConfig(nodeList string, pubKeysPath string, chainID *int64, templatesDir string, forwarderAddress string) []string { + nca := downloadNodePubKeys(nodeList, *chainID, pubKeysPath) nodeAddresses := []string{} for _, node := range nca[1:] { diff --git a/core/scripts/keystone/src/03_gen_crib_cluster_overrides_cmd_test.go b/core/scripts/keystone/src/03_gen_crib_cluster_overrides_cmd_test.go index 722b01e91c..53d43c2342 100644 --- a/core/scripts/keystone/src/03_gen_crib_cluster_overrides_cmd_test.go +++ b/core/scripts/keystone/src/03_gen_crib_cluster_overrides_cmd_test.go @@ -13,7 +13,7 @@ func TestGenerateCribConfig(t *testing.T) { forwarderAddress := "0x1234567890abcdef" publicKeysPath := "./testdata/PublicKeys.json" - lines := generateCribConfig(publicKeysPath, &chainID, templatesDir, forwarderAddress) + lines := generateCribConfig(defaultNodeList, publicKeysPath, &chainID, templatesDir, forwarderAddress) snaps.MatchSnapshot(t, strings.Join(lines, "\n")) } diff --git a/core/scripts/keystone/src/04_delete_ocr3_jobs_cmd.go b/core/scripts/keystone/src/04_delete_ocr3_jobs_cmd.go index 2ebed000ed..136691962d 100644 --- a/core/scripts/keystone/src/04_delete_ocr3_jobs_cmd.go +++ b/core/scripts/keystone/src/04_delete_ocr3_jobs_cmd.go @@ -5,14 +5,14 @@ import ( "encoding/json" "flag" "fmt" + "os" "github.com/urfave/cli" helpers "github.com/smartcontractkit/chainlink/core/scripts/common" ) -type deleteJobs struct { -} +type deleteJobs struct{} type OCRSpec struct { ContractID string @@ -22,11 +22,16 @@ type BootSpec struct { ContractID string } +type WorkflowSpec struct { + WorkflowID string +} + type JobSpec struct { Id string Name string BootstrapSpec BootSpec OffChainReporting2OracleSpec OCRSpec + WorkflowSpec WorkflowSpec } func NewDeleteJobsCommand() *deleteJobs { @@ -38,9 +43,26 @@ func (g *deleteJobs) Name() string { } func (g *deleteJobs) Run(args []string) { - deployedContracts, err := LoadDeployedContracts() + fs := flag.NewFlagSet(g.Name(), flag.ContinueOnError) + nodeList := fs.String("nodes", "", "Custom node list location") + artefactsDir := fs.String("artefacts", "", "Custom artefacts directory location") + + err := fs.Parse(args) + if err != nil { + fs.Usage() + os.Exit(1) + } + + if *artefactsDir == "" { + *artefactsDir = defaultArtefactsDir + } + if *nodeList == "" { + *nodeList = defaultNodeList + } + + deployedContracts, err := LoadDeployedContracts(*artefactsDir) helpers.PanicErr(err) - nodes := downloadNodeAPICredentialsDefault() + nodes := downloadNodeAPICredentials(*nodeList) for _, node := range nodes { output := &bytes.Buffer{} diff --git a/core/scripts/keystone/src/05_deploy_initialize_capabilities_registry.go b/core/scripts/keystone/src/05_deploy_initialize_capabilities_registry.go index 8762241543..3352267d14 100644 --- a/core/scripts/keystone/src/05_deploy_initialize_capabilities_registry.go +++ b/core/scripts/keystone/src/05_deploy_initialize_capabilities_registry.go @@ -11,10 +11,11 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - ragetypes "github.com/smartcontractkit/libocr/ragep2p/types" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/durationpb" + ragetypes "github.com/smartcontractkit/libocr/ragep2p/types" + capabilitiespb "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" "github.com/smartcontractkit/chainlink-common/pkg/values" @@ -373,8 +374,14 @@ func (c *deployAndInitializeCapabilitiesRegistryCommand) Run(args []string) { panic(err) } - cc = newCapabilityConfig() - ccb, err = proto.Marshal(cc) + targetCapabilityConfig := newCapabilityConfig() + targetCapabilityConfig.RemoteConfig = &capabilitiespb.CapabilityConfig_RemoteTargetConfig{ + RemoteTargetConfig: &capabilitiespb.RemoteTargetConfig{ + RequestHashExcludedAttributes: []string{"signed_report.Signatures"}, + }, + } + + remoteTargetConfigBytes, err := proto.Marshal(targetCapabilityConfig) if err != nil { panic(err) } @@ -382,7 +389,7 @@ func (c *deployAndInitializeCapabilitiesRegistryCommand) Run(args []string) { cfgs = []kcr.CapabilitiesRegistryCapabilityConfiguration{ { CapabilityId: wid, - Config: ccb, + Config: remoteTargetConfigBytes, }, } _, err = reg.AddDON(env.Owner, ps, cfgs, true, false, 1) diff --git a/core/scripts/keystone/src/06_deploy_workflows_cmd.go b/core/scripts/keystone/src/06_deploy_workflows_cmd.go new file mode 100644 index 0000000000..0ca8e5d4a7 --- /dev/null +++ b/core/scripts/keystone/src/06_deploy_workflows_cmd.go @@ -0,0 +1,71 @@ +package src + +import ( + "bytes" + "errors" + "flag" + "fmt" + "os" + + "github.com/urfave/cli" + + helpers "github.com/smartcontractkit/chainlink/core/scripts/common" +) + +type deployWorkflows struct{} + +func NewDeployWorkflowsCommand() *deployWorkflows { + return &deployWorkflows{} +} + +func (g *deployWorkflows) Name() string { + return "deploy-workflows" +} + +func (g *deployWorkflows) Run(args []string) { + fs := flag.NewFlagSet(g.Name(), flag.ContinueOnError) + workflowFile := fs.String("workflow", "workflow.yml", "path to workflow file") + nodeList := fs.String("nodes", "", "Custom node list location") + err := fs.Parse(args) + if err != nil || workflowFile == nil || *workflowFile == "" { + fs.Usage() + os.Exit(1) + } + if *nodeList == "" { + *nodeList = defaultNodeList + } + fmt.Println("Deploying workflows") + + // use a separate list + nodes := downloadNodeAPICredentials(*nodeList) + + if _, err = os.Stat(*workflowFile); err != nil { + PanicErr(errors.New("toml file does not exist")) + } + + for i, n := range nodes { + if i == 0 { + continue // skip bootstrap node + } + output := &bytes.Buffer{} + client, app := newApp(n, output) + fmt.Println("Logging in:", n.url) + loginFs := flag.NewFlagSet("test", flag.ContinueOnError) + loginFs.Bool("bypass-version-check", true, "") + loginCtx := cli.NewContext(app, loginFs, nil) + err := client.RemoteLogin(loginCtx) + helpers.PanicErr(err) + output.Reset() + + fmt.Printf("Deploying workflow\n... \n") + fs := flag.NewFlagSet("test", flag.ExitOnError) + err = fs.Parse([]string{*workflowFile}) + + helpers.PanicErr(err) + err = client.CreateJob(cli.NewContext(app, fs, nil)) + if err != nil { + fmt.Println("Failed to deploy workflow:", "Error:", err) + } + output.Reset() + } +} diff --git a/core/scripts/keystone/src/07_delete_workflows_cmd.go b/core/scripts/keystone/src/07_delete_workflows_cmd.go new file mode 100644 index 0000000000..cccedaf9e7 --- /dev/null +++ b/core/scripts/keystone/src/07_delete_workflows_cmd.go @@ -0,0 +1,74 @@ +package src + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "os" + + "github.com/urfave/cli" + + helpers "github.com/smartcontractkit/chainlink/core/scripts/common" +) + +type deleteWorkflows struct{} + +func NewDeleteWorkflowsCommand() *deleteWorkflows { + return &deleteWorkflows{} +} + +func (g *deleteWorkflows) Name() string { + return "delete-workflows" +} + +func (g *deleteWorkflows) Run(args []string) { + fs := flag.NewFlagSet(g.Name(), flag.ExitOnError) + nodeList := fs.String("nodes", "", "Custom node list location") + + err := fs.Parse(args) + if err != nil { + fs.Usage() + os.Exit(1) + } + + if *nodeList == "" { + *nodeList = defaultNodeList + } + + nodes := downloadNodeAPICredentials(*nodeList) + + for _, node := range nodes { + output := &bytes.Buffer{} + client, app := newApp(node, output) + + fmt.Println("Logging in:", node.url) + loginFs := flag.NewFlagSet("test", flag.ContinueOnError) + loginFs.Bool("bypass-version-check", true, "") + loginCtx := cli.NewContext(app, loginFs, nil) + err := client.RemoteLogin(loginCtx) + helpers.PanicErr(err) + output.Reset() + + fileFs := flag.NewFlagSet("test", flag.ExitOnError) + err = client.ListJobs(cli.NewContext(app, fileFs, nil)) + helpers.PanicErr(err) + + var parsed []JobSpec + err = json.Unmarshal(output.Bytes(), &parsed) + helpers.PanicErr(err) + + for _, jobSpec := range parsed { + if jobSpec.WorkflowSpec.WorkflowID != "" { + fmt.Println("Deleting workflow job ID:", jobSpec.Id, "name:", jobSpec.Name) + set := flag.NewFlagSet("test", flag.ExitOnError) + err = set.Parse([]string{jobSpec.Id}) + helpers.PanicErr(err) + err = client.DeleteJob(cli.NewContext(app, set, nil)) + helpers.PanicErr(err) + } + } + + output.Reset() + } +} diff --git a/core/scripts/keystone/src/88_gen_jobspecs.go b/core/scripts/keystone/src/88_gen_jobspecs.go index 6a9c911a5f..5f0b9097d2 100644 --- a/core/scripts/keystone/src/88_gen_jobspecs.go +++ b/core/scripts/keystone/src/88_gen_jobspecs.go @@ -34,12 +34,12 @@ func genSpecs( ocrConfigContractAddress string, ) donHostSpec { nodes := downloadNodeAPICredentials(nodeListPath) - nca := downloadNodePubKeys(chainID, pubkeysPath) + nca := downloadNodePubKeys(nodeListPath, chainID, pubkeysPath) bootstrapNode := nca[0] bootstrapSpecLines, err := readLines(filepath.Join(templatesDir, bootstrapSpecTemplate)) helpers.PanicErr(err) - bootHost := nodes[0].url.Host + bootHost := nodes[0].url.Hostname() bootstrapSpecLines = replacePlaceholders( bootstrapSpecLines, chainID, p2pPort, diff --git a/core/scripts/keystone/src/88_gen_ocr3_config.go b/core/scripts/keystone/src/88_gen_ocr3_config.go index fe9241a2bd..1107df57ca 100644 --- a/core/scripts/keystone/src/88_gen_ocr3_config.go +++ b/core/scripts/keystone/src/88_gen_ocr3_config.go @@ -96,10 +96,10 @@ func mustReadConfig(fileName string) (output TopLevelConfigSource) { return mustParseJSON[TopLevelConfigSource](fileName) } -func generateOCR3Config(configFile string, chainID int64, pubKeysPath string) orc2drOracleConfig { +func generateOCR3Config(nodeList string, configFile string, chainID int64, pubKeysPath string) orc2drOracleConfig { topLevelCfg := mustReadConfig(configFile) cfg := topLevelCfg.OracleConfig - nca := downloadNodePubKeys(chainID, pubKeysPath) + nca := downloadNodePubKeys(nodeList, chainID, pubKeysPath) onchainPubKeys := []common.Address{} for _, n := range nca { diff --git a/core/scripts/keystone/src/88_gen_ocr3_config_test.go b/core/scripts/keystone/src/88_gen_ocr3_config_test.go index 185354ec2f..10cdc07b20 100644 --- a/core/scripts/keystone/src/88_gen_ocr3_config_test.go +++ b/core/scripts/keystone/src/88_gen_ocr3_config_test.go @@ -10,7 +10,7 @@ import ( func TestGenerateOCR3Config(t *testing.T) { // Generate OCR3 config - config := generateOCR3Config("./testdata/SampleConfig.json", 11155111, "./testdata/PublicKeys.json") + config := generateOCR3Config(".cache/NodeList.txt", "./testdata/SampleConfig.json", 11155111, "./testdata/PublicKeys.json") matchOffchainConfig := match.Custom("OffchainConfig", func(s any) (any, error) { // coerce the value to a string diff --git a/core/scripts/keystone/src/99_fetch_keys.go b/core/scripts/keystone/src/99_fetch_keys.go index 4fcb6f138a..b115a7bb94 100644 --- a/core/scripts/keystone/src/99_fetch_keys.go +++ b/core/scripts/keystone/src/99_fetch_keys.go @@ -17,14 +17,14 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) -func downloadNodePubKeys(chainID int64, pubKeysPath string) []NodeKeys { +func downloadNodePubKeys(nodeList string, chainID int64, pubKeysPath string) []NodeKeys { // Check if file exists already, and if so, return the keys if _, err := os.Stat(pubKeysPath); err == nil { fmt.Println("Loading existing public keys at:", pubKeysPath) return mustParseJSON[[]NodeKeys](pubKeysPath) } - nodes := downloadNodeAPICredentialsDefault() + nodes := downloadNodeAPICredentials(nodeList) nodesKeys := mustFetchNodesKeys(chainID, nodes) marshalledNodeKeys, err := json.MarshalIndent(nodesKeys, "", " ") @@ -40,13 +40,6 @@ func downloadNodePubKeys(chainID int64, pubKeysPath string) []NodeKeys { return nodesKeys } -// downloadNodeAPICredentialsDefault downloads the node API credentials, or loads them from disk if they already exist -// -// The nodes are sorted by URL. In the case of crib, the bootstrap node is the first node in the list. -func downloadNodeAPICredentialsDefault() []*node { - return downloadNodeAPICredentials(".cache/NodeList.txt") -} - // downloadNodeAPICredentials downloads the node API credentials, or loads them from disk if they already exist // // The nodes are sorted by URL. In the case of crib, the bootstrap node is the first node in the list. diff --git a/core/scripts/keystone/src/99_files.go b/core/scripts/keystone/src/99_files.go index d334b0fd56..08ba12e419 100644 --- a/core/scripts/keystone/src/99_files.go +++ b/core/scripts/keystone/src/99_files.go @@ -11,7 +11,9 @@ import ( ) const ( - artefactsDir = "artefacts" + defaultArtefactsDir = "artefacts" + defaultPublicKeys = ".cache/PublicKeys.json" + defaultNodeList = ".cache/NodeList.txt" deployedContractsJSON = "deployed_contracts.json" bootstrapSpecTemplate = "bootstrap.toml" cribOverrideTemplate = "crib-overrides.yaml" diff --git a/core/scripts/keystone/src/__snapshots__/88_gen_jobspecs_test.snap b/core/scripts/keystone/src/__snapshots__/88_gen_jobspecs_test.snap index 1ee7f67894..a4b4e6e302 100755 --- a/core/scripts/keystone/src/__snapshots__/88_gen_jobspecs_test.snap +++ b/core/scripts/keystone/src/__snapshots__/88_gen_jobspecs_test.snap @@ -33,13 +33,12 @@ chainID = "11155111" command = "chainlink-ocr3-capability" ocrVersion = 3 pluginName = "ocr-capability" -providerType = "plugin" +providerType = "ocr3-capability" telemetryType = "plugin" [onchainSigningStrategy] strategyName = 'single-chain' [onchainSigningStrategy.config] -publicKey = '8fa807463ad73f9ee855cfd60ba406dcf98a2855b3dd8af613107b0f6890a707' -------------------------------- Oracle 1: @@ -63,13 +62,12 @@ chainID = "11155111" command = "chainlink-ocr3-capability" ocrVersion = 3 pluginName = "ocr-capability" -providerType = "plugin" +providerType = "ocr3-capability" telemetryType = "plugin" [onchainSigningStrategy] strategyName = 'single-chain' [onchainSigningStrategy.config] -publicKey = '8fa807463ad73f9ee855cfd60ba406dcf98a2855b3dd8af613107b0f6890a707' -------------------------------- Oracle 2: @@ -93,13 +91,12 @@ chainID = "11155111" command = "chainlink-ocr3-capability" ocrVersion = 3 pluginName = "ocr-capability" -providerType = "plugin" +providerType = "ocr3-capability" telemetryType = "plugin" [onchainSigningStrategy] strategyName = 'single-chain' [onchainSigningStrategy.config] -publicKey = '8fa807463ad73f9ee855cfd60ba406dcf98a2855b3dd8af613107b0f6890a707' -------------------------------- Oracle 3: @@ -123,13 +120,12 @@ chainID = "11155111" command = "chainlink-ocr3-capability" ocrVersion = 3 pluginName = "ocr-capability" -providerType = "plugin" +providerType = "ocr3-capability" telemetryType = "plugin" [onchainSigningStrategy] strategyName = 'single-chain' [onchainSigningStrategy.config] -publicKey = '8fa807463ad73f9ee855cfd60ba406dcf98a2855b3dd8af613107b0f6890a707' --- diff --git a/core/scripts/keystone/templates/oracle.toml b/core/scripts/keystone/templates/oracle.toml index f2ff87de92..6049ad925d 100644 --- a/core/scripts/keystone/templates/oracle.toml +++ b/core/scripts/keystone/templates/oracle.toml @@ -17,10 +17,9 @@ chainID = "{{ chain_id }}" command = "chainlink-ocr3-capability" ocrVersion = 3 pluginName = "ocr-capability" -providerType = "plugin" +providerType = "ocr3-capability" telemetryType = "plugin" [onchainSigningStrategy] strategyName = 'single-chain' [onchainSigningStrategy.config] -publicKey = '8fa807463ad73f9ee855cfd60ba406dcf98a2855b3dd8af613107b0f6890a707' diff --git a/core/services/ccip/mocks/orm.go b/core/services/ccip/mocks/orm.go index 8a987c2160..20302dd0be 100644 --- a/core/services/ccip/mocks/orm.go +++ b/core/services/ccip/mocks/orm.go @@ -8,6 +8,8 @@ import ( ccip "github.com/smartcontractkit/chainlink/v2/core/services/ccip" mock "github.com/stretchr/testify/mock" + + time "time" ) // ORM is an autogenerated mock type for the ORM type @@ -335,6 +337,123 @@ func (_c *ORM_InsertTokenPricesForDestChain_Call) RunAndReturn(run func(context. return _c } +// UpsertGasPricesForDestChain provides a mock function with given fields: ctx, destChainSelector, gasPrices +func (_m *ORM) UpsertGasPricesForDestChain(ctx context.Context, destChainSelector uint64, gasPrices []ccip.GasPrice) (int64, error) { + ret := _m.Called(ctx, destChainSelector, gasPrices) + + if len(ret) == 0 { + panic("no return value specified for UpsertGasPricesForDestChain") + } + + var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, uint64, []ccip.GasPrice) (int64, error)); ok { + return rf(ctx, destChainSelector, gasPrices) + } + if rf, ok := ret.Get(0).(func(context.Context, uint64, []ccip.GasPrice) int64); ok { + r0 = rf(ctx, destChainSelector, gasPrices) + } else { + r0 = ret.Get(0).(int64) + } + + if rf, ok := ret.Get(1).(func(context.Context, uint64, []ccip.GasPrice) error); ok { + r1 = rf(ctx, destChainSelector, gasPrices) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ORM_UpsertGasPricesForDestChain_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpsertGasPricesForDestChain' +type ORM_UpsertGasPricesForDestChain_Call struct { + *mock.Call +} + +// UpsertGasPricesForDestChain is a helper method to define mock.On call +// - ctx context.Context +// - destChainSelector uint64 +// - gasPrices []ccip.GasPrice +func (_e *ORM_Expecter) UpsertGasPricesForDestChain(ctx interface{}, destChainSelector interface{}, gasPrices interface{}) *ORM_UpsertGasPricesForDestChain_Call { + return &ORM_UpsertGasPricesForDestChain_Call{Call: _e.mock.On("UpsertGasPricesForDestChain", ctx, destChainSelector, gasPrices)} +} + +func (_c *ORM_UpsertGasPricesForDestChain_Call) Run(run func(ctx context.Context, destChainSelector uint64, gasPrices []ccip.GasPrice)) *ORM_UpsertGasPricesForDestChain_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(uint64), args[2].([]ccip.GasPrice)) + }) + return _c +} + +func (_c *ORM_UpsertGasPricesForDestChain_Call) Return(_a0 int64, _a1 error) *ORM_UpsertGasPricesForDestChain_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ORM_UpsertGasPricesForDestChain_Call) RunAndReturn(run func(context.Context, uint64, []ccip.GasPrice) (int64, error)) *ORM_UpsertGasPricesForDestChain_Call { + _c.Call.Return(run) + return _c +} + +// UpsertTokenPricesForDestChain provides a mock function with given fields: ctx, destChainSelector, tokenPrices, interval +func (_m *ORM) UpsertTokenPricesForDestChain(ctx context.Context, destChainSelector uint64, tokenPrices []ccip.TokenPrice, interval time.Duration) (int64, error) { + ret := _m.Called(ctx, destChainSelector, tokenPrices, interval) + + if len(ret) == 0 { + panic("no return value specified for UpsertTokenPricesForDestChain") + } + + var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, uint64, []ccip.TokenPrice, time.Duration) (int64, error)); ok { + return rf(ctx, destChainSelector, tokenPrices, interval) + } + if rf, ok := ret.Get(0).(func(context.Context, uint64, []ccip.TokenPrice, time.Duration) int64); ok { + r0 = rf(ctx, destChainSelector, tokenPrices, interval) + } else { + r0 = ret.Get(0).(int64) + } + + if rf, ok := ret.Get(1).(func(context.Context, uint64, []ccip.TokenPrice, time.Duration) error); ok { + r1 = rf(ctx, destChainSelector, tokenPrices, interval) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ORM_UpsertTokenPricesForDestChain_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpsertTokenPricesForDestChain' +type ORM_UpsertTokenPricesForDestChain_Call struct { + *mock.Call +} + +// UpsertTokenPricesForDestChain is a helper method to define mock.On call +// - ctx context.Context +// - destChainSelector uint64 +// - tokenPrices []ccip.TokenPrice +// - interval time.Duration +func (_e *ORM_Expecter) UpsertTokenPricesForDestChain(ctx interface{}, destChainSelector interface{}, tokenPrices interface{}, interval interface{}) *ORM_UpsertTokenPricesForDestChain_Call { + return &ORM_UpsertTokenPricesForDestChain_Call{Call: _e.mock.On("UpsertTokenPricesForDestChain", ctx, destChainSelector, tokenPrices, interval)} +} + +func (_c *ORM_UpsertTokenPricesForDestChain_Call) Run(run func(ctx context.Context, destChainSelector uint64, tokenPrices []ccip.TokenPrice, interval time.Duration)) *ORM_UpsertTokenPricesForDestChain_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(uint64), args[2].([]ccip.TokenPrice), args[3].(time.Duration)) + }) + return _c +} + +func (_c *ORM_UpsertTokenPricesForDestChain_Call) Return(_a0 int64, _a1 error) *ORM_UpsertTokenPricesForDestChain_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ORM_UpsertTokenPricesForDestChain_Call) RunAndReturn(run func(context.Context, uint64, []ccip.TokenPrice, time.Duration) (int64, error)) *ORM_UpsertTokenPricesForDestChain_Call { + _c.Call.Return(run) + return _c +} + // 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/ccip/observability.go b/core/services/ccip/observability.go new file mode 100644 index 0000000000..8a061893ce --- /dev/null +++ b/core/services/ccip/observability.go @@ -0,0 +1,115 @@ +package ccip + +import ( + "context" + "strconv" + "time" + + "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +var ( + sqlLatencyBuckets = []float64{ + float64(10 * time.Millisecond), + float64(20 * time.Millisecond), + float64(30 * time.Millisecond), + float64(40 * time.Millisecond), + float64(50 * time.Millisecond), + float64(70 * time.Millisecond), + float64(90 * time.Millisecond), + float64(100 * time.Millisecond), + float64(200 * time.Millisecond), + float64(300 * time.Millisecond), + float64(400 * time.Millisecond), + float64(500 * time.Millisecond), + float64(750 * time.Millisecond), + float64(1 * time.Second), + } + ccipQueryDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{ + Name: "ccip_orm_query_duration", + Buckets: sqlLatencyBuckets, + }, []string{"query", "destChainSelector"}) + ccipQueryDatasets = promauto.NewGaugeVec(prometheus.GaugeOpts{ + Name: "ccip_orm_dataset_size", + }, []string{"query", "destChainSelector"}) +) + +type observedORM struct { + ORM + queryDuration *prometheus.HistogramVec + datasetSize *prometheus.GaugeVec +} + +var _ ORM = (*observedORM)(nil) + +func NewObservedORM(ds sqlutil.DataSource, lggr logger.Logger) (*observedORM, error) { + delegate, err := NewORM(ds, lggr) + if err != nil { + return nil, err + } + + return &observedORM{ + ORM: delegate, + queryDuration: ccipQueryDuration, + datasetSize: ccipQueryDatasets, + }, nil +} + +func (o *observedORM) GetGasPricesByDestChain(ctx context.Context, destChainSelector uint64) ([]GasPrice, error) { + return withObservedQueryAndResults(o, "GetGasPricesByDestChain", destChainSelector, func() ([]GasPrice, error) { + return o.ORM.GetGasPricesByDestChain(ctx, destChainSelector) + }) +} + +func (o *observedORM) GetTokenPricesByDestChain(ctx context.Context, destChainSelector uint64) ([]TokenPrice, error) { + return withObservedQueryAndResults(o, "GetTokenPricesByDestChain", destChainSelector, func() ([]TokenPrice, error) { + return o.ORM.GetTokenPricesByDestChain(ctx, destChainSelector) + }) +} + +func (o *observedORM) UpsertGasPricesForDestChain(ctx context.Context, destChainSelector uint64, gasPrices []GasPrice) (int64, error) { + return withObservedQueryAndRowsAffected(o, "UpsertGasPricesForDestChain", destChainSelector, func() (int64, error) { + return o.ORM.UpsertGasPricesForDestChain(ctx, destChainSelector, gasPrices) + }) +} + +func (o *observedORM) UpsertTokenPricesForDestChain(ctx context.Context, destChainSelector uint64, tokenPrices []TokenPrice, interval time.Duration) (int64, error) { + return withObservedQueryAndRowsAffected(o, "UpsertTokenPricesForDestChain", destChainSelector, func() (int64, error) { + return o.ORM.UpsertTokenPricesForDestChain(ctx, destChainSelector, tokenPrices, interval) + }) +} + +func withObservedQueryAndRowsAffected(o *observedORM, queryName string, chainSelector uint64, query func() (int64, error)) (int64, error) { + rowsAffected, err := withObservedQuery(o, queryName, chainSelector, query) + if err == nil { + o.datasetSize. + WithLabelValues(queryName, strconv.FormatUint(chainSelector, 10)). + Set(float64(rowsAffected)) + } + return rowsAffected, err +} + +func withObservedQueryAndResults[T any](o *observedORM, queryName string, chainSelector uint64, query func() ([]T, error)) ([]T, error) { + results, err := withObservedQuery(o, queryName, chainSelector, query) + if err == nil { + o.datasetSize. + WithLabelValues(queryName, strconv.FormatUint(chainSelector, 10)). + Set(float64(len(results))) + } + return results, err +} + +func withObservedQuery[T any](o *observedORM, queryName string, chainSelector uint64, query func() (T, error)) (T, error) { + queryStarted := time.Now() + defer func() { + o.queryDuration. + WithLabelValues(queryName, strconv.FormatUint(chainSelector, 10)). + Observe(float64(time.Since(queryStarted))) + }() + return query() +} diff --git a/core/services/ccip/observability_test.go b/core/services/ccip/observability_test.go new file mode 100644 index 0000000000..24bfb4a9ec --- /dev/null +++ b/core/services/ccip/observability_test.go @@ -0,0 +1,94 @@ +package ccip + +import ( + "math/big" + "testing" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/testutil" + io_prometheus_client "github.com/prometheus/client_model/go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +func Test_MetricsAreTrackedForAllMethods(t *testing.T) { + ctx := testutils.Context(t) + db := pgtest.NewSqlxDB(t) + ccipORM, err := NewObservedORM(db, logger.TestLogger(t)) + require.NoError(t, err) + + tokenPrices := []TokenPrice{ + { + TokenAddr: "0xA", + TokenPrice: assets.NewWei(big.NewInt(1e18)), + }, + { + TokenAddr: "0xB", + TokenPrice: assets.NewWei(big.NewInt(1e18)), + }, + } + tokensUpserted, err := ccipORM.UpsertTokenPricesForDestChain(ctx, 100, tokenPrices, time.Second) + require.NoError(t, err) + assert.Equal(t, len(tokenPrices), int(tokensUpserted)) + assert.Equal(t, len(tokenPrices), counterFromGaugeByLabels(ccipORM.datasetSize, "UpsertTokenPricesForDestChain", "100")) + assert.Equal(t, 0, counterFromGaugeByLabels(ccipORM.datasetSize, "UpsertTokenPricesForDestChain", "200")) + + tokens, err := ccipORM.GetTokenPricesByDestChain(ctx, 100) + require.NoError(t, err) + assert.Equal(t, len(tokenPrices), len(tokens)) + assert.Equal(t, len(tokenPrices), counterFromGaugeByLabels(ccipORM.datasetSize, "GetTokenPricesByDestChain", "100")) + assert.Equal(t, 1, counterFromHistogramByLabels(t, ccipORM.queryDuration, "GetTokenPricesByDestChain", "100")) + + gasPrices := []GasPrice{ + { + SourceChainSelector: 200, + GasPrice: assets.NewWei(big.NewInt(1e18)), + }, + { + SourceChainSelector: 201, + GasPrice: assets.NewWei(big.NewInt(1e18)), + }, + { + SourceChainSelector: 202, + GasPrice: assets.NewWei(big.NewInt(1e18)), + }, + } + gasUpserted, err := ccipORM.UpsertGasPricesForDestChain(ctx, 100, gasPrices) + require.NoError(t, err) + assert.Equal(t, len(gasPrices), int(gasUpserted)) + assert.Equal(t, len(gasPrices), counterFromGaugeByLabels(ccipORM.datasetSize, "UpsertGasPricesForDestChain", "100")) + assert.Equal(t, 0, counterFromGaugeByLabels(ccipORM.datasetSize, "UpsertGasPricesForDestChain", "200")) + + gas, err := ccipORM.GetGasPricesByDestChain(ctx, 100) + require.NoError(t, err) + assert.Equal(t, len(gasPrices), len(gas)) + assert.Equal(t, len(gasPrices), counterFromGaugeByLabels(ccipORM.datasetSize, "GetGasPricesByDestChain", "100")) + assert.Equal(t, 1, counterFromHistogramByLabels(t, ccipORM.queryDuration, "GetGasPricesByDestChain", "100")) +} + +func counterFromHistogramByLabels(t *testing.T, histogramVec *prometheus.HistogramVec, labels ...string) int { + observer, err := histogramVec.GetMetricWithLabelValues(labels...) + require.NoError(t, err) + + metricCh := make(chan prometheus.Metric, 1) + observer.(prometheus.Histogram).Collect(metricCh) + close(metricCh) + + metric := <-metricCh + pb := &io_prometheus_client.Metric{} + err = metric.Write(pb) + require.NoError(t, err) + + return int(pb.GetHistogram().GetSampleCount()) +} + +func counterFromGaugeByLabels(gaugeVec *prometheus.GaugeVec, labels ...string) int { + value := testutil.ToFloat64(gaugeVec.WithLabelValues(labels...)) + return int(value) +} diff --git a/core/services/ccip/orm.go b/core/services/ccip/orm.go index d074ea7473..09cc0af835 100644 --- a/core/services/ccip/orm.go +++ b/core/services/ccip/orm.go @@ -8,6 +8,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + "github.com/smartcontractkit/chainlink/v2/core/logger" ) type GasPrice struct { @@ -36,6 +37,9 @@ type ORM interface { GetGasPricesByDestChain(ctx context.Context, destChainSelector uint64) ([]GasPrice, error) GetTokenPricesByDestChain(ctx context.Context, destChainSelector uint64) ([]TokenPrice, error) + UpsertGasPricesForDestChain(ctx context.Context, destChainSelector uint64, gasPrices []GasPrice) (int64, error) + UpsertTokenPricesForDestChain(ctx context.Context, destChainSelector uint64, tokenPrices []TokenPrice, interval time.Duration) (int64, error) + InsertGasPricesForDestChain(ctx context.Context, destChainSelector uint64, jobId int32, gasPrices []GasPriceUpdate) error InsertTokenPricesForDestChain(ctx context.Context, destChainSelector uint64, jobId int32, tokenPrices []TokenPriceUpdate) error @@ -44,18 +48,20 @@ type ORM interface { } type orm struct { - ds sqlutil.DataSource + ds sqlutil.DataSource + lggr logger.Logger } var _ ORM = (*orm)(nil) -func NewORM(ds sqlutil.DataSource) (ORM, error) { +func NewORM(ds sqlutil.DataSource, lggr logger.Logger) (ORM, error) { if ds == nil { return nil, fmt.Errorf("datasource to CCIP NewORM cannot be nil") } return &orm{ - ds: ds, + ds: ds, + lggr: lggr, }, nil } @@ -89,10 +95,143 @@ func (o *orm) GetTokenPricesByDestChain(ctx context.Context, destChainSelector u if err != nil { return nil, err } - return tokenPrices, nil } +func (o *orm) UpsertGasPricesForDestChain(ctx context.Context, destChainSelector uint64, gasPrices []GasPrice) (int64, error) { + if len(gasPrices) == 0 { + return 0, nil + } + + uniqueGasUpdates := make(map[string]GasPrice) + for _, gasPrice := range gasPrices { + key := fmt.Sprintf("%d-%d", gasPrice.SourceChainSelector, destChainSelector) + uniqueGasUpdates[key] = gasPrice + } + + insertData := make([]map[string]interface{}, 0, len(uniqueGasUpdates)) + for _, price := range uniqueGasUpdates { + insertData = append(insertData, map[string]interface{}{ + "chain_selector": destChainSelector, + "source_chain_selector": price.SourceChainSelector, + "gas_price": price.GasPrice, + }) + } + + stmt := `INSERT INTO ccip.observed_gas_prices (chain_selector, source_chain_selector, gas_price, updated_at) + VALUES (:chain_selector, :source_chain_selector, :gas_price, statement_timestamp()) + ON CONFLICT (source_chain_selector, chain_selector) + DO UPDATE SET gas_price = EXCLUDED.gas_price, updated_at = EXCLUDED.updated_at;` + + result, err := o.ds.NamedExecContext(ctx, stmt, insertData) + if err != nil { + return 0, fmt.Errorf("error inserting gas prices %w", err) + } + return result.RowsAffected() +} + +// UpsertTokenPricesForDestChain inserts or updates only relevant token prices. +// In order to reduce locking an unnecessary writes to the table, we start with fetching current prices. +// If price for a token doesn't change or was updated recently we don't include that token to the upsert query. +// We don't run in TX intentionally, because we don't want to lock the table and conflicts are resolved on the insert level +func (o *orm) UpsertTokenPricesForDestChain(ctx context.Context, destChainSelector uint64, tokenPrices []TokenPrice, interval time.Duration) (int64, error) { + if len(tokenPrices) == 0 { + return 0, nil + } + + tokensToUpdate, err := o.pickOnlyRelevantTokensForUpdate(ctx, destChainSelector, tokenPrices, interval) + if err != nil || len(tokensToUpdate) == 0 { + return 0, err + } + + insertData := make([]map[string]interface{}, 0, len(tokensToUpdate)) + for _, price := range tokensToUpdate { + insertData = append(insertData, map[string]interface{}{ + "chain_selector": destChainSelector, + "token_addr": price.TokenAddr, + "token_price": price.TokenPrice, + }) + } + + stmt := `INSERT INTO ccip.observed_token_prices (chain_selector, token_addr, token_price, updated_at) + VALUES (:chain_selector, :token_addr, :token_price, statement_timestamp()) + ON CONFLICT (token_addr, chain_selector) + DO UPDATE SET token_price = EXCLUDED.token_price, updated_at = EXCLUDED.updated_at;` + result, err := o.ds.NamedExecContext(ctx, stmt, insertData) + if err != nil { + return 0, fmt.Errorf("error inserting token prices %w", err) + } + return result.RowsAffected() +} + +// pickOnlyRelevantTokensForUpdate returns only tokens that need to be updated. Multiple jobs can be updating the same tokens, +// in order to reduce table locking and redundant upserts we start with reading the table and checking which tokens are eligible for update. +// A token is eligible for update when time since last update is greater than the interval. +func (o *orm) pickOnlyRelevantTokensForUpdate( + ctx context.Context, + destChainSelector uint64, + tokenPrices []TokenPrice, + interval time.Duration, +) ([]TokenPrice, error) { + tokenPricesByAddress := toTokensByAddress(tokenPrices) + + // Picks only tokens which were recently updated and can be ignored, + // we will filter out these tokens from the upsert query. + stmt := ` + SELECT + token_addr + FROM ccip.observed_token_prices + WHERE + chain_selector = $1 + and token_addr = any($2) + and updated_at >= statement_timestamp() - $3::interval + ` + + pgInterval := fmt.Sprintf("%d milliseconds", interval.Milliseconds()) + args := []interface{}{destChainSelector, tokenAddrsToBytes(tokenPricesByAddress), pgInterval} + var dbTokensToIgnore []string + if err := o.ds.SelectContext(ctx, &dbTokensToIgnore, stmt, args...); err != nil { + return nil, err + } + + tokensToIgnore := make(map[string]struct{}, len(dbTokensToIgnore)) + for _, tk := range dbTokensToIgnore { + tokensToIgnore[tk] = struct{}{} + } + + tokenPricesToUpdate := make([]TokenPrice, 0, len(tokenPrices)) + for tokenAddr, tokenPrice := range tokenPricesByAddress { + eligibleForUpdate := false + if _, ok := tokensToIgnore[tokenAddr]; !ok { + eligibleForUpdate = true + tokenPricesToUpdate = append(tokenPricesToUpdate, TokenPrice{TokenAddr: tokenAddr, TokenPrice: tokenPrice}) + } + o.lggr.Debugw( + "Token price eligibility for database update", + "eligibleForUpdate", eligibleForUpdate, + "token", tokenAddr, + "price", tokenPrice, + ) + } + return tokenPricesToUpdate, nil +} + +func toTokensByAddress(tokens []TokenPrice) map[string]*assets.Wei { + tokensByAddr := make(map[string]*assets.Wei, len(tokens)) + for _, tk := range tokens { + tokensByAddr[tk.TokenAddr] = tk.TokenPrice + } + return tokensByAddr +} + +func tokenAddrsToBytes(tokens map[string]*assets.Wei) [][]byte { + addrs := make([][]byte, 0, len(tokens)) + for tkAddr := range tokens { + addrs = append(addrs, []byte(tkAddr)) + } + return addrs +} + func (o *orm) InsertGasPricesForDestChain(ctx context.Context, destChainSelector uint64, jobId int32, gasPrices []GasPriceUpdate) error { if len(gasPrices) == 0 { return nil diff --git a/core/services/ccip/orm_test.go b/core/services/ccip/orm_test.go index 7b7b8d8271..e778ddf6ce 100644 --- a/core/services/ccip/orm_test.go +++ b/core/services/ccip/orm_test.go @@ -15,13 +15,18 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +var ( + r = rand.New(rand.NewSource(time.Now().UnixNano())) ) func setupORM(t *testing.T) (ORM, sqlutil.DataSource) { t.Helper() db := pgtest.NewSqlxDB(t) - orm, err := NewORM(db) + orm, err := NewORM(db, logger.TestLogger(t)) require.NoError(t, err) @@ -37,12 +42,12 @@ func generateChainSelectors(n int) []uint64 { return selectors } -func generateGasPriceUpdates(chainSelector uint64, n int) []GasPriceUpdate { - updates := make([]GasPriceUpdate, n) +func generateGasPrices(chainSelector uint64, n int) []GasPrice { + updates := make([]GasPrice, n) for i := 0; i < n; i++ { // gas prices can take up whole range of uint256 uint256Max := new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(256), nil), big.NewInt(1)) - row := GasPriceUpdate{ + row := GasPrice{ SourceChainSelector: chainSelector, GasPrice: assets.NewWei(new(big.Int).Sub(uint256Max, big.NewInt(int64(i)))), } @@ -61,10 +66,21 @@ func generateTokenAddresses(n int) []string { return addrs } -func generateTokenPriceUpdates(tokenAddr string, n int) []TokenPriceUpdate { - updates := make([]TokenPriceUpdate, n) +func generateRandomTokenPrices(tokenAddrs []string) []TokenPrice { + updates := make([]TokenPrice, 0, len(tokenAddrs)) + for _, addr := range tokenAddrs { + updates = append(updates, TokenPrice{ + TokenAddr: addr, + TokenPrice: assets.NewWei(new(big.Int).Rand(r, big.NewInt(1e18))), + }) + } + return updates +} + +func generateTokenPrices(tokenAddr string, n int) []TokenPrice { + updates := make([]TokenPrice, n) for i := 0; i < n; i++ { - row := TokenPriceUpdate{ + row := TokenPrice{ TokenAddr: tokenAddr, TokenPrice: assets.NewWei(new(big.Int).Mul(big.NewInt(1e18), big.NewInt(int64(i)))), } @@ -134,20 +150,20 @@ func TestORM_InsertAndGetGasPrices(t *testing.T) { sourceSelectors := generateChainSelectors(numSourceChainSelectors) - updates := make(map[uint64][]GasPriceUpdate) + updates := make(map[uint64][]GasPrice) for _, selector := range sourceSelectors { - updates[selector] = generateGasPriceUpdates(selector, numUpdatesPerSourceSelector) + updates[selector] = generateGasPrices(selector, numUpdatesPerSourceSelector) } // 5 jobs, each inserting prices for 10 chains, with 20 updates per chain. - expectedPrices := make(map[uint64]GasPriceUpdate) + expectedPrices := make(map[uint64]GasPrice) for i := 0; i < numJobs; i++ { for selector, updatesPerSelector := range updates { lastIndex := len(updatesPerSelector) - 1 - err := orm.InsertGasPricesForDestChain(ctx, destSelector, int32(i), updatesPerSelector[:lastIndex]) + _, err := orm.UpsertGasPricesForDestChain(ctx, destSelector, updatesPerSelector[:lastIndex]) assert.NoError(t, err) - err = orm.InsertGasPricesForDestChain(ctx, destSelector, int32(i), updatesPerSelector[lastIndex:]) + _, err = orm.UpsertGasPricesForDestChain(ctx, destSelector, updatesPerSelector[lastIndex:]) assert.NoError(t, err) expectedPrices[selector] = updatesPerSelector[lastIndex] @@ -156,7 +172,7 @@ func TestORM_InsertAndGetGasPrices(t *testing.T) { // verify number of rows inserted numRows := getGasTableRowCount(t, db) - assert.Equal(t, numJobs*numSourceChainSelectors*numUpdatesPerSourceSelector, numRows) + assert.Equal(t, numSourceChainSelectors, numRows) prices, err := orm.GetGasPricesByDestChain(ctx, destSelector) assert.NoError(t, err) @@ -170,15 +186,15 @@ func TestORM_InsertAndGetGasPrices(t *testing.T) { } // after the initial inserts, insert new round of prices, 1 price per selector this time - var combinedUpdates []GasPriceUpdate + var combinedUpdates []GasPrice for selector, updatesPerSelector := range updates { combinedUpdates = append(combinedUpdates, updatesPerSelector[0]) expectedPrices[selector] = updatesPerSelector[0] } - err = orm.InsertGasPricesForDestChain(ctx, destSelector, 1, combinedUpdates) + _, err = orm.UpsertGasPricesForDestChain(ctx, destSelector, combinedUpdates) assert.NoError(t, err) - assert.Equal(t, numJobs*numSourceChainSelectors*numUpdatesPerSourceSelector+numSourceChainSelectors, getGasTableRowCount(t, db)) + assert.Equal(t, numSourceChainSelectors, getGasTableRowCount(t, db)) prices, err = orm.GetGasPricesByDestChain(ctx, destSelector) assert.NoError(t, err) @@ -190,7 +206,7 @@ func TestORM_InsertAndGetGasPrices(t *testing.T) { } } -func TestORM_InsertAndDeleteGasPrices(t *testing.T) { +func TestORM_UpsertGasPrices(t *testing.T) { t.Parallel() ctx := testutils.Context(t) @@ -202,13 +218,13 @@ func TestORM_InsertAndDeleteGasPrices(t *testing.T) { sourceSelectors := generateChainSelectors(numSourceChainSelectors) - updates := make(map[uint64][]GasPriceUpdate) + updates := make(map[uint64][]GasPrice) for _, selector := range sourceSelectors { - updates[selector] = generateGasPriceUpdates(selector, numUpdatesPerSourceSelector) + updates[selector] = generateGasPrices(selector, numUpdatesPerSourceSelector) } for _, updatesPerSelector := range updates { - err := orm.InsertGasPricesForDestChain(ctx, destSelector, 1, updatesPerSelector) + _, err := orm.UpsertGasPricesForDestChain(ctx, destSelector, updatesPerSelector) assert.NoError(t, err) } @@ -217,21 +233,11 @@ func TestORM_InsertAndDeleteGasPrices(t *testing.T) { // insert for the 2nd time after interimTimeStamp for _, updatesPerSelector := range updates { - err := orm.InsertGasPricesForDestChain(ctx, destSelector, 1, updatesPerSelector) + _, err := orm.UpsertGasPricesForDestChain(ctx, destSelector, updatesPerSelector) assert.NoError(t, err) } - assert.Equal(t, 2*numSourceChainSelectors*numUpdatesPerSourceSelector, getGasTableRowCount(t, db)) - - // clear by sleepSec should delete rows inserted before it - err := orm.ClearGasPricesByDestChain(ctx, destSelector, sleepSec) - assert.NoError(t, err) - assert.Equal(t, numSourceChainSelectors*numUpdatesPerSourceSelector, getGasTableRowCount(t, db)) - - // clear by 0 expiration seconds should delete all rows - err = orm.ClearGasPricesByDestChain(ctx, destSelector, 0) - assert.NoError(t, err) - assert.Equal(t, 0, getGasTableRowCount(t, db)) + assert.Equal(t, numSourceChainSelectors, getGasTableRowCount(t, db)) } func TestORM_InsertAndGetTokenPrices(t *testing.T) { @@ -247,20 +253,20 @@ func TestORM_InsertAndGetTokenPrices(t *testing.T) { addrs := generateTokenAddresses(numAddresses) - updates := make(map[string][]TokenPriceUpdate) + updates := make(map[string][]TokenPrice) for _, addr := range addrs { - updates[addr] = generateTokenPriceUpdates(addr, numUpdatesPerAddress) + updates[addr] = generateTokenPrices(addr, numUpdatesPerAddress) } // 5 jobs, each inserting prices for 10 chains, with 20 updates per chain. - expectedPrices := make(map[string]TokenPriceUpdate) + expectedPrices := make(map[string]TokenPrice) for i := 0; i < numJobs; i++ { for addr, updatesPerAddr := range updates { lastIndex := len(updatesPerAddr) - 1 - err := orm.InsertTokenPricesForDestChain(ctx, destSelector, int32(i), updatesPerAddr[:lastIndex]) + _, err := orm.UpsertTokenPricesForDestChain(ctx, destSelector, updatesPerAddr[:lastIndex], 0) assert.NoError(t, err) - err = orm.InsertTokenPricesForDestChain(ctx, destSelector, int32(i), updatesPerAddr[lastIndex:]) + _, err = orm.UpsertTokenPricesForDestChain(ctx, destSelector, updatesPerAddr[lastIndex:], 0) assert.NoError(t, err) expectedPrices[addr] = updatesPerAddr[lastIndex] @@ -269,7 +275,7 @@ func TestORM_InsertAndGetTokenPrices(t *testing.T) { // verify number of rows inserted numRows := getTokenTableRowCount(t, db) - assert.Equal(t, numJobs*numAddresses*numUpdatesPerAddress, numRows) + assert.Equal(t, numAddresses, numRows) prices, err := orm.GetTokenPricesByDestChain(ctx, destSelector) assert.NoError(t, err) @@ -283,15 +289,15 @@ func TestORM_InsertAndGetTokenPrices(t *testing.T) { } // after the initial inserts, insert new round of prices, 1 price per selector this time - var combinedUpdates []TokenPriceUpdate + var combinedUpdates []TokenPrice for addr, updatesPerAddr := range updates { combinedUpdates = append(combinedUpdates, updatesPerAddr[0]) expectedPrices[addr] = updatesPerAddr[0] } - err = orm.InsertTokenPricesForDestChain(ctx, destSelector, 1, combinedUpdates) + _, err = orm.UpsertTokenPricesForDestChain(ctx, destSelector, combinedUpdates, 0) assert.NoError(t, err) - assert.Equal(t, numJobs*numAddresses*numUpdatesPerAddress+numAddresses, getTokenTableRowCount(t, db)) + assert.Equal(t, numAddresses, getTokenTableRowCount(t, db)) prices, err = orm.GetTokenPricesByDestChain(ctx, destSelector) assert.NoError(t, err) @@ -303,46 +309,68 @@ func TestORM_InsertAndGetTokenPrices(t *testing.T) { } } -func TestORM_InsertAndDeleteTokenPrices(t *testing.T) { +func TestORM_InsertTokenPricesWhenExpired(t *testing.T) { t.Parallel() ctx := testutils.Context(t) - - orm, db := setupORM(t) + orm, _ := setupORM(t) numAddresses := 10 - numUpdatesPerAddress := 20 - destSelector := uint64(1) - + destSelector := rand.Uint64() addrs := generateTokenAddresses(numAddresses) + initTokenUpdates := generateRandomTokenPrices(addrs) - updates := make(map[string][]TokenPriceUpdate) - for _, addr := range addrs { - updates[addr] = generateTokenPriceUpdates(addr, numUpdatesPerAddress) - } + // Insert the first time, table is initialized + rowsUpdated, err := orm.UpsertTokenPricesForDestChain(ctx, destSelector, initTokenUpdates, time.Minute) + require.NoError(t, err) + assert.Equal(t, int64(numAddresses), rowsUpdated) - for _, updatesPerAddr := range updates { - err := orm.InsertTokenPricesForDestChain(ctx, destSelector, 1, updatesPerAddr) - assert.NoError(t, err) - } + //time.Sleep(100 * time.Millisecond) - sleepSec := 2 - time.Sleep(time.Duration(sleepSec) * time.Second) + // Insert the second time, no updates, because prices haven't changed + rowsUpdated, err = orm.UpsertTokenPricesForDestChain(ctx, destSelector, initTokenUpdates, time.Minute) + require.NoError(t, err) + assert.Equal(t, int64(0), rowsUpdated) - // insert for the 2nd time after interimTimeStamp - for _, updatesPerAddr := range updates { - err := orm.InsertTokenPricesForDestChain(ctx, destSelector, 1, updatesPerAddr) - assert.NoError(t, err) + // There are new prices, but we still haven't reached interval + newPrices := generateRandomTokenPrices(addrs) + rowsUpdated, err = orm.UpsertTokenPricesForDestChain(ctx, destSelector, newPrices, time.Minute) + require.NoError(t, err) + assert.Equal(t, int64(0), rowsUpdated) + + time.Sleep(100 * time.Millisecond) + + // Again with the same new prices, but this time interval is reached + rowsUpdated, err = orm.UpsertTokenPricesForDestChain(ctx, destSelector, newPrices, time.Millisecond) + require.NoError(t, err) + assert.Equal(t, int64(numAddresses), rowsUpdated) + + dbTokenPrices, err := orm.GetTokenPricesByDestChain(ctx, destSelector) + require.NoError(t, err) + assert.Len(t, dbTokenPrices, numAddresses) + + dbTokenPricesByAddr := toTokensByAddress(dbTokenPrices) + for _, tkPrice := range newPrices { + dbToken, ok := dbTokenPricesByAddr[tkPrice.TokenAddr] + assert.True(t, ok) + assert.Equal(t, dbToken, tkPrice.TokenPrice) } +} - assert.Equal(t, 2*numAddresses*numUpdatesPerAddress, getTokenTableRowCount(t, db)) +func Benchmark_UpsertsTheSameTokenPrices(b *testing.B) { + db := pgtest.NewSqlxDB(b) + orm, err := NewORM(db, logger.NullLogger) + require.NoError(b, err) - // clear by sleepSec should delete rows inserted before it - err := orm.ClearTokenPricesByDestChain(ctx, destSelector, sleepSec) - assert.NoError(t, err) - assert.Equal(t, numAddresses*numUpdatesPerAddress, getTokenTableRowCount(t, db)) + ctx := testutils.Context(b) + numAddresses := 50 + destSelector := rand.Uint64() + addrs := generateTokenAddresses(numAddresses) + tokenUpdates := generateRandomTokenPrices(addrs) - // clear by 0 expiration seconds should delete all rows - err = orm.ClearTokenPricesByDestChain(ctx, destSelector, 0) - assert.NoError(t, err) - assert.Equal(t, 0, getTokenTableRowCount(t, db)) + b.ResetTimer() + + for i := 0; i < b.N; i++ { + _, err1 := orm.UpsertTokenPricesForDestChain(ctx, destSelector, tokenUpdates, time.Second) + require.NoError(b, err1) + } } diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index e880ecfad2..b938fe2cdf 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -30,8 +30,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/standardcapabilities" "github.com/smartcontractkit/chainlink/v2/core/static" - "github.com/smartcontractkit/chainlink/v2/core/services/promreporter" - "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/build" "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote" @@ -49,6 +47,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/feeds" "github.com/smartcontractkit/chainlink/v2/core/services/fluxmonitorv2" "github.com/smartcontractkit/chainlink/v2/core/services/gateway" + "github.com/smartcontractkit/chainlink/v2/core/services/headreporter" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keeper" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" @@ -150,7 +149,6 @@ type ChainlinkApplication struct { shutdownOnce sync.Once srvcs []services.ServiceCtx HealthChecker services.Checker - Nurse *services.Nurse logger logger.SugaredLogger AuditLogger audit.AuditLogger closeLogger func() error @@ -216,42 +214,52 @@ func NewApplication(opts ApplicationOpts) (Application, error) { signer := externalPeer externalPeerWrapper = externalPeer remoteDispatcher := remote.NewDispatcher(externalPeerWrapper, signer, opts.CapabilitiesRegistry, globalLogger) - srvcs = append(srvcs, remoteDispatcher) - dispatcher = remoteDispatcher } else { dispatcher = opts.CapabilitiesDispatcher externalPeerWrapper = opts.CapabilitiesPeerWrapper } - srvcs = append(srvcs, externalPeerWrapper) + srvcs = append(srvcs, externalPeerWrapper, dispatcher) - rid := cfg.Capabilities().ExternalRegistry().RelayID() - registryAddress := cfg.Capabilities().ExternalRegistry().Address() - relayer, err := relayerChainInterops.Get(rid) - if err != nil { - return nil, fmt.Errorf("could not fetch relayer %s configured for capabilities registry: %w", rid, err) - } + if cfg.Capabilities().ExternalRegistry().Address() != "" { + rid := cfg.Capabilities().ExternalRegistry().RelayID() + registryAddress := cfg.Capabilities().ExternalRegistry().Address() + relayer, err := relayerChainInterops.Get(rid) + if err != nil { + return nil, fmt.Errorf("could not fetch relayer %s configured for capabilities registry: %w", rid, err) + } + registrySyncer, err := registrysyncer.New( + globalLogger, + func() (p2ptypes.PeerID, error) { + p := externalPeerWrapper.GetPeer() + if p == nil { + return p2ptypes.PeerID{}, errors.New("could not get peer") + } - registrySyncer, err := registrysyncer.New( - globalLogger, - externalPeerWrapper, - relayer, - registryAddress, - ) - if err != nil { - return nil, fmt.Errorf("could not configure syncer: %w", err) - } + return p.ID(), nil + }, + relayer, + registryAddress, + registrysyncer.NewORM(opts.DS, globalLogger), + ) + if err != nil { + return nil, fmt.Errorf("could not configure syncer: %w", err) + } - wfLauncher := capabilities.NewLauncher( - globalLogger, - externalPeerWrapper, - dispatcher, - opts.CapabilitiesRegistry, - ) - registrySyncer.AddLauncher(wfLauncher) + wfLauncher := capabilities.NewLauncher( + globalLogger, + externalPeerWrapper, + dispatcher, + opts.CapabilitiesRegistry, + ) + registrySyncer.AddLauncher(wfLauncher) - srvcs = append(srvcs, dispatcher, wfLauncher, registrySyncer) + srvcs = append(srvcs, wfLauncher, registrySyncer) + } + } else { + globalLogger.Debug("External registry not configured, skipping registry syncer and starting with an empty registry") + opts.CapabilitiesRegistry.SetLocalRegistry(&capabilities.TestMetadataRegistry{}) } // LOOPs can be created as options, in the case of LOOP relayers, or @@ -281,14 +289,9 @@ func NewApplication(opts ApplicationOpts) (Application, error) { } ap := cfg.AutoPprof() - var nurse *services.Nurse if ap.Enabled() { globalLogger.Info("Nurse service (automatic pprof profiling) is enabled") - nurse = services.NewNurse(ap, globalLogger) - err := nurse.Start() - if err != nil { - return nil, err - } + srvcs = append(srvcs, services.NewNurse(ap, globalLogger)) } else { globalLogger.Info("Nurse service (automatic pprof profiling) is disabled") } @@ -324,8 +327,6 @@ func NewApplication(opts ApplicationOpts) (Application, error) { srvcs = append(srvcs, mailMon) srvcs = append(srvcs, relayerChainInterops.Services()...) - promReporter := promreporter.NewPromReporter(opts.DS, legacyEVMChains, globalLogger) - srvcs = append(srvcs, promReporter) // Initialize Local Users ORM and Authentication Provider specified in config // BasicAdminUsersORM is initialized and required regardless of separate Authentication Provider @@ -365,8 +366,16 @@ func NewApplication(opts ApplicationOpts) (Application, error) { workflowORM = workflowstore.NewDBStore(opts.DS, globalLogger, clockwork.NewRealClock()) ) + promReporter := headreporter.NewPrometheusReporter(opts.DS, legacyEVMChains) + chainIDs := make([]*big.Int, legacyEVMChains.Len()) + for i, chain := range legacyEVMChains.Slice() { + chainIDs[i] = chain.ID() + } + telemReporter := headreporter.NewTelemetryReporter(telemetryManager, globalLogger, chainIDs...) + headReporter := headreporter.NewHeadReporterService(opts.DS, globalLogger, promReporter, telemReporter) + srvcs = append(srvcs, headReporter) for _, chain := range legacyEVMChains.Slice() { - chain.HeadBroadcaster().Subscribe(promReporter) + chain.HeadBroadcaster().Subscribe(headReporter) chain.TxManager().RegisterResumeCallback(pipelineRunner.ResumeRun) } @@ -592,7 +601,6 @@ func NewApplication(opts ApplicationOpts) (Application, error) { SessionReaper: sessionReaper, ExternalInitiatorManager: externalInitiatorManager, HealthChecker: healthChecker, - Nurse: nurse, logger: globalLogger, AuditLogger: auditLogger, closeLogger: opts.CloseLogger, @@ -712,10 +720,6 @@ func (app *ChainlinkApplication) stop() (err error) { err = multierr.Append(err, app.FeedsService.Close()) } - if app.Nurse != nil { - err = multierr.Append(err, app.Nurse.Close()) - } - if app.profiler != nil { err = multierr.Append(err, app.profiler.Stop()) } diff --git a/core/services/chainlink/config.go b/core/services/chainlink/config.go index dc301c0967..476e758ccb 100644 --- a/core/services/chainlink/config.go +++ b/core/services/chainlink/config.go @@ -55,8 +55,13 @@ type RawConfig map[string]any // ValidateConfig returns an error if the Config is not valid for use, as-is. func (c *RawConfig) ValidateConfig() (err error) { if v, ok := (*c)["Enabled"]; ok { - if _, ok := v.(*bool); !ok { - err = multierr.Append(err, commonconfig.ErrInvalid{Name: "Enabled", Value: v, Msg: "expected *bool"}) + if _, ok := v.(bool); !ok { + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "Enabled", Value: v, Msg: "expected bool"}) + } + } + if v, ok := (*c)["ChainID"]; ok { + if _, ok := v.(string); !ok { + err = multierr.Append(err, commonconfig.ErrInvalid{Name: "ChainID", Value: v, Msg: "expected string"}) } } return err @@ -67,7 +72,17 @@ func (c *RawConfig) IsEnabled() bool { return false } - return (*c)["Enabled"] == nil || *(*c)["Enabled"].(*bool) + enabled, ok := (*c)["Enabled"].(bool) + return ok && enabled +} + +func (c *RawConfig) ChainID() string { + if c == nil { + return "" + } + + chainID, _ := (*c)["ChainID"].(string) + return chainID } // TOMLString returns a TOML encoded string. @@ -170,6 +185,9 @@ func (c *Config) SetFrom(f *Config) (err error) { err = multierr.Append(err, commonconfig.NamedMultiErrorList(err4, "Starknet")) } + // the plugin should handle it's own defaults and merging + c.Aptos = f.Aptos + _, err = commonconfig.MultiErrorList(err) return err diff --git a/core/services/chainlink/config_general.go b/core/services/chainlink/config_general.go index 5b6b839fb5..79c92f8214 100644 --- a/core/services/chainlink/config_general.go +++ b/core/services/chainlink/config_general.go @@ -209,6 +209,10 @@ func (g *generalConfig) StarknetConfigs() starknet.TOMLConfigs { return g.c.Starknet } +func (g *generalConfig) AptosConfigs() RawConfigs { + return g.c.Aptos +} + func (g *generalConfig) Validate() error { return g.validate(g.secrets.Validate) } diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 9afc0aa942..b808a6a154 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -618,6 +618,7 @@ func TestConfig_Marshal(t *testing.T) { TransactionAlreadyMined: ptr[string]("(: |^)transaction already mined"), Fatal: ptr[string]("(: |^)fatal"), ServiceUnavailable: ptr[string]("(: |^)service unavailable"), + TooManyResults: ptr[string]("(: |^)too many results"), }, }, OCR: evmcfg.OCR{ @@ -1096,6 +1097,7 @@ L2Full = '(: |^)l2 full' TransactionAlreadyMined = '(: |^)transaction already mined' Fatal = '(: |^)fatal' ServiceUnavailable = '(: |^)service unavailable' +TooManyResults = '(: |^)too many results' [EVM.OCR] ContractConfirmations = 11 @@ -1373,7 +1375,7 @@ func TestConfig_Validate(t *testing.T) { - 1: 2 errors: - ChainID: missing: required for all chains - Nodes: missing: must have at least one node - - Aptos.0.Enabled: invalid value (1): expected *bool`}, + - Aptos.0.Enabled: invalid value (1): expected bool`}, } { t.Run(tt.name, func(t *testing.T) { var c Config diff --git a/core/services/chainlink/mocks/general_config.go b/core/services/chainlink/mocks/general_config.go index c42ed5c701..f4594a4322 100644 --- a/core/services/chainlink/mocks/general_config.go +++ b/core/services/chainlink/mocks/general_config.go @@ -4,6 +4,8 @@ package mocks import ( chainlinkconfig "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" + chainlink "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" + config "github.com/smartcontractkit/chainlink/v2/core/config" cosmosconfig "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" @@ -81,6 +83,53 @@ func (_c *GeneralConfig_AppID_Call) RunAndReturn(run func() uuid.UUID) *GeneralC return _c } +// AptosConfigs provides a mock function with given fields: +func (_m *GeneralConfig) AptosConfigs() chainlink.RawConfigs { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for AptosConfigs") + } + + var r0 chainlink.RawConfigs + if rf, ok := ret.Get(0).(func() chainlink.RawConfigs); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(chainlink.RawConfigs) + } + } + + return r0 +} + +// GeneralConfig_AptosConfigs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AptosConfigs' +type GeneralConfig_AptosConfigs_Call struct { + *mock.Call +} + +// AptosConfigs is a helper method to define mock.On call +func (_e *GeneralConfig_Expecter) AptosConfigs() *GeneralConfig_AptosConfigs_Call { + return &GeneralConfig_AptosConfigs_Call{Call: _e.mock.On("AptosConfigs")} +} + +func (_c *GeneralConfig_AptosConfigs_Call) Run(run func()) *GeneralConfig_AptosConfigs_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *GeneralConfig_AptosConfigs_Call) Return(_a0 chainlink.RawConfigs) *GeneralConfig_AptosConfigs_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *GeneralConfig_AptosConfigs_Call) RunAndReturn(run func() chainlink.RawConfigs) *GeneralConfig_AptosConfigs_Call { + _c.Call.Return(run) + return _c +} + // AptosEnabled provides a mock function with given fields: func (_m *GeneralConfig) AptosEnabled() bool { ret := _m.Called() diff --git a/core/services/chainlink/relayer_chain_interoperators.go b/core/services/chainlink/relayer_chain_interoperators.go index 60381c0d47..ffcfc67b87 100644 --- a/core/services/chainlink/relayer_chain_interoperators.go +++ b/core/services/chainlink/relayer_chain_interoperators.go @@ -186,6 +186,23 @@ func InitStarknet(ctx context.Context, factory RelayerFactory, config StarkNetFa } } +// InitAptos is a option for instantiating Aptos relayers +func InitAptos(ctx context.Context, factory RelayerFactory, config AptosFactoryConfig) CoreRelayerChainInitFunc { + return func(op *CoreRelayerChainInteroperators) (err error) { + relayers, err := factory.NewAptos(config.Keystore, config.TOMLConfigs) + if err != nil { + return fmt.Errorf("failed to setup aptos relayer: %w", err) + } + + for id, relayer := range relayers { + op.srvs = append(op.srvs, relayer) + op.loopRelayers[id] = relayer + } + + return nil + } +} + // Get a [loop.Relayer] by id func (rs *CoreRelayerChainInteroperators) Get(id types.RelayID) (loop.Relayer, error) { rs.mu.Lock() diff --git a/core/services/chainlink/relayer_factory.go b/core/services/chainlink/relayer_factory.go index 849964f9be..11e477a54f 100644 --- a/core/services/chainlink/relayer_factory.go +++ b/core/services/chainlink/relayer_factory.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "net/http" "github.com/pelletier/go-toml/v2" @@ -18,7 +19,7 @@ import ( solcfg "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" pkgstarknet "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink" starkchain "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/chain" - "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" + starkcfg "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" coreconfig "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/config/env" @@ -37,6 +38,7 @@ type RelayerFactory struct { loop.GRPCOpts MercuryPool wsrpc.Pool CapabilitiesRegistry coretypes.CapabilitiesRegistry + HTTPClient *http.Client } type DummyFactoryConfig struct { @@ -85,6 +87,7 @@ func (r *RelayerFactory) NewEVM(ctx context.Context, config EVMFactoryConfig) (m MercuryPool: r.MercuryPool, TransmitterConfig: config.MercuryTransmitter, CapabilitiesRegistry: r.CapabilitiesRegistry, + HTTPClient: r.HTTPClient, } relayer, err2 := evmrelay.NewRelayer(lggr.Named(relayID.ChainID), chain, relayerOpts) if err2 != nil { @@ -171,12 +174,12 @@ func (r *RelayerFactory) NewSolana(ks keystore.Solana, chainCfgs solcfg.TOMLConf type StarkNetFactoryConfig struct { Keystore keystore.StarkNet - config.TOMLConfigs + starkcfg.TOMLConfigs } // TODO BCF-2606 consider consolidating the driving logic with that of NewSolana above via generics // perhaps when we implement a Cosmos LOOP -func (r *RelayerFactory) NewStarkNet(ks keystore.StarkNet, chainCfgs config.TOMLConfigs) (map[types.RelayID]loop.Relayer, error) { +func (r *RelayerFactory) NewStarkNet(ks keystore.StarkNet, chainCfgs starkcfg.TOMLConfigs) (map[types.RelayID]loop.Relayer, error) { starknetRelayers := make(map[types.RelayID]loop.Relayer) var ( @@ -205,7 +208,7 @@ func (r *RelayerFactory) NewStarkNet(ks keystore.StarkNet, chainCfgs config.TOML if cmdName := env.StarknetPlugin.Cmd.Get(); cmdName != "" { // setup the starknet relayer to be a LOOP cfgTOML, err := toml.Marshal(struct { - Starknet config.TOMLConfig + Starknet starkcfg.TOMLConfig }{Starknet: *chainCfg}) if err != nil { return nil, fmt.Errorf("failed to marshal StarkNet configs: %w", err) @@ -301,3 +304,65 @@ func (r *RelayerFactory) NewCosmos(config CosmosFactoryConfig) (map[types.RelayI } return relayers, nil } + +type AptosFactoryConfig struct { + Keystore keystore.Aptos + TOMLConfigs RawConfigs +} + +func (r *RelayerFactory) NewAptos(ks keystore.Aptos, chainCfgs RawConfigs) (map[types.RelayID]loop.Relayer, error) { + plugin := env.NewPlugin("aptos") + loopKs := &keystore.AptosLooppSigner{Aptos: ks} + return r.NewLOOPRelayer("Aptos", corerelay.NetworkAptos, plugin, loopKs, chainCfgs) +} + +func (r *RelayerFactory) NewLOOPRelayer(name string, network string, plugin env.Plugin, ks coretypes.Keystore, chainCfgs RawConfigs) (map[types.RelayID]loop.Relayer, error) { + relayers := make(map[types.RelayID]loop.Relayer) + lggr := r.Logger.Named(name) + + unique := make(map[string]struct{}) + // create one relayer per chain id + for _, chainCfg := range chainCfgs { + relayID := types.RelayID{Network: network, ChainID: chainCfg.ChainID()} + if _, alreadyExists := unique[relayID.Name()]; alreadyExists { + return nil, fmt.Errorf("duplicate chain definitions for %s", relayID.Name()) + } + unique[relayID.Name()] = struct{}{} + + // skip disabled chains from further processing + if !chainCfg.IsEnabled() { + lggr.Warnw("Skipping disabled chain", "id", relayID.ChainID) + continue + } + + lggr2 := lggr.Named(relayID.ChainID) + + cmdName := plugin.Cmd.Get() + if cmdName == "" { + return nil, fmt.Errorf("plugin not defined: %s", "") + } + + // setup the relayer as a LOOP + cfgTOML, err := toml.Marshal(chainCfg) + if err != nil { + return nil, fmt.Errorf("failed to marshal configs: %w", err) + } + + envVars, err := plugins.ParseEnvFile(plugin.Env.Get()) + if err != nil { + return nil, fmt.Errorf("failed to parse env file: %w", err) + } + cmdFn, err := plugins.NewCmdFactory(r.Register, plugins.CmdConfig{ + ID: relayID.Name(), + Cmd: cmdName, + Env: envVars, + }) + if err != nil { + return nil, fmt.Errorf("failed to create LOOP command: %w", err) + } + // the relayer service has a delicate keystore dependency. the value that is passed to NewRelayerService must + // be compatible with instantiating a starknet transaction manager KeystoreAdapter within the LOOPp executable. + relayers[relayID] = loop.NewRelayerService(lggr2, r.GRPCOpts, cmdFn, string(cfgTOML), ks, r.CapabilitiesRegistry) + } + return relayers, nil +} diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index c10d59f339..1e1c4351a2 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -384,6 +384,7 @@ L2Full = '(: |^)l2 full' TransactionAlreadyMined = '(: |^)transaction already mined' Fatal = '(: |^)fatal' ServiceUnavailable = '(: |^)service unavailable' +TooManyResults = '(: |^)too many results' [EVM.OCR] ContractConfirmations = 11 diff --git a/core/services/chainlink/types.go b/core/services/chainlink/types.go index 4aa3782549..74ffc5dc66 100644 --- a/core/services/chainlink/types.go +++ b/core/services/chainlink/types.go @@ -15,6 +15,7 @@ type GeneralConfig interface { CosmosConfigs() coscfg.TOMLConfigs SolanaConfigs() solcfg.TOMLConfigs StarknetConfigs() stkcfg.TOMLConfigs + AptosConfigs() RawConfigs // ConfigTOML returns both the user provided and effective configuration as TOML. ConfigTOML() (user, effective string) } diff --git a/core/services/feeds/proto/feeds_manager.pb.go b/core/services/feeds/proto/feeds_manager.pb.go index 89f351a427..010ee44ae8 100644 --- a/core/services/feeds/proto/feeds_manager.pb.go +++ b/core/services/feeds/proto/feeds_manager.pb.go @@ -7,10 +7,11 @@ package proto import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( @@ -79,8 +80,7 @@ const ( ChainType_CHAIN_TYPE_UNSPECIFIED ChainType = 0 ChainType_CHAIN_TYPE_EVM ChainType = 1 ChainType_CHAIN_TYPE_SOLANA ChainType = 2 - ChainType_CHAIN_TYPE_ZKSYNC ChainType = 3 - ChainType_CHAIN_TYPE_STARKNET ChainType = 4 + ChainType_CHAIN_TYPE_STARKNET ChainType = 3 ) // Enum value maps for ChainType. @@ -89,15 +89,13 @@ var ( 0: "CHAIN_TYPE_UNSPECIFIED", 1: "CHAIN_TYPE_EVM", 2: "CHAIN_TYPE_SOLANA", - 3: "CHAIN_TYPE_ZKSYNC", - 4: "CHAIN_TYPE_STARKNET", + 3: "CHAIN_TYPE_STARKNET", } ChainType_value = map[string]int32{ "CHAIN_TYPE_UNSPECIFIED": 0, "CHAIN_TYPE_EVM": 1, "CHAIN_TYPE_SOLANA": 2, - "CHAIN_TYPE_ZKSYNC": 3, - "CHAIN_TYPE_STARKNET": 4, + "CHAIN_TYPE_STARKNET": 3, } ) @@ -476,13 +474,17 @@ type ChainConfig struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Chain *Chain `protobuf:"bytes,1,opt,name=chain,proto3" json:"chain,omitempty"` - AccountAddress string `protobuf:"bytes,2,opt,name=account_address,json=accountAddress,proto3" json:"account_address,omitempty"` - AdminAddress string `protobuf:"bytes,3,opt,name=admin_address,json=adminAddress,proto3" json:"admin_address,omitempty"` - FluxMonitorConfig *FluxMonitorConfig `protobuf:"bytes,4,opt,name=flux_monitor_config,json=fluxMonitorConfig,proto3" json:"flux_monitor_config,omitempty"` - Ocr1Config *OCR1Config `protobuf:"bytes,5,opt,name=ocr1_config,json=ocr1Config,proto3" json:"ocr1_config,omitempty"` - Ocr2Config *OCR2Config `protobuf:"bytes,6,opt,name=ocr2_config,json=ocr2Config,proto3" json:"ocr2_config,omitempty"` - AccountAddressPublicKey *string `protobuf:"bytes,7,opt,name=account_address_public_key,json=accountAddressPublicKey,proto3,oneof" json:"account_address_public_key,omitempty"` + Chain *Chain `protobuf:"bytes,1,opt,name=chain,proto3" json:"chain,omitempty"` + AccountAddress string `protobuf:"bytes,2,opt,name=account_address,json=accountAddress,proto3" json:"account_address,omitempty"` + AdminAddress string `protobuf:"bytes,3,opt,name=admin_address,json=adminAddress,proto3" json:"admin_address,omitempty"` + FluxMonitorConfig *FluxMonitorConfig `protobuf:"bytes,4,opt,name=flux_monitor_config,json=fluxMonitorConfig,proto3" json:"flux_monitor_config,omitempty"` + Ocr1Config *OCR1Config `protobuf:"bytes,5,opt,name=ocr1_config,json=ocr1Config,proto3" json:"ocr1_config,omitempty"` + Ocr2Config *OCR2Config `protobuf:"bytes,6,opt,name=ocr2_config,json=ocr2Config,proto3" json:"ocr2_config,omitempty"` + // For EVM chains, we do not need this value and it is kept in the node's + // keystore. For starknet, because the wallet address needs to be deployed + // using this value and this pub key needs to be passed into the starknet + // relayer, we request the node to send this directly to CLO. + AccountAddressPublicKey *string `protobuf:"bytes,7,opt,name=account_address_public_key,json=accountAddressPublicKey,proto3,oneof" json:"account_address_public_key,omitempty"` } func (x *ChainConfig) Reset() { @@ -1912,53 +1914,52 @@ var file_pkg_noderpc_proto_feeds_manager_proto_rawDesc = []byte{ 0x5f, 0x4d, 0x4f, 0x4e, 0x49, 0x54, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x4a, 0x4f, 0x42, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4f, 0x43, 0x52, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x4a, 0x4f, 0x42, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4f, 0x43, 0x52, 0x32, 0x10, 0x03, 0x2a, - 0x82, 0x01, 0x0a, 0x09, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, - 0x16, 0x43, 0x48, 0x41, 0x49, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, - 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x48, 0x41, - 0x49, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x56, 0x4d, 0x10, 0x01, 0x12, 0x15, 0x0a, - 0x11, 0x43, 0x48, 0x41, 0x49, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x4f, 0x4c, 0x41, - 0x4e, 0x41, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x48, 0x41, 0x49, 0x4e, 0x5f, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x5a, 0x4b, 0x53, 0x59, 0x4e, 0x43, 0x10, 0x03, 0x12, 0x17, 0x0a, 0x13, 0x43, - 0x48, 0x41, 0x49, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x4b, 0x4e, - 0x45, 0x54, 0x10, 0x04, 0x32, 0xd8, 0x02, 0x0a, 0x0c, 0x46, 0x65, 0x65, 0x64, 0x73, 0x4d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x72, 0x12, 0x40, 0x0a, 0x0b, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, - 0x64, 0x4a, 0x6f, 0x62, 0x12, 0x17, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x41, 0x70, 0x70, 0x72, 0x6f, - 0x76, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, - 0x63, 0x66, 0x6d, 0x2e, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x6c, 0x74, - 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x17, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x48, 0x65, 0x61, - 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x18, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, - 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x0a, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x16, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x17, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0b, 0x52, 0x65, 0x6a, 0x65, - 0x63, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x12, 0x17, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x52, 0x65, - 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x18, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4a, - 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x0c, 0x43, 0x61, - 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x12, 0x18, 0x2e, 0x63, 0x66, 0x6d, - 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, - 0x6c, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, - 0xc4, 0x01, 0x0a, 0x0b, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, - 0x3d, 0x0a, 0x0a, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x16, 0x2e, + 0x6b, 0x0a, 0x09, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x16, + 0x43, 0x48, 0x41, 0x49, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, + 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x48, 0x41, 0x49, + 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x56, 0x4d, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, + 0x43, 0x48, 0x41, 0x49, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x4f, 0x4c, 0x41, 0x4e, + 0x41, 0x10, 0x02, 0x12, 0x17, 0x0a, 0x13, 0x43, 0x48, 0x41, 0x49, 0x4e, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x4b, 0x4e, 0x45, 0x54, 0x10, 0x03, 0x32, 0xd8, 0x02, 0x0a, + 0x0c, 0x46, 0x65, 0x65, 0x64, 0x73, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x12, 0x40, 0x0a, + 0x0b, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x12, 0x17, 0x2e, 0x63, + 0x66, 0x6d, 0x2e, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x41, 0x70, 0x70, 0x72, + 0x6f, 0x76, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x40, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x17, + 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x48, 0x65, + 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x3d, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x12, + 0x16, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x40, 0x0a, 0x0b, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x12, + 0x17, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4a, 0x6f, + 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x52, + 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x43, 0x0a, 0x0c, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x65, 0x64, 0x4a, + 0x6f, 0x62, 0x12, 0x18, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6c, + 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x63, + 0x66, 0x6d, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xc4, 0x01, 0x0a, 0x0b, 0x4e, 0x6f, 0x64, 0x65, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3d, 0x0a, 0x0a, 0x50, 0x72, 0x6f, 0x70, 0x6f, + 0x73, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x16, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x50, 0x72, 0x6f, 0x70, + 0x6f, 0x73, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x50, 0x72, 0x6f, 0x70, - 0x6f, 0x73, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, - 0x0a, 0x09, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x15, 0x2e, 0x63, 0x66, - 0x6d, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4a, - 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x09, 0x52, 0x65, - 0x76, 0x6f, 0x6b, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x15, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x52, 0x65, - 0x76, 0x6f, 0x6b, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, - 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x6d, 0x61, 0x72, 0x74, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, - 0x63, 0x74, 0x6b, 0x69, 0x74, 0x2f, 0x66, 0x65, 0x65, 0x64, 0x73, 0x2d, 0x6d, 0x61, 0x6e, 0x61, - 0x67, 0x65, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x72, 0x70, 0x63, 0x2f, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x09, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x4a, 0x6f, 0x62, 0x12, 0x15, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x63, 0x66, 0x6d, + 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x09, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x4a, 0x6f, 0x62, 0x12, + 0x15, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x4a, 0x6f, 0x62, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x63, 0x66, 0x6d, 0x2e, 0x52, 0x65, 0x76, + 0x6f, 0x6b, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3d, + 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x6d, 0x61, + 0x72, 0x74, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x6b, 0x69, 0x74, 0x2f, 0x66, 0x65, + 0x65, 0x64, 0x73, 0x2d, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, + 0x6e, 0x6f, 0x64, 0x65, 0x72, 0x70, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/core/services/feeds/proto/feeds_manager_wsrpc.pb.go b/core/services/feeds/proto/feeds_manager_wsrpc.pb.go index a002257e5c..85476b1188 100644 --- a/core/services/feeds/proto/feeds_manager_wsrpc.pb.go +++ b/core/services/feeds/proto/feeds_manager_wsrpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-wsrpc. DO NOT EDIT. // versions: // - protoc-gen-go-wsrpc v0.0.1 -// - protoc v3.21.7 +// - protoc v4.25.3 package proto diff --git a/core/services/feeds/service.go b/core/services/feeds/service.go index 6dd855787f..61e69809b3 100644 --- a/core/services/feeds/service.go +++ b/core/services/feeds/service.go @@ -108,6 +108,7 @@ type Service interface { RejectSpec(ctx context.Context, id int64) error UpdateSpecDefinition(ctx context.Context, id int64, spec string) error + // Unsafe_SetConnectionsManager Only for testing Unsafe_SetConnectionsManager(ConnectionsManager) } @@ -208,7 +209,7 @@ func (s *service) RegisterManager(ctx context.Context, params RegisterManagerPar var txerr error id, txerr = tx.CreateManager(ctx, &mgr) - if err != nil { + if txerr != nil { return txerr } @@ -218,6 +219,9 @@ func (s *service) RegisterManager(ctx context.Context, params RegisterManagerPar return nil }) + if err != nil { + return 0, err + } privkey, err := s.getCSAPrivateKey() if err != nil { diff --git a/core/services/feeds/service_test.go b/core/services/feeds/service_test.go index 8c2f60bd63..c7ef46f071 100644 --- a/core/services/feeds/service_test.go +++ b/core/services/feeds/service_test.go @@ -265,6 +265,52 @@ func Test_Service_RegisterManager(t *testing.T) { assert.Equal(t, actual, id) } +func Test_Service_RegisterManager_InvalidCreateManager(t *testing.T) { + t.Parallel() + + var ( + id = int64(1) + pubKeyHex = "0f17c3bf72de8beef6e2d17a14c0a972f5d7e0e66e70722373f12b88382d40f9" + ) + + var pubKey crypto.PublicKey + _, err := hex.Decode([]byte(pubKeyHex), pubKey) + require.NoError(t, err) + + var ( + mgr = feeds.FeedsManager{ + Name: "FMS", + URI: "localhost:8080", + PublicKey: pubKey, + } + params = feeds.RegisterManagerParams{ + Name: "FMS", + URI: "localhost:8080", + PublicKey: pubKey, + } + ) + + svc := setupTestService(t) + + svc.orm.On("CountManagers", mock.Anything).Return(int64(0), nil) + svc.orm.On("CreateManager", mock.Anything, &mgr, mock.Anything). + Return(id, errors.New("orm error")) + // ListManagers runs in a goroutine so it might be called. + svc.orm.On("ListManagers", testutils.Context(t)).Return([]feeds.FeedsManager{mgr}, nil).Maybe() + + transactCall := svc.orm.On("Transact", mock.Anything, mock.Anything) + transactCall.Run(func(args mock.Arguments) { + fn := args[1].(func(orm feeds.ORM) error) + transactCall.ReturnArguments = mock.Arguments{fn(svc.orm)} + }) + _, err = svc.RegisterManager(testutils.Context(t), params) + // We need to stop the service because the manager will attempt to make a + // connection + svc.Close() + require.Error(t, err) + assert.Equal(t, "orm error", err.Error()) +} + func Test_Service_ListManagers(t *testing.T) { t.Parallel() ctx := testutils.Context(t) diff --git a/core/services/fluxmonitorv2/deviation_checker.go b/core/services/fluxmonitorv2/deviation_checker.go index 51e85de371..9dc399b09f 100644 --- a/core/services/fluxmonitorv2/deviation_checker.go +++ b/core/services/fluxmonitorv2/deviation_checker.go @@ -3,7 +3,7 @@ package fluxmonitorv2 import ( "github.com/shopspring/decimal" - "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink-common/pkg/logger" ) // DeviationThresholds carries parameters used by the threshold-trigger logic @@ -26,7 +26,7 @@ func NewDeviationChecker(rel, abs float64, lggr logger.Logger) *DeviationChecker Rel: rel, Abs: abs, }, - lggr: lggr.Named("DeviationChecker").With("threshold", rel, "absoluteThreshold", abs), + lggr: logger.Sugared(lggr).Named("DeviationChecker").With("threshold", rel, "absoluteThreshold", abs), } } diff --git a/core/services/fluxmonitorv2/flux_monitor.go b/core/services/fluxmonitorv2/flux_monitor.go index 9175feb1a6..b8154ab679 100644 --- a/core/services/fluxmonitorv2/flux_monitor.go +++ b/core/services/fluxmonitorv2/flux_monitor.go @@ -13,6 +13,7 @@ import ( "github.com/pkg/errors" "github.com/shopspring/decimal" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" @@ -22,7 +23,6 @@ import ( evmutils "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/flags_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/flux_aggregator_wrapper" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/recovery" "github.com/smartcontractkit/chainlink/v2/core/services/fluxmonitorv2/promfm" "github.com/smartcontractkit/chainlink/v2/core/services/job" @@ -56,7 +56,10 @@ const DefaultHibernationPollPeriod = 24 * time.Hour // FluxMonitor polls external price adapters via HTTP to check for price swings. type FluxMonitor struct { - services.StateMachine + services.Service + eng *services.Engine + logger logger.SugaredLogger + contractAddress common.Address oracleAddress common.Address jobSpec job.Job @@ -77,13 +80,8 @@ type FluxMonitor struct { logBroadcaster log.Broadcaster chainID *big.Int - logger logger.SugaredLogger - backlog *utils.BoundedPriorityQueue[log.Broadcast] chProcessLogs chan struct{} - - chStop services.StopChan - waitOnStop chan struct{} } // NewFluxMonitor returns a new instance of PollingDeviationChecker. @@ -105,7 +103,7 @@ func NewFluxMonitor( flags Flags, fluxAggregator flux_aggregator_wrapper.FluxAggregatorInterface, logBroadcaster log.Broadcaster, - fmLogger logger.Logger, + lggr logger.Logger, chainID *big.Int, ) (*FluxMonitor, error) { fm := &FluxMonitor{ @@ -126,7 +124,6 @@ func NewFluxMonitor( flags: flags, logBroadcaster: logBroadcaster, fluxAggregator: fluxAggregator, - logger: logger.Sugared(fmLogger), chainID: chainID, backlog: utils.NewBoundedPriorityQueue[log.Broadcast](map[uint]int{ // We want reconnecting nodes to be able to submit to a round @@ -136,9 +133,13 @@ func NewFluxMonitor( PriorityFlagChangedLog: 2, }), chProcessLogs: make(chan struct{}, 1), - chStop: make(services.StopChan), - waitOnStop: make(chan struct{}), } + fm.Service, fm.eng = services.Config{ + Name: "FluxMonitor", + Start: fm.start, + Close: fm.close, + }.NewServiceEngine(lggr) + fm.logger = logger.Sugared(fm.eng) return fm, nil } @@ -220,7 +221,7 @@ func NewFromJobSpec( return nil, err } - fmLogger := lggr.With( + fmLogger := logger.With(lggr, "jobID", jobSpec.ID, "contract", fmSpec.ContractAddress.Hex(), ) @@ -279,14 +280,9 @@ const ( // Start implements the job.Service interface. It begins the CSP consumer in a // single goroutine to poll the price adapters and listen to NewRound events. -func (fm *FluxMonitor) Start(context.Context) error { - return fm.StartOnce("FluxMonitor", func() error { - fm.logger.Debug("Starting Flux Monitor for job") - - go fm.consume() - - return nil - }) +func (fm *FluxMonitor) start(context.Context) error { + fm.eng.Go(fm.consume) + return nil } func (fm *FluxMonitor) IsHibernating() bool { @@ -304,16 +300,12 @@ func (fm *FluxMonitor) IsHibernating() bool { return !isFlagLowered } -// Close implements the job.Service interface. It stops this instance from +// close stops this instance from // polling, cleaning up resources. -func (fm *FluxMonitor) Close() error { - return fm.StopOnce("FluxMonitor", func() error { - fm.pollManager.Stop() - close(fm.chStop) - <-fm.waitOnStop +func (fm *FluxMonitor) close() error { + fm.pollManager.Stop() - return nil - }) + return nil } // JobID implements the listener.Listener interface. @@ -354,10 +346,8 @@ func (fm *FluxMonitor) HandleLog(ctx context.Context, broadcast log.Broadcast) { } } -func (fm *FluxMonitor) consume() { - defer close(fm.waitOnStop) - - if err := fm.SetOracleAddress(); err != nil { +func (fm *FluxMonitor) consume(ctx context.Context) { + if err := fm.SetOracleAddress(ctx); err != nil { fm.logger.Warnw( "unable to set oracle address, this flux monitor job may not work correctly", "err", err, @@ -398,46 +388,46 @@ func (fm *FluxMonitor) consume() { for { select { - case <-fm.chStop: + case <-ctx.Done(): return case <-fm.chProcessLogs: - recovery.WrapRecover(fm.logger, fm.processLogs) + recovery.WrapRecover(fm.logger, func() { fm.processLogs(ctx) }) case at := <-fm.pollManager.PollTickerTicks(): tickLogger.Debugf("Poll ticker fired on %v", formatTime(at)) recovery.WrapRecover(fm.logger, func() { - fm.pollIfEligible(PollRequestTypePoll, fm.deviationChecker, nil) + fm.pollIfEligible(ctx, PollRequestTypePoll, fm.deviationChecker, nil) }) case at := <-fm.pollManager.IdleTimerTicks(): tickLogger.Debugf("Idle timer fired on %v", formatTime(at)) recovery.WrapRecover(fm.logger, func() { - fm.pollIfEligible(PollRequestTypeIdle, NewZeroDeviationChecker(fm.logger), nil) + fm.pollIfEligible(ctx, PollRequestTypeIdle, NewZeroDeviationChecker(fm.logger), nil) }) case at := <-fm.pollManager.RoundTimerTicks(): tickLogger.Debugf("Round timer fired on %v", formatTime(at)) recovery.WrapRecover(fm.logger, func() { - fm.pollIfEligible(PollRequestTypeRound, fm.deviationChecker, nil) + fm.pollIfEligible(ctx, PollRequestTypeRound, fm.deviationChecker, nil) }) case at := <-fm.pollManager.HibernationTimerTicks(): tickLogger.Debugf("Hibernation timer fired on %v", formatTime(at)) recovery.WrapRecover(fm.logger, func() { - fm.pollIfEligible(PollRequestTypeHibernation, NewZeroDeviationChecker(fm.logger), nil) + fm.pollIfEligible(ctx, PollRequestTypeHibernation, NewZeroDeviationChecker(fm.logger), nil) }) case at := <-fm.pollManager.RetryTickerTicks(): tickLogger.Debugf("Retry ticker fired on %v", formatTime(at)) recovery.WrapRecover(fm.logger, func() { - fm.pollIfEligible(PollRequestTypeRetry, NewZeroDeviationChecker(fm.logger), nil) + fm.pollIfEligible(ctx, PollRequestTypeRetry, NewZeroDeviationChecker(fm.logger), nil) }) case at := <-fm.pollManager.DrumbeatTicks(): tickLogger.Debugf("Drumbeat ticker fired on %v", formatTime(at)) recovery.WrapRecover(fm.logger, func() { - fm.pollIfEligible(PollRequestTypeDrumbeat, NewZeroDeviationChecker(fm.logger), nil) + fm.pollIfEligible(ctx, PollRequestTypeDrumbeat, NewZeroDeviationChecker(fm.logger), nil) }) case request := <-fm.pollManager.Poll(): @@ -446,7 +436,7 @@ func (fm *FluxMonitor) consume() { break default: recovery.WrapRecover(fm.logger, func() { - fm.pollIfEligible(request.Type, fm.deviationChecker, nil) + fm.pollIfEligible(ctx, request.Type, fm.deviationChecker, nil) }) } } @@ -460,11 +450,7 @@ func formatTime(at time.Time) string { // SetOracleAddress sets the oracle address which matches the node's keys. // If none match, it uses the first available key -func (fm *FluxMonitor) SetOracleAddress() error { - // fm on deprecation path, using dangling context - ctx, cancel := fm.chStop.NewCtx() - defer cancel() - +func (fm *FluxMonitor) SetOracleAddress(ctx context.Context) error { oracleAddrs, err := fm.fluxAggregator.GetOracles(nil) if err != nil { fm.logger.Error("failed to get list of oracles from FluxAggregator contract") @@ -502,10 +488,7 @@ func (fm *FluxMonitor) SetOracleAddress() error { return errors.New("No keys found") } -func (fm *FluxMonitor) processLogs() { - ctx, cancel := fm.chStop.NewCtx() - defer cancel() - +func (fm *FluxMonitor) processLogs(ctx context.Context) { for ctx.Err() == nil && !fm.backlog.Empty() { broadcast := fm.backlog.Take() fm.processBroadcast(ctx, broadcast) @@ -529,7 +512,7 @@ func (fm *FluxMonitor) processBroadcast(ctx context.Context, broadcast log.Broad decodedLog := broadcast.DecodedLog() switch log := decodedLog.(type) { case *flux_aggregator_wrapper.FluxAggregatorNewRound: - fm.respondToNewRoundLog(*log, broadcast) + fm.respondToNewRoundLog(ctx, *log, broadcast) case *flux_aggregator_wrapper.FluxAggregatorAnswerUpdated: fm.respondToAnswerUpdatedLog(*log) fm.markLogAsConsumed(ctx, broadcast, decodedLog, started) @@ -540,7 +523,7 @@ func (fm *FluxMonitor) processBroadcast(ctx context.Context, broadcast log.Broad // Only reactivate if it is hibernating if fm.pollManager.isHibernating.Load() { fm.pollManager.Awaken(fm.initialRoundState()) - fm.pollIfEligible(PollRequestTypeAwaken, NewZeroDeviationChecker(fm.logger), broadcast) + fm.pollIfEligible(ctx, PollRequestTypeAwaken, NewZeroDeviationChecker(fm.logger), broadcast) } default: fm.logger.Errorf("unknown log %v of type %T", log, log) @@ -589,10 +572,8 @@ func (fm *FluxMonitor) respondToAnswerUpdatedLog(log flux_aggregator_wrapper.Flu // The NewRound log tells us that an oracle has initiated a new round. This tells us that we // need to poll and submit an answer to the contract regardless of the deviation. -func (fm *FluxMonitor) respondToNewRoundLog(log flux_aggregator_wrapper.FluxAggregatorNewRound, lb log.Broadcast) { +func (fm *FluxMonitor) respondToNewRoundLog(ctx context.Context, log flux_aggregator_wrapper.FluxAggregatorNewRound, lb log.Broadcast) { started := time.Now() - ctx, cancel := fm.chStop.NewCtx() - defer cancel() newRoundLogger := fm.logger.With( "round", log.RoundId, @@ -819,10 +800,8 @@ func (fm *FluxMonitor) checkEligibilityAndAggregatorFunding(roundState flux_aggr return nil } -func (fm *FluxMonitor) pollIfEligible(pollReq PollRequestType, deviationChecker *DeviationChecker, broadcast log.Broadcast) { +func (fm *FluxMonitor) pollIfEligible(ctx context.Context, pollReq PollRequestType, deviationChecker *DeviationChecker, broadcast log.Broadcast) { started := time.Now() - ctx, cancel := fm.chStop.NewCtx() - defer cancel() l := fm.logger.With( "threshold", deviationChecker.Thresholds.Rel, diff --git a/core/services/fluxmonitorv2/flux_monitor_test.go b/core/services/fluxmonitorv2/flux_monitor_test.go index 87ed4e3053..098b1f4a47 100644 --- a/core/services/fluxmonitorv2/flux_monitor_test.go +++ b/core/services/fluxmonitorv2/flux_monitor_test.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/google/uuid" + "github.com/jmoiron/sqlx" "github.com/onsi/gomega" "github.com/pkg/errors" "github.com/shopspring/decimal" @@ -18,11 +19,11 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" - "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" logmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" @@ -491,7 +492,7 @@ func TestFluxMonitor_PollIfEligible(t *testing.T) { oracles := []common.Address{nodeAddr, testutils.NewAddress()} tm.fluxAggregator.On("GetOracles", nilOpts).Return(oracles, nil) - require.NoError(t, fm.SetOracleAddress()) + require.NoError(t, fm.SetOracleAddress(tests.Context(t))) fm.ExportedPollIfEligible(thresholds.rel, thresholds.abs) }) } @@ -526,7 +527,7 @@ func TestFluxMonitor_PollIfEligible_Creates_JobErr(t *testing.T) { Once() tm.fluxAggregator.On("GetOracles", nilOpts).Return(oracles, nil) - require.NoError(t, fm.SetOracleAddress()) + require.NoError(t, fm.SetOracleAddress(tests.Context(t))) fm.ExportedPollIfEligible(1, 1) } @@ -1171,7 +1172,7 @@ func TestFluxMonitor_RoundTimeoutCausesPoll_timesOutAtZero(t *testing.T) { tm.fluxAggregator.On("Address").Return(common.Address{}) tm.fluxAggregator.On("GetOracles", nilOpts).Return(oracles, nil) - require.NoError(t, fm.SetOracleAddress()) + require.NoError(t, fm.SetOracleAddress(tests.Context(t))) fm.ExportedRoundState(t) servicetest.Run(t, fm) @@ -1506,7 +1507,7 @@ func TestFluxMonitor_DoesNotDoubleSubmit(t *testing.T) { Return(nil) tm.fluxAggregator.On("GetOracles", nilOpts).Return(oracles, nil) - require.NoError(t, fm.SetOracleAddress()) + require.NoError(t, fm.SetOracleAddress(tests.Context(t))) tm.fluxAggregator.On("LatestRoundData", nilOpts).Return(flux_aggregator_wrapper.LatestRoundData{ Answer: big.NewInt(10), @@ -1635,7 +1636,7 @@ func TestFluxMonitor_DoesNotDoubleSubmit(t *testing.T) { Once() tm.fluxAggregator.On("GetOracles", nilOpts).Return(oracles, nil) - require.NoError(t, fm.SetOracleAddress()) + require.NoError(t, fm.SetOracleAddress(tests.Context(t))) fm.ExportedPollIfEligible(0, 0) // Now fire off the NewRound log and ensure it does not respond this time @@ -1732,7 +1733,7 @@ func TestFluxMonitor_DoesNotDoubleSubmit(t *testing.T) { Once() tm.fluxAggregator.On("GetOracles", nilOpts).Return(oracles, nil) - require.NoError(t, fm.SetOracleAddress()) + require.NoError(t, fm.SetOracleAddress(tests.Context(t))) fm.ExportedPollIfEligible(0, 0) // Now fire off the NewRound log and ensure it does not respond this time diff --git a/core/services/fluxmonitorv2/helpers_test.go b/core/services/fluxmonitorv2/helpers_test.go index d321ddc35c..80db82351c 100644 --- a/core/services/fluxmonitorv2/helpers_test.go +++ b/core/services/fluxmonitorv2/helpers_test.go @@ -19,11 +19,15 @@ func (fm *FluxMonitor) Format(f fmt.State, verb rune) { } func (fm *FluxMonitor) ExportedPollIfEligible(threshold, absoluteThreshold float64) { - fm.pollIfEligible(PollRequestTypePoll, NewDeviationChecker(threshold, absoluteThreshold, fm.logger), nil) + ctx, cancel := fm.eng.NewCtx() + defer cancel() + fm.pollIfEligible(ctx, PollRequestTypePoll, NewDeviationChecker(threshold, absoluteThreshold, fm.logger), nil) } func (fm *FluxMonitor) ExportedProcessLogs() { - fm.processLogs() + ctx, cancel := fm.eng.NewCtx() + defer cancel() + fm.processLogs(ctx) } func (fm *FluxMonitor) ExportedBacklog() *utils.BoundedPriorityQueue[log.Broadcast] { @@ -36,7 +40,9 @@ func (fm *FluxMonitor) ExportedRoundState(t *testing.T) { } func (fm *FluxMonitor) ExportedRespondToNewRoundLog(log *flux_aggregator_wrapper.FluxAggregatorNewRound, broadcast log.Broadcast) { - fm.respondToNewRoundLog(*log, broadcast) + ctx, cancel := fm.eng.NewCtx() + defer cancel() + fm.respondToNewRoundLog(ctx, *log, broadcast) } func (fm *FluxMonitor) ExportedRespondToFlagsRaisedLog() { diff --git a/core/services/fluxmonitorv2/integrations_test.go b/core/services/fluxmonitorv2/integrations_test.go index 908d9fb73d..40bdf71743 100644 --- a/core/services/fluxmonitorv2/integrations_test.go +++ b/core/services/fluxmonitorv2/integrations_test.go @@ -26,6 +26,7 @@ import ( commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" + "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" diff --git a/core/services/fluxmonitorv2/poll_manager.go b/core/services/fluxmonitorv2/poll_manager.go index 78b99aec4d..aca6c75a31 100644 --- a/core/services/fluxmonitorv2/poll_manager.go +++ b/core/services/fluxmonitorv2/poll_manager.go @@ -5,8 +5,8 @@ import ( "sync/atomic" "time" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/flux_aggregator_wrapper" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -64,7 +64,7 @@ type PollManager struct { } // NewPollManager initializes a new PollManager -func NewPollManager(cfg PollManagerConfig, logger logger.Logger) (*PollManager, error) { +func NewPollManager(cfg PollManagerConfig, lggr logger.Logger) (*PollManager, error) { minBackoffDuration := cfg.MinRetryBackoffDuration if cfg.IdleTimerPeriod < minBackoffDuration { minBackoffDuration = cfg.IdleTimerPeriod @@ -82,7 +82,7 @@ func NewPollManager(cfg PollManagerConfig, logger logger.Logger) (*PollManager, p := &PollManager{ cfg: cfg, - logger: logger.Named("PollManager"), + logger: logger.Named(lggr, "PollManager"), hibernationTimer: utils.NewResettableTimer(), pollTicker: utils.NewPausableTicker(cfg.PollTickerInterval), @@ -277,7 +277,7 @@ func (pm *PollManager) startIdleTimer(roundStartedAtUTC uint64) { deadline := startedAt.Add(pm.cfg.IdleTimerPeriod) deadlineDuration := time.Until(deadline) - log := pm.logger.With( + log := logger.With(pm.logger, "pollFrequency", pm.cfg.PollTickerInterval, "idleDuration", pm.cfg.IdleTimerPeriod, "startedAt", roundStartedAtUTC, @@ -300,7 +300,7 @@ func (pm *PollManager) startIdleTimer(roundStartedAtUTC uint64) { // startRoundTimer starts the round timer func (pm *PollManager) startRoundTimer(roundTimesOutAt uint64) { - log := pm.logger.With( + log := logger.With(pm.logger, "pollFrequency", pm.cfg.PollTickerInterval, "idleDuration", pm.cfg.IdleTimerPeriod, "timesOutAt", roundTimesOutAt, diff --git a/core/services/headreporter/head_reporter.go b/core/services/headreporter/head_reporter.go new file mode 100644 index 0000000000..94de8ae2be --- /dev/null +++ b/core/services/headreporter/head_reporter.go @@ -0,0 +1,111 @@ +package headreporter + +import ( + "context" + "sync" + "time" + + "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" + "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" + + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +type ( + HeadReporter interface { + ReportNewHead(ctx context.Context, head *evmtypes.Head) error + ReportPeriodic(ctx context.Context) error + } + + HeadReporterService struct { + services.StateMachine + ds sqlutil.DataSource + lggr logger.Logger + newHeads *mailbox.Mailbox[*evmtypes.Head] + chStop services.StopChan + wgDone sync.WaitGroup + reportPeriod time.Duration + reporters []HeadReporter + unsubscribeFns []func() + } +) + +func NewHeadReporterService(ds sqlutil.DataSource, lggr logger.Logger, reporters ...HeadReporter) *HeadReporterService { + return &HeadReporterService{ + ds: ds, + lggr: lggr.Named("HeadReporter"), + newHeads: mailbox.NewSingle[*evmtypes.Head](), + chStop: make(chan struct{}), + reporters: reporters, + reportPeriod: 15 * time.Second, + } +} + +func (hrd *HeadReporterService) Subscribe(subFn func(types.HeadTrackable) (evmtypes.Head, func())) { + _, unsubscribe := subFn(hrd) + hrd.unsubscribeFns = append(hrd.unsubscribeFns, unsubscribe) +} + +func (hrd *HeadReporterService) Start(context.Context) error { + return hrd.StartOnce(hrd.Name(), func() error { + hrd.wgDone.Add(1) + go hrd.eventLoop() + return nil + }) +} + +func (hrd *HeadReporterService) Close() error { + return hrd.StopOnce(hrd.Name(), func() error { + close(hrd.chStop) + hrd.wgDone.Wait() + return nil + }) +} + +func (hrd *HeadReporterService) Name() string { + return hrd.lggr.Name() +} + +func (hrd *HeadReporterService) HealthReport() map[string]error { + return map[string]error{hrd.Name(): hrd.Healthy()} +} + +func (hrd *HeadReporterService) OnNewLongestChain(ctx context.Context, head *evmtypes.Head) { + hrd.newHeads.Deliver(head) +} + +func (hrd *HeadReporterService) eventLoop() { + hrd.lggr.Debug("Starting event loop") + defer hrd.wgDone.Done() + ctx, cancel := hrd.chStop.NewCtx() + defer cancel() + after := time.After(hrd.reportPeriod) + for { + select { + case <-hrd.newHeads.Notify(): + head, exists := hrd.newHeads.Retrieve() + if !exists { + continue + } + for _, reporter := range hrd.reporters { + err := reporter.ReportNewHead(ctx, head) + if err != nil && ctx.Err() == nil { + hrd.lggr.Errorw("Error reporting new head", "err", err) + } + } + case <-after: + for _, reporter := range hrd.reporters { + err := reporter.ReportPeriodic(ctx) + if err != nil && ctx.Err() == nil { + hrd.lggr.Errorw("Error in periodic report", "err", err) + } + } + after = time.After(hrd.reportPeriod) + case <-hrd.chStop: + return + } + } +} diff --git a/core/services/headreporter/head_reporter_mock.go b/core/services/headreporter/head_reporter_mock.go new file mode 100644 index 0000000000..21978abb86 --- /dev/null +++ b/core/services/headreporter/head_reporter_mock.go @@ -0,0 +1,130 @@ +// Code generated by mockery v2.43.2. DO NOT EDIT. + +package headreporter + +import ( + context "context" + + types "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + mock "github.com/stretchr/testify/mock" +) + +// MockHeadReporter is an autogenerated mock type for the HeadReporter type +type MockHeadReporter struct { + mock.Mock +} + +type MockHeadReporter_Expecter struct { + mock *mock.Mock +} + +func (_m *MockHeadReporter) EXPECT() *MockHeadReporter_Expecter { + return &MockHeadReporter_Expecter{mock: &_m.Mock} +} + +// ReportNewHead provides a mock function with given fields: ctx, head +func (_m *MockHeadReporter) ReportNewHead(ctx context.Context, head *types.Head) error { + ret := _m.Called(ctx, head) + + if len(ret) == 0 { + panic("no return value specified for ReportNewHead") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *types.Head) error); ok { + r0 = rf(ctx, head) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockHeadReporter_ReportNewHead_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ReportNewHead' +type MockHeadReporter_ReportNewHead_Call struct { + *mock.Call +} + +// ReportNewHead is a helper method to define mock.On call +// - ctx context.Context +// - head *types.Head +func (_e *MockHeadReporter_Expecter) ReportNewHead(ctx interface{}, head interface{}) *MockHeadReporter_ReportNewHead_Call { + return &MockHeadReporter_ReportNewHead_Call{Call: _e.mock.On("ReportNewHead", ctx, head)} +} + +func (_c *MockHeadReporter_ReportNewHead_Call) Run(run func(ctx context.Context, head *types.Head)) *MockHeadReporter_ReportNewHead_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*types.Head)) + }) + return _c +} + +func (_c *MockHeadReporter_ReportNewHead_Call) Return(_a0 error) *MockHeadReporter_ReportNewHead_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockHeadReporter_ReportNewHead_Call) RunAndReturn(run func(context.Context, *types.Head) error) *MockHeadReporter_ReportNewHead_Call { + _c.Call.Return(run) + return _c +} + +// ReportPeriodic provides a mock function with given fields: ctx +func (_m *MockHeadReporter) ReportPeriodic(ctx context.Context) error { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for ReportPeriodic") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockHeadReporter_ReportPeriodic_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ReportPeriodic' +type MockHeadReporter_ReportPeriodic_Call struct { + *mock.Call +} + +// ReportPeriodic is a helper method to define mock.On call +// - ctx context.Context +func (_e *MockHeadReporter_Expecter) ReportPeriodic(ctx interface{}) *MockHeadReporter_ReportPeriodic_Call { + return &MockHeadReporter_ReportPeriodic_Call{Call: _e.mock.On("ReportPeriodic", ctx)} +} + +func (_c *MockHeadReporter_ReportPeriodic_Call) Run(run func(ctx context.Context)) *MockHeadReporter_ReportPeriodic_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *MockHeadReporter_ReportPeriodic_Call) Return(_a0 error) *MockHeadReporter_ReportPeriodic_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockHeadReporter_ReportPeriodic_Call) RunAndReturn(run func(context.Context) error) *MockHeadReporter_ReportPeriodic_Call { + _c.Call.Return(run) + return _c +} + +// NewMockHeadReporter creates a new instance of MockHeadReporter. 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 NewMockHeadReporter(t interface { + mock.TestingT + Cleanup(func()) +}) *MockHeadReporter { + mock := &MockHeadReporter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/services/headreporter/head_reporter_test.go b/core/services/headreporter/head_reporter_test.go new file mode 100644 index 0000000000..304dd59a47 --- /dev/null +++ b/core/services/headreporter/head_reporter_test.go @@ -0,0 +1,51 @@ +package headreporter + +import ( + "sync/atomic" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" + + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +func NewHead() evmtypes.Head { + return evmtypes.Head{Number: 42, EVMChainID: ubig.NewI(0)} +} + +func Test_HeadReporterService(t *testing.T) { + t.Run("report everything", func(t *testing.T) { + db := pgtest.NewSqlxDB(t) + + headReporter := NewMockHeadReporter(t) + service := NewHeadReporterService(db, logger.TestLogger(t), headReporter) + service.reportPeriod = time.Second + err := service.Start(testutils.Context(t)) + require.NoError(t, err) + + var reportCalls atomic.Int32 + head := NewHead() + headReporter.On("ReportNewHead", mock.Anything, &head).Run(func(args mock.Arguments) { + reportCalls.Add(1) + }).Return(nil) + headReporter.On("ReportPeriodic", mock.Anything).Run(func(args mock.Arguments) { + reportCalls.Add(1) + }).Return(nil) + service.OnNewLongestChain(testutils.Context(t), &head) + + require.Eventually(t, func() bool { return reportCalls.Load() == 2 }, 5*time.Second, 100*time.Millisecond) + }) + + t.Run("has default report period", func(t *testing.T) { + service := NewHeadReporterService(pgtest.NewSqlxDB(t), logger.TestLogger(t), NewMockHeadReporter(t)) + assert.Equal(t, service.reportPeriod, 15*time.Second) + }) +} diff --git a/core/services/headreporter/helper_test.go b/core/services/headreporter/helper_test.go new file mode 100644 index 0000000000..fa05182a85 --- /dev/null +++ b/core/services/headreporter/helper_test.go @@ -0,0 +1,5 @@ +package headreporter + +func (p *prometheusReporter) SetBackend(b PrometheusBackend) { + p.backend = b +} diff --git a/core/services/headreporter/prometheus_backend_mock.go b/core/services/headreporter/prometheus_backend_mock.go new file mode 100644 index 0000000000..ca83f6c4fb --- /dev/null +++ b/core/services/headreporter/prometheus_backend_mock.go @@ -0,0 +1,204 @@ +// Code generated by mockery v2.43.2. DO NOT EDIT. + +package headreporter + +import ( + big "math/big" + + mock "github.com/stretchr/testify/mock" +) + +// MockPrometheusBackend is an autogenerated mock type for the PrometheusBackend type +type MockPrometheusBackend struct { + mock.Mock +} + +type MockPrometheusBackend_Expecter struct { + mock *mock.Mock +} + +func (_m *MockPrometheusBackend) EXPECT() *MockPrometheusBackend_Expecter { + return &MockPrometheusBackend_Expecter{mock: &_m.Mock} +} + +// SetMaxUnconfirmedAge provides a mock function with given fields: _a0, _a1 +func (_m *MockPrometheusBackend) SetMaxUnconfirmedAge(_a0 *big.Int, _a1 float64) { + _m.Called(_a0, _a1) +} + +// MockPrometheusBackend_SetMaxUnconfirmedAge_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetMaxUnconfirmedAge' +type MockPrometheusBackend_SetMaxUnconfirmedAge_Call struct { + *mock.Call +} + +// SetMaxUnconfirmedAge is a helper method to define mock.On call +// - _a0 *big.Int +// - _a1 float64 +func (_e *MockPrometheusBackend_Expecter) SetMaxUnconfirmedAge(_a0 interface{}, _a1 interface{}) *MockPrometheusBackend_SetMaxUnconfirmedAge_Call { + return &MockPrometheusBackend_SetMaxUnconfirmedAge_Call{Call: _e.mock.On("SetMaxUnconfirmedAge", _a0, _a1)} +} + +func (_c *MockPrometheusBackend_SetMaxUnconfirmedAge_Call) Run(run func(_a0 *big.Int, _a1 float64)) *MockPrometheusBackend_SetMaxUnconfirmedAge_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*big.Int), args[1].(float64)) + }) + return _c +} + +func (_c *MockPrometheusBackend_SetMaxUnconfirmedAge_Call) Return() *MockPrometheusBackend_SetMaxUnconfirmedAge_Call { + _c.Call.Return() + return _c +} + +func (_c *MockPrometheusBackend_SetMaxUnconfirmedAge_Call) RunAndReturn(run func(*big.Int, float64)) *MockPrometheusBackend_SetMaxUnconfirmedAge_Call { + _c.Call.Return(run) + return _c +} + +// SetMaxUnconfirmedBlocks provides a mock function with given fields: _a0, _a1 +func (_m *MockPrometheusBackend) SetMaxUnconfirmedBlocks(_a0 *big.Int, _a1 int64) { + _m.Called(_a0, _a1) +} + +// MockPrometheusBackend_SetMaxUnconfirmedBlocks_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetMaxUnconfirmedBlocks' +type MockPrometheusBackend_SetMaxUnconfirmedBlocks_Call struct { + *mock.Call +} + +// SetMaxUnconfirmedBlocks is a helper method to define mock.On call +// - _a0 *big.Int +// - _a1 int64 +func (_e *MockPrometheusBackend_Expecter) SetMaxUnconfirmedBlocks(_a0 interface{}, _a1 interface{}) *MockPrometheusBackend_SetMaxUnconfirmedBlocks_Call { + return &MockPrometheusBackend_SetMaxUnconfirmedBlocks_Call{Call: _e.mock.On("SetMaxUnconfirmedBlocks", _a0, _a1)} +} + +func (_c *MockPrometheusBackend_SetMaxUnconfirmedBlocks_Call) Run(run func(_a0 *big.Int, _a1 int64)) *MockPrometheusBackend_SetMaxUnconfirmedBlocks_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*big.Int), args[1].(int64)) + }) + return _c +} + +func (_c *MockPrometheusBackend_SetMaxUnconfirmedBlocks_Call) Return() *MockPrometheusBackend_SetMaxUnconfirmedBlocks_Call { + _c.Call.Return() + return _c +} + +func (_c *MockPrometheusBackend_SetMaxUnconfirmedBlocks_Call) RunAndReturn(run func(*big.Int, int64)) *MockPrometheusBackend_SetMaxUnconfirmedBlocks_Call { + _c.Call.Return(run) + return _c +} + +// SetPipelineRunsQueued provides a mock function with given fields: n +func (_m *MockPrometheusBackend) SetPipelineRunsQueued(n int) { + _m.Called(n) +} + +// MockPrometheusBackend_SetPipelineRunsQueued_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPipelineRunsQueued' +type MockPrometheusBackend_SetPipelineRunsQueued_Call struct { + *mock.Call +} + +// SetPipelineRunsQueued is a helper method to define mock.On call +// - n int +func (_e *MockPrometheusBackend_Expecter) SetPipelineRunsQueued(n interface{}) *MockPrometheusBackend_SetPipelineRunsQueued_Call { + return &MockPrometheusBackend_SetPipelineRunsQueued_Call{Call: _e.mock.On("SetPipelineRunsQueued", n)} +} + +func (_c *MockPrometheusBackend_SetPipelineRunsQueued_Call) Run(run func(n int)) *MockPrometheusBackend_SetPipelineRunsQueued_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(int)) + }) + return _c +} + +func (_c *MockPrometheusBackend_SetPipelineRunsQueued_Call) Return() *MockPrometheusBackend_SetPipelineRunsQueued_Call { + _c.Call.Return() + return _c +} + +func (_c *MockPrometheusBackend_SetPipelineRunsQueued_Call) RunAndReturn(run func(int)) *MockPrometheusBackend_SetPipelineRunsQueued_Call { + _c.Call.Return(run) + return _c +} + +// SetPipelineTaskRunsQueued provides a mock function with given fields: n +func (_m *MockPrometheusBackend) SetPipelineTaskRunsQueued(n int) { + _m.Called(n) +} + +// MockPrometheusBackend_SetPipelineTaskRunsQueued_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetPipelineTaskRunsQueued' +type MockPrometheusBackend_SetPipelineTaskRunsQueued_Call struct { + *mock.Call +} + +// SetPipelineTaskRunsQueued is a helper method to define mock.On call +// - n int +func (_e *MockPrometheusBackend_Expecter) SetPipelineTaskRunsQueued(n interface{}) *MockPrometheusBackend_SetPipelineTaskRunsQueued_Call { + return &MockPrometheusBackend_SetPipelineTaskRunsQueued_Call{Call: _e.mock.On("SetPipelineTaskRunsQueued", n)} +} + +func (_c *MockPrometheusBackend_SetPipelineTaskRunsQueued_Call) Run(run func(n int)) *MockPrometheusBackend_SetPipelineTaskRunsQueued_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(int)) + }) + return _c +} + +func (_c *MockPrometheusBackend_SetPipelineTaskRunsQueued_Call) Return() *MockPrometheusBackend_SetPipelineTaskRunsQueued_Call { + _c.Call.Return() + return _c +} + +func (_c *MockPrometheusBackend_SetPipelineTaskRunsQueued_Call) RunAndReturn(run func(int)) *MockPrometheusBackend_SetPipelineTaskRunsQueued_Call { + _c.Call.Return(run) + return _c +} + +// SetUnconfirmedTransactions provides a mock function with given fields: _a0, _a1 +func (_m *MockPrometheusBackend) SetUnconfirmedTransactions(_a0 *big.Int, _a1 int64) { + _m.Called(_a0, _a1) +} + +// MockPrometheusBackend_SetUnconfirmedTransactions_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetUnconfirmedTransactions' +type MockPrometheusBackend_SetUnconfirmedTransactions_Call struct { + *mock.Call +} + +// SetUnconfirmedTransactions is a helper method to define mock.On call +// - _a0 *big.Int +// - _a1 int64 +func (_e *MockPrometheusBackend_Expecter) SetUnconfirmedTransactions(_a0 interface{}, _a1 interface{}) *MockPrometheusBackend_SetUnconfirmedTransactions_Call { + return &MockPrometheusBackend_SetUnconfirmedTransactions_Call{Call: _e.mock.On("SetUnconfirmedTransactions", _a0, _a1)} +} + +func (_c *MockPrometheusBackend_SetUnconfirmedTransactions_Call) Run(run func(_a0 *big.Int, _a1 int64)) *MockPrometheusBackend_SetUnconfirmedTransactions_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*big.Int), args[1].(int64)) + }) + return _c +} + +func (_c *MockPrometheusBackend_SetUnconfirmedTransactions_Call) Return() *MockPrometheusBackend_SetUnconfirmedTransactions_Call { + _c.Call.Return() + return _c +} + +func (_c *MockPrometheusBackend_SetUnconfirmedTransactions_Call) RunAndReturn(run func(*big.Int, int64)) *MockPrometheusBackend_SetUnconfirmedTransactions_Call { + _c.Call.Return(run) + return _c +} + +// NewMockPrometheusBackend creates a new instance of MockPrometheusBackend. 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 NewMockPrometheusBackend(t interface { + mock.TestingT + Cleanup(func()) +}) *MockPrometheusBackend { + mock := &MockPrometheusBackend{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/services/promreporter/prom_reporter.go b/core/services/headreporter/prometheus_reporter.go similarity index 63% rename from core/services/promreporter/prom_reporter.go rename to core/services/headreporter/prometheus_reporter.go index 31d5f1129e..3e39c7aca4 100644 --- a/core/services/promreporter/prom_reporter.go +++ b/core/services/headreporter/prometheus_reporter.go @@ -1,40 +1,28 @@ -package promreporter +package headreporter import ( "context" "fmt" "math/big" - "sync" "time" - "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" - txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" - "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "go.uber.org/multierr" - "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/smartcontractkit/chainlink-common/pkg/utils/mailbox" - + "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" + txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) type ( - promReporter struct { - services.StateMachine - ds sqlutil.DataSource - chains legacyevm.LegacyChainContainer - lggr logger.Logger - backend PrometheusBackend - newHeads *mailbox.Mailbox[*evmtypes.Head] - chStop services.StopChan - wgDone sync.WaitGroup - reportPeriod time.Duration + prometheusReporter struct { + ds sqlutil.DataSource + chains legacyevm.LegacyChainContainer + backend PrometheusBackend } PrometheusBackend interface { @@ -71,103 +59,15 @@ var ( }) ) -func (defaultBackend) SetUnconfirmedTransactions(evmChainID *big.Int, n int64) { - promUnconfirmedTransactions.WithLabelValues(evmChainID.String()).Set(float64(n)) -} - -func (defaultBackend) SetMaxUnconfirmedAge(evmChainID *big.Int, s float64) { - promMaxUnconfirmedAge.WithLabelValues(evmChainID.String()).Set(s) -} - -func (defaultBackend) SetMaxUnconfirmedBlocks(evmChainID *big.Int, n int64) { - promMaxUnconfirmedBlocks.WithLabelValues(evmChainID.String()).Set(float64(n)) -} - -func (defaultBackend) SetPipelineRunsQueued(n int) { - promPipelineTaskRunsQueued.Set(float64(n)) -} - -func (defaultBackend) SetPipelineTaskRunsQueued(n int) { - promPipelineRunsQueued.Set(float64(n)) -} - -func NewPromReporter(ds sqlutil.DataSource, chainContainer legacyevm.LegacyChainContainer, lggr logger.Logger, opts ...interface{}) *promReporter { - var backend PrometheusBackend = defaultBackend{} - period := 15 * time.Second - for _, opt := range opts { - switch v := opt.(type) { - case time.Duration: - period = v - case PrometheusBackend: - backend = v - } - } - - chStop := make(chan struct{}) - return &promReporter{ - ds: ds, - chains: chainContainer, - lggr: lggr.Named("PromReporter"), - backend: backend, - newHeads: mailbox.NewSingle[*evmtypes.Head](), - chStop: chStop, - reportPeriod: period, +func NewPrometheusReporter(ds sqlutil.DataSource, chainContainer legacyevm.LegacyChainContainer) *prometheusReporter { + return &prometheusReporter{ + ds: ds, + chains: chainContainer, + backend: defaultBackend{}, } } -// Start starts PromReporter. -func (pr *promReporter) Start(context.Context) error { - return pr.StartOnce("PromReporter", func() error { - pr.wgDone.Add(1) - go pr.eventLoop() - return nil - }) -} - -func (pr *promReporter) Close() error { - return pr.StopOnce("PromReporter", func() error { - close(pr.chStop) - pr.wgDone.Wait() - return nil - }) -} -func (pr *promReporter) Name() string { - return pr.lggr.Name() -} - -func (pr *promReporter) HealthReport() map[string]error { - return map[string]error{pr.Name(): pr.Healthy()} -} - -func (pr *promReporter) OnNewLongestChain(ctx context.Context, head *evmtypes.Head) { - pr.newHeads.Deliver(head) -} - -func (pr *promReporter) eventLoop() { - pr.lggr.Debug("Starting event loop") - defer pr.wgDone.Done() - ctx, cancel := pr.chStop.NewCtx() - defer cancel() - for { - select { - case <-pr.newHeads.Notify(): - head, exists := pr.newHeads.Retrieve() - if !exists { - continue - } - pr.reportHeadMetrics(ctx, head) - case <-time.After(pr.reportPeriod): - if err := errors.Wrap(pr.reportPipelineRunStats(ctx), "reportPipelineRunStats failed"); err != nil { - pr.lggr.Errorw("Error reporting prometheus metrics", "err", err) - } - - case <-pr.chStop: - return - } - } -} - -func (pr *promReporter) getTxm(evmChainID *big.Int) (txmgr.TxManager, error) { +func (pr *prometheusReporter) getTxm(evmChainID *big.Int) (txmgr.TxManager, error) { chain, err := pr.chains.Get(evmChainID.String()) if err != nil { return nil, fmt.Errorf("failed to get chain: %w", err) @@ -175,20 +75,16 @@ func (pr *promReporter) getTxm(evmChainID *big.Int) (txmgr.TxManager, error) { return chain.TxManager(), nil } -func (pr *promReporter) reportHeadMetrics(ctx context.Context, head *evmtypes.Head) { +func (pr *prometheusReporter) ReportNewHead(ctx context.Context, head *evmtypes.Head) error { evmChainID := head.EVMChainID.ToInt() - err := multierr.Combine( + return multierr.Combine( errors.Wrap(pr.reportPendingEthTxes(ctx, evmChainID), "reportPendingEthTxes failed"), errors.Wrap(pr.reportMaxUnconfirmedAge(ctx, evmChainID), "reportMaxUnconfirmedAge failed"), errors.Wrap(pr.reportMaxUnconfirmedBlocks(ctx, head), "reportMaxUnconfirmedBlocks failed"), ) - - if err != nil && ctx.Err() == nil { - pr.lggr.Errorw("Error reporting prometheus metrics", "err", err) - } } -func (pr *promReporter) reportPendingEthTxes(ctx context.Context, evmChainID *big.Int) (err error) { +func (pr *prometheusReporter) reportPendingEthTxes(ctx context.Context, evmChainID *big.Int) (err error) { txm, err := pr.getTxm(evmChainID) if err != nil { return fmt.Errorf("failed to get txm: %w", err) @@ -202,7 +98,7 @@ func (pr *promReporter) reportPendingEthTxes(ctx context.Context, evmChainID *bi return nil } -func (pr *promReporter) reportMaxUnconfirmedAge(ctx context.Context, evmChainID *big.Int) (err error) { +func (pr *prometheusReporter) reportMaxUnconfirmedAge(ctx context.Context, evmChainID *big.Int) (err error) { txm, err := pr.getTxm(evmChainID) if err != nil { return fmt.Errorf("failed to get txm: %w", err) @@ -221,7 +117,7 @@ func (pr *promReporter) reportMaxUnconfirmedAge(ctx context.Context, evmChainID return nil } -func (pr *promReporter) reportMaxUnconfirmedBlocks(ctx context.Context, head *evmtypes.Head) (err error) { +func (pr *prometheusReporter) reportMaxUnconfirmedBlocks(ctx context.Context, head *evmtypes.Head) (err error) { txm, err := pr.getTxm(head.EVMChainID.ToInt()) if err != nil { return fmt.Errorf("failed to get txm: %w", err) @@ -240,7 +136,11 @@ func (pr *promReporter) reportMaxUnconfirmedBlocks(ctx context.Context, head *ev return nil } -func (pr *promReporter) reportPipelineRunStats(ctx context.Context) (err error) { +func (pr *prometheusReporter) ReportPeriodic(ctx context.Context) error { + return errors.Wrap(pr.reportPipelineRunStats(ctx), "reportPipelineRunStats failed") +} + +func (pr *prometheusReporter) reportPipelineRunStats(ctx context.Context) (err error) { rows, err := pr.ds.QueryContext(ctx, ` SELECT pipeline_run_id FROM pipeline_task_runs WHERE finished_at IS NULL `) @@ -271,3 +171,23 @@ SELECT pipeline_run_id FROM pipeline_task_runs WHERE finished_at IS NULL return nil } + +func (defaultBackend) SetUnconfirmedTransactions(evmChainID *big.Int, n int64) { + promUnconfirmedTransactions.WithLabelValues(evmChainID.String()).Set(float64(n)) +} + +func (defaultBackend) SetMaxUnconfirmedAge(evmChainID *big.Int, s float64) { + promMaxUnconfirmedAge.WithLabelValues(evmChainID.String()).Set(s) +} + +func (defaultBackend) SetMaxUnconfirmedBlocks(evmChainID *big.Int, n int64) { + promMaxUnconfirmedBlocks.WithLabelValues(evmChainID.String()).Set(float64(n)) +} + +func (defaultBackend) SetPipelineRunsQueued(n int) { + promPipelineTaskRunsQueued.Set(float64(n)) +} + +func (defaultBackend) SetPipelineTaskRunsQueued(n int) { + promPipelineRunsQueued.Set(float64(n)) +} diff --git a/core/services/promreporter/prom_reporter_test.go b/core/services/headreporter/prometheus_reporter_test.go similarity index 64% rename from core/services/promreporter/prom_reporter_test.go rename to core/services/headreporter/prometheus_reporter_test.go index b61fa25bdc..d96e617fd7 100644 --- a/core/services/promreporter/prom_reporter_test.go +++ b/core/services/headreporter/prometheus_reporter_test.go @@ -1,8 +1,7 @@ -package promreporter_test +package headreporter_test import ( "math/big" - "sync/atomic" "testing" "time" @@ -10,90 +9,40 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" - evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - "github.com/smartcontractkit/chainlink/v2/core/internal/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/promreporter" + "github.com/smartcontractkit/chainlink/v2/core/services/headreporter" ) -func newHead() evmtypes.Head { - return evmtypes.Head{Number: 42, EVMChainID: ubig.NewI(0)} -} - -func newLegacyChainContainer(t *testing.T, db *sqlx.DB) legacyevm.LegacyChainContainer { - config, dbConfig, evmConfig := txmgr.MakeTestConfigs(t) - keyStore := cltest.NewKeyStore(t, db).Eth() - ethClient := evmtest.NewEthClientMockWithDefaultChain(t) - estimator, err := gas.NewEstimator(logger.TestLogger(t), ethClient, config, evmConfig.GasEstimator()) - require.NoError(t, err) - lggr := logger.TestLogger(t) - lpOpts := logpoller.Opts{ - PollPeriod: 100 * time.Millisecond, - FinalityDepth: 2, - BackfillBatchSize: 3, - RpcBatchSize: 2, - KeepFinalizedBlocksDepth: 1000, - } - ht := headtracker.NewSimulatedHeadTracker(ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) - lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr), ethClient, lggr, ht, lpOpts) - - txm, err := txmgr.NewTxm( - db, - evmConfig, - evmConfig.GasEstimator(), - evmConfig.Transactions(), - nil, - dbConfig, - dbConfig.Listener(), - ethClient, - lggr, - lp, - keyStore, - estimator) - require.NoError(t, err) - - cfg := configtest.NewGeneralConfig(t, nil) - return cltest.NewLegacyChainsWithMockChainAndTxManager(t, ethClient, cfg, txm) -} - -func Test_PromReporter_OnNewLongestChain(t *testing.T) { +func Test_PrometheusReporter(t *testing.T) { t.Run("with nothing in the database", func(t *testing.T) { db := pgtest.NewSqlxDB(t) - backend := mocks.NewPrometheusBackend(t) - reporter := promreporter.NewPromReporter(db, newLegacyChainContainer(t, db), logger.TestLogger(t), backend, 10*time.Millisecond) - - var subscribeCalls atomic.Int32 - + backend := headreporter.NewMockPrometheusBackend(t) backend.On("SetUnconfirmedTransactions", big.NewInt(0), int64(0)).Return() backend.On("SetMaxUnconfirmedAge", big.NewInt(0), float64(0)).Return() backend.On("SetMaxUnconfirmedBlocks", big.NewInt(0), int64(0)).Return() - backend.On("SetPipelineTaskRunsQueued", 0).Return() - backend.On("SetPipelineRunsQueued", 0). - Run(func(args mock.Arguments) { - subscribeCalls.Add(1) - }). - Return() - servicetest.Run(t, reporter) + reporter := headreporter.NewPrometheusReporter(db, newLegacyChainContainer(t, db)) + reporter.SetBackend(backend) - head := newHead() - reporter.OnNewLongestChain(testutils.Context(t), &head) + head := headreporter.NewHead() + err := reporter.ReportNewHead(testutils.Context(t), &head) + require.NoError(t, err) - require.Eventually(t, func() bool { return subscribeCalls.Load() >= 1 }, 12*time.Second, 100*time.Millisecond) + backend.On("SetPipelineTaskRunsQueued", 0).Return() + backend.On("SetPipelineRunsQueued", 0).Return() + err = reporter.ReportPeriodic(testutils.Context(t)) + require.NoError(t, err) }) t.Run("with unconfirmed evm.txes", func(t *testing.T) { @@ -102,61 +51,93 @@ func Test_PromReporter_OnNewLongestChain(t *testing.T) { ethKeyStore := cltest.NewKeyStore(t, db).Eth() _, fromAddress := cltest.MustInsertRandomKey(t, ethKeyStore) - var subscribeCalls atomic.Int32 + etx := cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 0, fromAddress) + cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 1, fromAddress) + cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 2, fromAddress) + require.NoError(t, txStore.UpdateTxAttemptBroadcastBeforeBlockNum(testutils.Context(t), etx.ID, 7)) - backend := mocks.NewPrometheusBackend(t) + backend := headreporter.NewMockPrometheusBackend(t) backend.On("SetUnconfirmedTransactions", big.NewInt(0), int64(3)).Return() backend.On("SetMaxUnconfirmedAge", big.NewInt(0), mock.MatchedBy(func(s float64) bool { return s > 0 })).Return() backend.On("SetMaxUnconfirmedBlocks", big.NewInt(0), int64(35)).Return() - backend.On("SetPipelineTaskRunsQueued", 0).Return() - backend.On("SetPipelineRunsQueued", 0). - Run(func(args mock.Arguments) { - subscribeCalls.Add(1) - }). - Return() - reporter := promreporter.NewPromReporter(db, newLegacyChainContainer(t, db), logger.TestLogger(t), backend, 10*time.Millisecond) - servicetest.Run(t, reporter) - etx := cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 0, fromAddress) - cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 1, fromAddress) - cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 2, fromAddress) - require.NoError(t, txStore.UpdateTxAttemptBroadcastBeforeBlockNum(testutils.Context(t), etx.ID, 7)) + reporter := headreporter.NewPrometheusReporter(db, newLegacyChainContainer(t, db)) + reporter.SetBackend(backend) + + head := headreporter.NewHead() + err := reporter.ReportNewHead(testutils.Context(t), &head) + require.NoError(t, err) - head := newHead() - reporter.OnNewLongestChain(testutils.Context(t), &head) + backend.On("SetPipelineTaskRunsQueued", 0).Return() + backend.On("SetPipelineRunsQueued", 0).Return() - require.Eventually(t, func() bool { return subscribeCalls.Load() >= 1 }, 12*time.Second, 100*time.Millisecond) + err = reporter.ReportPeriodic(testutils.Context(t)) + require.NoError(t, err) }) t.Run("with unfinished pipeline task runs", func(t *testing.T) { db := pgtest.NewSqlxDB(t) pgtest.MustExec(t, db, `SET CONSTRAINTS pipeline_task_runs_pipeline_run_id_fkey DEFERRED`) - backend := mocks.NewPrometheusBackend(t) - reporter := promreporter.NewPromReporter(db, newLegacyChainContainer(t, db), logger.TestLogger(t), backend, 10*time.Millisecond) - cltest.MustInsertUnfinishedPipelineTaskRun(t, db, 1) cltest.MustInsertUnfinishedPipelineTaskRun(t, db, 1) cltest.MustInsertUnfinishedPipelineTaskRun(t, db, 2) - var subscribeCalls atomic.Int32 - + backend := headreporter.NewMockPrometheusBackend(t) backend.On("SetUnconfirmedTransactions", big.NewInt(0), int64(0)).Return() backend.On("SetMaxUnconfirmedAge", big.NewInt(0), float64(0)).Return() backend.On("SetMaxUnconfirmedBlocks", big.NewInt(0), int64(0)).Return() - backend.On("SetPipelineTaskRunsQueued", 3).Return() - backend.On("SetPipelineRunsQueued", 2). - Run(func(args mock.Arguments) { - subscribeCalls.Add(1) - }). - Return() - servicetest.Run(t, reporter) - head := newHead() - reporter.OnNewLongestChain(testutils.Context(t), &head) + reporter := headreporter.NewPrometheusReporter(db, newLegacyChainContainer(t, db)) + reporter.SetBackend(backend) + + head := headreporter.NewHead() + err := reporter.ReportNewHead(testutils.Context(t), &head) + require.NoError(t, err) + + backend.On("SetPipelineTaskRunsQueued", 3).Return() + backend.On("SetPipelineRunsQueued", 2).Return() - require.Eventually(t, func() bool { return subscribeCalls.Load() >= 1 }, 12*time.Second, 100*time.Millisecond) + err = reporter.ReportPeriodic(testutils.Context(t)) + require.NoError(t, err) }) } + +func newLegacyChainContainer(t *testing.T, db *sqlx.DB) legacyevm.LegacyChainContainer { + config, dbConfig, evmConfig := txmgr.MakeTestConfigs(t) + keyStore := cltest.NewKeyStore(t, db).Eth() + ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + estimator, err := gas.NewEstimator(logger.TestLogger(t), ethClient, config, evmConfig.GasEstimator()) + require.NoError(t, err) + lggr := logger.TestLogger(t) + lpOpts := logpoller.Opts{ + PollPeriod: 100 * time.Millisecond, + FinalityDepth: 2, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + } + ht := headtracker.NewSimulatedHeadTracker(ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr), ethClient, lggr, ht, lpOpts) + + txm, err := txmgr.NewTxm( + db, + evmConfig, + evmConfig.GasEstimator(), + evmConfig.Transactions(), + nil, + dbConfig, + dbConfig.Listener(), + ethClient, + lggr, + lp, + keyStore, + estimator, + ht) + require.NoError(t, err) + + cfg := configtest.NewGeneralConfig(t, nil) + return cltest.NewLegacyChainsWithMockChainAndTxManager(t, ethClient, cfg, txm) +} diff --git a/core/services/headreporter/telemetry_reporter.go b/core/services/headreporter/telemetry_reporter.go new file mode 100644 index 0000000000..0d93ca59a4 --- /dev/null +++ b/core/services/headreporter/telemetry_reporter.go @@ -0,0 +1,69 @@ +package headreporter + +import ( + "context" + "math/big" + + "github.com/pkg/errors" + + "github.com/smartcontractkit/libocr/commontypes" + "google.golang.org/protobuf/proto" + + evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/synchronization" + "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" + "github.com/smartcontractkit/chainlink/v2/core/services/telemetry" +) + +type telemetryReporter struct { + lggr logger.Logger + endpoints map[uint64]commontypes.MonitoringEndpoint +} + +func NewTelemetryReporter(monitoringEndpointGen telemetry.MonitoringEndpointGenerator, lggr logger.Logger, chainIDs ...*big.Int) HeadReporter { + endpoints := make(map[uint64]commontypes.MonitoringEndpoint) + for _, chainID := range chainIDs { + endpoints[chainID.Uint64()] = monitoringEndpointGen.GenMonitoringEndpoint("EVM", chainID.String(), "", synchronization.HeadReport) + } + return &telemetryReporter{lggr: lggr.Named("TelemetryReporter"), endpoints: endpoints} +} + +func (t *telemetryReporter) ReportNewHead(ctx context.Context, head *evmtypes.Head) error { + monitoringEndpoint := t.endpoints[head.EVMChainID.ToInt().Uint64()] + if monitoringEndpoint == nil { + return errors.Errorf("No monitoring endpoint provided chain_id=%d", head.EVMChainID.Int64()) + } + var finalized *telem.Block + latestFinalizedHead := head.LatestFinalizedHead() + if latestFinalizedHead != nil { + finalized = &telem.Block{ + Timestamp: uint64(latestFinalizedHead.GetTimestamp().UTC().Unix()), + Number: uint64(latestFinalizedHead.BlockNumber()), + Hash: latestFinalizedHead.BlockHash().Hex(), + } + } + request := &telem.HeadReportRequest{ + ChainID: head.EVMChainID.String(), + Latest: &telem.Block{ + Timestamp: uint64(head.Timestamp.UTC().Unix()), + Number: uint64(head.Number), + Hash: head.Hash.Hex(), + }, + Finalized: finalized, + } + bytes, err := proto.Marshal(request) + if err != nil { + return errors.WithMessage(err, "telem.HeadReportRequest marshal error") + } + monitoringEndpoint.SendLog(bytes) + if finalized == nil { + t.lggr.Infow("No finalized block was found", "chainID", head.EVMChainID.Int64(), + "head.number", head.Number, "chainLength", head.ChainLength()) + } + return nil +} + +func (t *telemetryReporter) ReportPeriodic(ctx context.Context) error { + return nil +} diff --git a/core/services/headreporter/telemetry_reporter_test.go b/core/services/headreporter/telemetry_reporter_test.go new file mode 100644 index 0000000000..85bfea5866 --- /dev/null +++ b/core/services/headreporter/telemetry_reporter_test.go @@ -0,0 +1,108 @@ +package headreporter_test + +import ( + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/assert" + "google.golang.org/protobuf/proto" + + mocks2 "github.com/smartcontractkit/chainlink/v2/common/types/mocks" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/headreporter" + "github.com/smartcontractkit/chainlink/v2/core/services/synchronization" + "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" + "github.com/smartcontractkit/chainlink/v2/core/services/telemetry" +) + +func Test_TelemetryReporter_NewHead(t *testing.T) { + head := evmtypes.Head{ + Number: 42, + EVMChainID: ubig.NewI(100), + Hash: common.HexToHash("0x1010"), + Timestamp: time.UnixMilli(1000), + IsFinalized: false, + Parent: &evmtypes.Head{ + Number: 41, + Hash: common.HexToHash("0x1009"), + Timestamp: time.UnixMilli(999), + IsFinalized: true, + }, + } + requestBytes, err := proto.Marshal(&telem.HeadReportRequest{ + ChainID: "100", + Latest: &telem.Block{ + Timestamp: uint64(head.Timestamp.UTC().Unix()), + Number: 42, + Hash: head.Hash.Hex(), + }, + Finalized: &telem.Block{ + Timestamp: uint64(head.Parent.Timestamp.UTC().Unix()), + Number: 41, + Hash: head.Parent.Hash.Hex(), + }, + }) + assert.NoError(t, err) + + monitoringEndpoint := mocks2.NewMonitoringEndpoint(t) + monitoringEndpoint.On("SendLog", requestBytes).Return() + + monitoringEndpointGen := telemetry.NewMockMonitoringEndpointGenerator(t) + monitoringEndpointGen. + On("GenMonitoringEndpoint", "EVM", "100", "", synchronization.HeadReport). + Return(monitoringEndpoint) + reporter := headreporter.NewTelemetryReporter(monitoringEndpointGen, logger.TestLogger(t), big.NewInt(100)) + + err = reporter.ReportNewHead(testutils.Context(t), &head) + assert.NoError(t, err) +} + +func Test_TelemetryReporter_NewHeadMissingFinalized(t *testing.T) { + head := evmtypes.Head{ + Number: 42, + EVMChainID: ubig.NewI(100), + Hash: common.HexToHash("0x1010"), + Timestamp: time.UnixMilli(1000), + IsFinalized: false, + } + requestBytes, err := proto.Marshal(&telem.HeadReportRequest{ + ChainID: "100", + Latest: &telem.Block{ + Timestamp: uint64(head.Timestamp.UTC().Unix()), + Number: 42, + Hash: head.Hash.Hex(), + }, + }) + assert.NoError(t, err) + + monitoringEndpoint := mocks2.NewMonitoringEndpoint(t) + monitoringEndpoint.On("SendLog", requestBytes).Return() + + monitoringEndpointGen := telemetry.NewMockMonitoringEndpointGenerator(t) + monitoringEndpointGen. + On("GenMonitoringEndpoint", "EVM", "100", "", synchronization.HeadReport). + Return(monitoringEndpoint) + reporter := headreporter.NewTelemetryReporter(monitoringEndpointGen, logger.TestLogger(t), big.NewInt(100)) + + err = reporter.ReportNewHead(testutils.Context(t), &head) + assert.NoError(t, err) +} + +func Test_TelemetryReporter_NewHead_MissingEndpoint(t *testing.T) { + monitoringEndpointGen := telemetry.NewMockMonitoringEndpointGenerator(t) + monitoringEndpointGen. + On("GenMonitoringEndpoint", "EVM", "100", "", synchronization.HeadReport). + Return(nil) + + reporter := headreporter.NewTelemetryReporter(monitoringEndpointGen, logger.TestLogger(t), big.NewInt(100)) + + head := evmtypes.Head{Number: 42, EVMChainID: ubig.NewI(100)} + + err := reporter.ReportNewHead(testutils.Context(t), &head) + assert.Errorf(t, err, "No monitoring endpoint provided chain_id=100") +} diff --git a/core/services/keeper/integration_test.go b/core/services/keeper/integration_test.go index 455f5bb506..494e0a1615 100644 --- a/core/services/keeper/integration_test.go +++ b/core/services/keeper/integration_test.go @@ -17,6 +17,7 @@ import ( "github.com/stretchr/testify/require" commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" diff --git a/core/services/keystore/keys/aptoskey/account.go b/core/services/keystore/keys/aptoskey/account.go deleted file mode 100644 index 89f62d3301..0000000000 --- a/core/services/keystore/keys/aptoskey/account.go +++ /dev/null @@ -1,72 +0,0 @@ -package aptoskey - -import ( - "encoding/hex" - "errors" - "fmt" - "strings" -) - -// AccountAddress is a 32 byte address on the Aptos blockchain -// It can represent an Object, an Account, and much more. -// -// AccountAddress is copied from the aptos sdk because: -// 1. There are still breaking changes in sdk and we don't want the dependency. -// 2. AccountAddress is just a wrapper and can be easily extracted out. -// -// https://github.com/aptos-labs/aptos-go-sdk/blob/main/internal/types/account.go -type AccountAddress [32]byte - -// IsSpecial Returns whether the address is a "special" address. Addresses are considered -// special if the first 63 characters of the hex string are zero. In other words, -// an address is special if the first 31 bytes are zero and the last byte is -// smaller than `0b10000` (16). In other words, special is defined as an address -// that matches the following regex: `^0x0{63}[0-9a-f]$`. In short form this means -// the addresses in the range from `0x0` to `0xf` (inclusive) are special. -// For more details see the v1 address standard defined as part of AIP-40: -// https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-40.md -func (aa *AccountAddress) IsSpecial() bool { - for _, b := range aa[:31] { - if b != 0 { - return false - } - } - return aa[31] < 0x10 -} - -// String Returns the canonical string representation of the AccountAddress -func (aa *AccountAddress) String() string { - if aa.IsSpecial() { - return fmt.Sprintf("0x%x", aa[31]) - } - return BytesToHex(aa[:]) -} - -// ParseStringRelaxed parses a string into an AccountAddress -func (aa *AccountAddress) ParseStringRelaxed(x string) error { - x = strings.TrimPrefix(x, "0x") - if len(x) < 1 { - return ErrAddressTooShort - } - if len(x) > 64 { - return ErrAddressTooLong - } - if len(x)%2 != 0 { - x = "0" + x - } - bytes, err := hex.DecodeString(x) - if err != nil { - return err - } - // zero-prefix/right-align what bytes we got - copy((*aa)[32-len(bytes):], bytes) - - return nil -} - -var ErrAddressTooShort = errors.New("AccountAddress too short") -var ErrAddressTooLong = errors.New("AccountAddress too long") - -func BytesToHex(bytes []byte) string { - return "0x" + hex.EncodeToString(bytes) -} diff --git a/core/services/keystore/keys/aptoskey/account_test.go b/core/services/keystore/keys/aptoskey/account_test.go deleted file mode 100644 index b9ed4ea04a..0000000000 --- a/core/services/keystore/keys/aptoskey/account_test.go +++ /dev/null @@ -1,141 +0,0 @@ -package aptoskey - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -// Tests extracted from -// https://github.com/aptos-labs/aptos-go-sdk/blob/5ee5ac308e5881b508c0a5124f5e0b8df27a4d40/internal/types/account_test.go - -func TestStringOutput(t *testing.T) { - inputs := [][]byte{ - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F}, - {0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, - {0x02, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, - {0x00, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, - {0x00, 0x04, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, - {0x00, 0x00, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, - } - expected := []string{ - "0x0", - "0x1", - "0xf", - "0x1234123412341234123412341234123412341234123412340123456789abcdef", - "0x0234123412341234123412341234123412341234123412340123456789abcdef", - "0x0034123412341234123412341234123412341234123412340123456789abcdef", - "0x0004123412341234123412341234123412341234123412340123456789abcdef", - "0x0000123412341234123412341234123412341234123412340123456789abcdef", - } - - for i := 0; i < len(inputs); i++ { - addr := AccountAddress(inputs[i]) - assert.Equal(t, expected[i], addr.String()) - } -} - -func TestAccountAddress_ParseStringRelaxed_Error(t *testing.T) { - var owner AccountAddress - err := owner.ParseStringRelaxed("0x") - assert.Error(t, err) - err = owner.ParseStringRelaxed("0xF1234567812345678123456781234567812345678123456781234567812345678") - assert.Error(t, err) - err = owner.ParseStringRelaxed("NotHex") - assert.Error(t, err) -} - -func TestAccountAddress_String(t *testing.T) { - testCases := []struct { - name string - address AccountAddress - expected string - }{ - { - name: "Special address", - address: AccountAddress{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, - expected: "0x1", - }, - { - name: "Non-special address", - address: AccountAddress{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}, - expected: "0x123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0", - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - assert.Equal(t, tc.expected, tc.address.String()) - }) - } -} - -func TestAccountAddress_IsSpecial(t *testing.T) { - testCases := []struct { - name string - address AccountAddress - expected bool - }{ - { - name: "Special address", - address: AccountAddress{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, - expected: true, - }, - { - name: "Non-special address", - address: AccountAddress{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}, - expected: false, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - assert.Equal(t, tc.expected, tc.address.IsSpecial()) - }) - } -} - -func TestBytesToHex(t *testing.T) { - testCases := []struct { - name string - bytes []byte - expected string - }{ - { - name: "Empty bytes", - bytes: []byte{}, - expected: "0x", - }, - { - name: "Non-empty bytes", - bytes: []byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}, - expected: "0x123456789abcdef0", - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - assert.Equal(t, tc.expected, BytesToHex(tc.bytes)) - }) - } -} - -func TestAccountSpecialString(t *testing.T) { - var aa AccountAddress - aa[31] = 3 - aas := aa.String() - if aas != "0x3" { - t.Errorf("wanted 0x3 got %s", aas) - } - - var aa2 AccountAddress - err := aa2.ParseStringRelaxed("0x3") - if err != nil { - t.Errorf("unexpected err %s", err) - } - if aa2 != aa { - t.Errorf("aa2 != aa") - } -} diff --git a/core/services/keystore/keys/aptoskey/key.go b/core/services/keystore/keys/aptoskey/key.go index ec9b27a359..fa8925454e 100644 --- a/core/services/keystore/keys/aptoskey/key.go +++ b/core/services/keystore/keys/aptoskey/key.go @@ -7,7 +7,7 @@ import ( "fmt" "io" - "github.com/mr-tron/base58" + "golang.org/x/crypto/sha3" ) // Raw represents the Aptos private key @@ -37,6 +37,7 @@ var _ fmt.GoStringer = &Key{} // Key represents Aptos key type Key struct { + // TODO: store initial Account() derivation to support key rotation privkey ed25519.PrivateKey pubKey ed25519.PublicKey } @@ -72,14 +73,20 @@ func (key Key) ID() string { return key.PublicKeyStr() } +// https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-40.md#long +func (key Key) Account() string { + authKey := sha3.Sum256(append([]byte(key.pubKey), 0x00)) + return fmt.Sprintf("%064x", authKey) +} + // GetPublic get Key's public key func (key Key) GetPublic() ed25519.PublicKey { return key.pubKey } -// PublicKeyStr return base58 encoded public key +// PublicKeyStr returns hex encoded public key func (key Key) PublicKeyStr() string { - return base58.Encode(key.pubKey) + return fmt.Sprintf("%064x", key.pubKey) } // Raw returns the seed from private key diff --git a/core/services/keystore/keys/aptoskey/key_test.go b/core/services/keystore/keys/aptoskey/key_test.go new file mode 100644 index 0000000000..277a30eb9f --- /dev/null +++ b/core/services/keystore/keys/aptoskey/key_test.go @@ -0,0 +1,17 @@ +package aptoskey + +import ( + "encoding/hex" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestAptosKey(t *testing.T) { + bytes, err := hex.DecodeString("f0d07ab448018b2754475f9a3b580218b0675a1456aad96ad607c7bbd7d9237b") + require.NoError(t, err) + k := Raw(bytes).Key() + assert.Equal(t, k.PublicKeyStr(), "2acd605efc181e2af8a0b8c0686a5e12578efa1253d15a235fa5e5ad970c4b29") + assert.Equal(t, k.Account(), "69d8b07f5945185873c622ea66873b0e1fb921de7b94d904d3ef9be80770682e") +} diff --git a/core/services/keystore/keys/cosmoskey/key.go b/core/services/keystore/keys/cosmoskey/key.go index 3e516a21ab..b5ea255f23 100644 --- a/core/services/keystore/keys/cosmoskey/key.go +++ b/core/services/keystore/keys/cosmoskey/key.go @@ -59,9 +59,6 @@ func newFrom(reader io.Reader) Key { panic(err) } privKey := secpSigningAlgo.Generate()(rawKey.D.Bytes()) - if err != nil { - panic(err) - } return Key{ d: rawKey.D, diff --git a/core/services/keystore/keys/ocr2key/aptos_keyring.go b/core/services/keystore/keys/ocr2key/aptos_keyring.go index 51e6c3a9c8..cdc5afa792 100644 --- a/core/services/keystore/keys/ocr2key/aptos_keyring.go +++ b/core/services/keystore/keys/ocr2key/aptos_keyring.go @@ -2,12 +2,11 @@ package ocr2key import ( "crypto/ed25519" - "encoding/binary" "io" "github.com/hdevalence/ed25519consensus" "github.com/pkg/errors" - "golang.org/x/crypto/blake2s" + "golang.org/x/crypto/blake2b" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -37,17 +36,15 @@ func (akr *aptosKeyring) PublicKey() ocrtypes.OnchainPublicKey { func (akr *aptosKeyring) reportToSigData(reportCtx ocrtypes.ReportContext, report ocrtypes.Report) ([]byte, error) { rawReportContext := evmutil.RawReportContext(reportCtx) - h, err := blake2s.New256(nil) + h, err := blake2b.New256(nil) if err != nil { return nil, err } - reportLen := make([]byte, 4) - binary.BigEndian.PutUint32(reportLen[0:], uint32(len(report))) - h.Write(reportLen[:]) - h.Write(report) + // blake2b_256(report_context | report) h.Write(rawReportContext[0][:]) h.Write(rawReportContext[1][:]) h.Write(rawReportContext[2][:]) + h.Write(report) return h.Sum(nil), nil } diff --git a/core/services/llo/bm/dummy_transmitter.go b/core/services/llo/bm/dummy_transmitter.go index c349c9e0ee..06fd0915b3 100644 --- a/core/services/llo/bm/dummy_transmitter.go +++ b/core/services/llo/bm/dummy_transmitter.go @@ -2,6 +2,7 @@ package bm import ( "context" + "fmt" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" @@ -10,12 +11,13 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2plus/types" ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink/v2/core/services/llo/evm" + "github.com/smartcontractkit/chainlink-data-streams/llo" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "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 @@ -39,7 +41,7 @@ type transmitter struct { func NewTransmitter(lggr logger.Logger, fromAccount string) Transmitter { return &transmitter{ - lggr.Named("DummyTransmitter"), + logger.Named(lggr, "DummyTransmitter"), fromAccount, } } @@ -60,21 +62,44 @@ func (t *transmitter) Transmit( sigs []types.AttributedOnchainSignature, ) error { lggr := t.lggr - switch report.Info.ReportFormat { - case llotypes.ReportFormatJSON: - r, err := (llo.JSONReportCodec{}).Decode(report.Report) - if err != nil { - lggr.Debugw("Failed to decode JSON report", "err", err) + { + switch report.Info.ReportFormat { + case llotypes.ReportFormatJSON: + r, err := (llo.JSONReportCodec{}).Decode(report.Report) + if err != nil { + lggr.Debugw(fmt.Sprintf("Failed to decode report with type %s", report.Info.ReportFormat), "err", err) + } else if r.SeqNr > 0 { + lggr = logger.With(lggr, + "report.Report.ConfigDigest", r.ConfigDigest, + "report.Report.SeqNr", r.SeqNr, + "report.Report.ChannelID", r.ChannelID, + "report.Report.ValidAfterSeconds", r.ValidAfterSeconds, + "report.Report.ObservationTimestampSeconds", r.ObservationTimestampSeconds, + "report.Report.Values", r.Values, + "report.Report.Specimen", r.Specimen, + ) + } + case llotypes.ReportFormatEVMPremiumLegacy: + r, err := (evm.ReportCodecPremiumLegacy{}).Decode(report.Report) + if err != nil { + lggr.Debugw(fmt.Sprintf("Failed to decode report with type %s", report.Info.ReportFormat), "err", err) + } else if r.ObservationsTimestamp > 0 { + lggr = logger.With(lggr, + "report.Report.FeedId", r.FeedId, + "report.Report.ObservationsTimestamp", r.ObservationsTimestamp, + "report.Report.BenchmarkPrice", r.BenchmarkPrice, + "report.Report.Bid", r.Bid, + "report.Report.Ask", r.Ask, + "report.Report.ValidFromTimestamp", r.ValidFromTimestamp, + "report.Report.ExpiresAt", r.ExpiresAt, + "report.Report.LinkFee", r.LinkFee, + "report.Report.NativeFee", r.NativeFee, + ) + } + default: + err := fmt.Errorf("unhandled report format: %s", report.Info.ReportFormat) + lggr.Debugw(fmt.Sprintf("Failed to decode report with type %s", report.Info.ReportFormat), "err", err) } - lggr = lggr.With( - "report.Report.ConfigDigest", r.ConfigDigest, - "report.Report.SeqNr", r.SeqNr, - "report.Report.ChannelID", r.ChannelID, - "report.Report.ValidAfterSeconds", r.ValidAfterSeconds, - "report.Report.Values", r.Values, - "report.Report.Specimen", r.Specimen, - ) - default: } transmitSuccessCount.Inc() lggr.Infow("Transmit (dummy)", "digest", digest, "seqNr", seqNr, "report.Report", report.Report, "report.Info", report.Info, "sigs", sigs) diff --git a/core/services/llo/channel_definition_cache_factory.go b/core/services/llo/channel_definition_cache_factory.go index 51906e0ff1..0cc2543cdf 100644 --- a/core/services/llo/channel_definition_cache_factory.go +++ b/core/services/llo/channel_definition_cache_factory.go @@ -2,14 +2,15 @@ package llo import ( "fmt" + "net/http" "sync" "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink-common/pkg/logger" 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" ) @@ -19,25 +20,29 @@ type ChannelDefinitionCacheFactory interface { var _ ChannelDefinitionCacheFactory = &channelDefinitionCacheFactory{} -func NewChannelDefinitionCacheFactory(lggr logger.Logger, orm ChannelDefinitionCacheORM, lp logpoller.LogPoller) ChannelDefinitionCacheFactory { +func NewChannelDefinitionCacheFactory(lggr logger.Logger, orm ChannelDefinitionCacheORM, lp logpoller.LogPoller, client *http.Client) ChannelDefinitionCacheFactory { return &channelDefinitionCacheFactory{ lggr, orm, lp, - make(map[common.Address]struct{}), + client, + make(map[common.Address]map[uint32]struct{}), sync.Mutex{}, } } type channelDefinitionCacheFactory struct { - lggr logger.Logger - orm ChannelDefinitionCacheORM - lp logpoller.LogPoller + lggr logger.Logger + orm ChannelDefinitionCacheORM + lp logpoller.LogPoller + client *http.Client - caches map[common.Address]struct{} + caches map[common.Address]map[uint32]struct{} mu sync.Mutex } +// TODO: Test this +// MERC-3653 func (f *channelDefinitionCacheFactory) NewCache(cfg lloconfig.PluginConfig) (llotypes.ChannelDefinitionCache, error) { if cfg.ChannelDefinitions != "" { return NewStaticChannelDefinitionCache(f.lggr, cfg.ChannelDefinitions) @@ -45,14 +50,18 @@ func (f *channelDefinitionCacheFactory) NewCache(cfg lloconfig.PluginConfig) (ll addr := cfg.ChannelDefinitionsContractAddress fromBlock := cfg.ChannelDefinitionsContractFromBlock + donID := cfg.DonID f.mu.Lock() defer f.mu.Unlock() - if _, exists := f.caches[addr]; exists { + if _, exists := f.caches[addr][donID]; exists { // This shouldn't really happen and isn't supported - return nil, fmt.Errorf("cache already exists for contract address %s", addr.Hex()) + return nil, fmt.Errorf("cache already exists for contract address %s and don ID %d", addr.Hex(), donID) } - f.caches[addr] = struct{}{} - return NewChannelDefinitionCache(f.lggr, f.orm, f.lp, addr, fromBlock), nil + if _, exists := f.caches[addr]; !exists { + f.caches[addr] = make(map[uint32]struct{}) + } + f.caches[addr][donID] = struct{}{} + return NewChannelDefinitionCache(f.lggr, f.orm, f.client, f.lp, addr, donID, fromBlock), nil } diff --git a/core/services/llo/codecs.go b/core/services/llo/codecs.go new file mode 100644 index 0000000000..a67b30d2f2 --- /dev/null +++ b/core/services/llo/codecs.go @@ -0,0 +1,18 @@ +package llo + +import ( + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" + "github.com/smartcontractkit/chainlink-data-streams/llo" + + "github.com/smartcontractkit/chainlink/v2/core/services/llo/evm" +) + +// NOTE: All supported codecs must be specified here +func NewCodecs() map[llotypes.ReportFormat]llo.ReportCodec { + codecs := make(map[llotypes.ReportFormat]llo.ReportCodec) + + codecs[llotypes.ReportFormatJSON] = llo.JSONReportCodec{} + codecs[llotypes.ReportFormatEVMPremiumLegacy] = evm.ReportCodecPremiumLegacy{} + + return codecs +} diff --git a/core/services/llo/codecs_test.go b/core/services/llo/codecs_test.go new file mode 100644 index 0000000000..d26b72dd35 --- /dev/null +++ b/core/services/llo/codecs_test.go @@ -0,0 +1,16 @@ +package llo + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" +) + +func Test_NewCodecs(t *testing.T) { + c := NewCodecs() + + assert.Contains(t, c, llotypes.ReportFormatJSON, "expected JSON to be supported") + assert.Contains(t, c, llotypes.ReportFormatEVMPremiumLegacy, "expected EVMPremiumLegacy to be supported") +} diff --git a/core/services/llo/data_source.go b/core/services/llo/data_source.go index 20b14edad5..4f6e91675c 100644 --- a/core/services/llo/data_source.go +++ b/core/services/llo/data_source.go @@ -3,12 +3,12 @@ package llo import ( "context" "fmt" - "math/big" "sort" "sync" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/shopspring/decimal" "golang.org/x/exp/maps" llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" @@ -17,6 +17,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" "github.com/smartcontractkit/chainlink/v2/core/services/streams" + "github.com/smartcontractkit/chainlink/v2/core/utils" ) var ( @@ -97,7 +98,7 @@ func (d *dataSource) Observe(ctx context.Context, streamValues llo.StreamValues, go func(streamID llotypes.StreamID) { defer wg.Done() - var val *big.Int + var val llo.StreamValue stream, exists := d.registry.Get(streamID) if !exists { @@ -115,9 +116,7 @@ func (d *dataSource) Observe(ctx context.Context, streamValues llo.StreamValues, promObservationErrorCount.WithLabelValues(fmt.Sprintf("%d", streamID)).Inc() return } - // TODO: support types other than *big.Int - // https://smartcontract-it.atlassian.net/browse/MERC-3525 - val, err = streams.ExtractBigInt(trrs) + val, err = ExtractStreamValue(trrs) if err != nil { errmu.Lock() errors = append(errors, ErrObservationFailed{inner: err, run: run, streamID: streamID, reason: "failed to extract big.Int"}) @@ -128,7 +127,7 @@ func (d *dataSource) Observe(ctx context.Context, streamValues llo.StreamValues, if val != nil { svmu.Lock() defer svmu.Unlock() - streamValues[streamID] = nil + streamValues[streamID] = val } }(streamID) } @@ -140,18 +139,18 @@ func (d *dataSource) Observe(ctx context.Context, streamValues llo.StreamValues, if len(errors) > 0 { sort.Slice(errors, func(i, j int) bool { return errors[i].streamID < errors[j].streamID }) failedStreamIDs = make([]streams.StreamID, len(errors)) + errStrs := make([]string, len(errors)) for i, e := range errors { + errStrs[i] = e.String() failedStreamIDs[i] = e.streamID } - d.lggr.Warnw("Observation failed for streams", "failedStreamIDs", failedStreamIDs, "errors", errors, "seqNr", opts.SeqNr()) + d.lggr.Warnw("Observation failed for streams", "failedStreamIDs", failedStreamIDs, "errors", errStrs, "seqNr", opts.SeqNr()) } if opts.VerboseLogging() { successes := make([]streams.StreamID, 0, len(streamValues)) - for strmID, res := range streamValues { - if res != nil { - successes = append(successes, strmID) - } + for strmID, _ := range streamValues { + successes = append(successes, strmID) } sort.Slice(successes, func(i, j int) bool { return successes[i] < successes[j] }) d.lggr.Debugw("Observation complete", "successfulStreamIDs", successes, "failedStreamIDs", failedStreamIDs, "values", streamValues, "seqNr", opts.SeqNr()) @@ -159,3 +158,57 @@ func (d *dataSource) Observe(ctx context.Context, streamValues llo.StreamValues, return nil } + +// ExtractStreamValue extracts a StreamValue from a TaskRunResults +func ExtractStreamValue(trrs pipeline.TaskRunResults) (llo.StreamValue, error) { + // pipeline.TaskRunResults comes ordered asc by index, this is guaranteed + // by the pipeline executor + finaltrrs := trrs.Terminals() + + // TODO: Special handling for missing native/link streams? + // https://smartcontract-it.atlassian.net/browse/MERC-5949 + + // HACK: Right now we rely on the number of outputs to determine whether + // its a Decimal or a Quote. + // This isn't very robust or future-proof but is sufficient to support v0.3 + // compat. + // There are a number of different possible ways to solve this in future. + // See: https://smartcontract-it.atlassian.net/browse/MERC-5934 + switch len(finaltrrs) { + case 1: + res := finaltrrs[0].Result + if res.Error != nil { + return nil, res.Error + } + val, err := toDecimal(res.Value) + if err != nil { + return nil, fmt.Errorf("failed to parse BenchmarkPrice: %w", err) + } + return llo.ToDecimal(val), nil + case 3: + // Expect ordering of Benchmark, Bid, Ask + results := make([]decimal.Decimal, 3) + for i, trr := range finaltrrs { + res := trr.Result + if res.Error != nil { + return nil, fmt.Errorf("failed to parse stream output into Quote (task index: %d): %w", i, res.Error) + } + val, err := toDecimal(res.Value) + if err != nil { + return nil, fmt.Errorf("failed to parse decimal: %w", err) + } + results[i] = val + } + return &llo.Quote{ + Benchmark: results[0], + Bid: results[1], + Ask: results[2], + }, nil + default: + return nil, fmt.Errorf("invalid number of results, expected: 1 or 3, got: %d", len(finaltrrs)) + } +} + +func toDecimal(val interface{}) (decimal.Decimal, error) { + return utils.ToDecimal(val) +} diff --git a/core/services/llo/data_source_test.go b/core/services/llo/data_source_test.go index 3716bb2a58..59cd870bc9 100644 --- a/core/services/llo/data_source_test.go +++ b/core/services/llo/data_source_test.go @@ -6,6 +6,7 @@ import ( "math/big" "testing" + "github.com/shopspring/decimal" "github.com/stretchr/testify/assert" "github.com/smartcontractkit/chainlink-data-streams/llo" @@ -55,7 +56,6 @@ func (m mockOpts) VerboseLogging() bool { return true } func (m mockOpts) SeqNr() uint64 { return 42 } func Test_DataSource(t *testing.T) { - t.Skip("waiting on https://github.com/smartcontractkit/chainlink/pull/13780") lggr := logger.TestLogger(t) reg := &mockRegistry{make(map[streams.StreamID]*mockStream)} ds := newDataSource(lggr, reg) @@ -78,7 +78,11 @@ func Test_DataSource(t *testing.T) { err := ds.Observe(ctx, vals, mockOpts{}) assert.NoError(t, err) - assert.Equal(t, llo.StreamValues{}, vals) + assert.Equal(t, llo.StreamValues{ + 2: llo.ToDecimal(decimal.NewFromInt(40602)), + 1: llo.ToDecimal(decimal.NewFromInt(2181)), + 3: llo.ToDecimal(decimal.NewFromInt(15)), + }, 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")) @@ -89,7 +93,11 @@ func Test_DataSource(t *testing.T) { err := ds.Observe(ctx, vals, mockOpts{}) assert.NoError(t, err) - assert.Equal(t, llo.StreamValues{}, vals) + assert.Equal(t, llo.StreamValues{ + 2: llo.ToDecimal(decimal.NewFromInt(40602)), + 1: nil, + 3: nil, + }, vals) }) }) } diff --git a/core/services/llo/delegate.go b/core/services/llo/delegate.go index ddcab383ef..aff670e807 100644 --- a/core/services/llo/delegate.go +++ b/core/services/llo/delegate.go @@ -76,10 +76,7 @@ func NewDelegate(cfg DelegateConfig) (job.ServiceCtx, error) { 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 := NewCodecs() // TODO: Do these services need starting? // https://smartcontract-it.atlassian.net/browse/MERC-3386 diff --git a/core/services/llo/evm/fees.go b/core/services/llo/evm/fees.go new file mode 100644 index 0000000000..b74d68b08d --- /dev/null +++ b/core/services/llo/evm/fees.go @@ -0,0 +1,31 @@ +package evm + +import ( + "math/big" + + "github.com/shopspring/decimal" +) + +// FeeScalingFactor indicates the multiplier applied to fees. +// e.g. for a 1e18 multiplier, a LINK fee of 7.42 will be represented as 7.42e18 +// This is what will be baked into the report for use on-chain. +var FeeScalingFactor = decimal.NewFromInt(1e18) + +// NOTE: Inexact divisions will have this degree of precision +const Precision int32 = 18 + +// CalculateFee outputs a fee in wei according to the formula: baseUSDFee / tokenPriceInUSD +func CalculateFee(tokenPriceInUSD decimal.Decimal, baseUSDFee decimal.Decimal) *big.Int { + if tokenPriceInUSD.IsZero() || baseUSDFee.IsZero() { + // zero fee if token price or base fee is zero + return big.NewInt(0) + } + + // fee denominated in token + fee := baseUSDFee.DivRound(tokenPriceInUSD, Precision) + + // fee scaled up + fee = fee.Mul(FeeScalingFactor) + + return fee.BigInt() +} diff --git a/core/services/llo/evm/fees_test.go b/core/services/llo/evm/fees_test.go new file mode 100644 index 0000000000..16ee98db7d --- /dev/null +++ b/core/services/llo/evm/fees_test.go @@ -0,0 +1,45 @@ +package evm + +import ( + "math/big" + "testing" + + "github.com/shopspring/decimal" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_Fees(t *testing.T) { + BaseUSDFee, err := decimal.NewFromString("0.70") + require.NoError(t, err) + t.Run("with token price > 1", func(t *testing.T) { + tokenPriceInUSD := decimal.NewFromInt32(1630) + fee := CalculateFee(tokenPriceInUSD, BaseUSDFee) + expectedFee := big.NewInt(429447852760736) + if fee.Cmp(expectedFee) != 0 { + t.Errorf("Expected fee to be %v, got %v", expectedFee, fee) + } + }) + + t.Run("with token price < 1", func(t *testing.T) { + tokenPriceInUSD := decimal.NewFromFloat32(0.4) + fee := CalculateFee(tokenPriceInUSD, BaseUSDFee) + expectedFee := big.NewInt(1750000000000000000) + if fee.Cmp(expectedFee) != 0 { + t.Errorf("Expected fee to be %v, got %v", expectedFee, fee) + } + }) + + t.Run("with token price == 0", func(t *testing.T) { + tokenPriceInUSD := decimal.NewFromInt32(0) + fee := CalculateFee(tokenPriceInUSD, BaseUSDFee) + assert.Equal(t, big.NewInt(0), fee) + }) + + t.Run("with base fee == 0", func(t *testing.T) { + tokenPriceInUSD := decimal.NewFromInt32(123) + BaseUSDFee = decimal.NewFromInt32(0) + fee := CalculateFee(tokenPriceInUSD, BaseUSDFee) + assert.Equal(t, big.NewInt(0), fee) + }) +} diff --git a/core/services/llo/evm/report_codec_premium_legacy.go b/core/services/llo/evm/report_codec_premium_legacy.go new file mode 100644 index 0000000000..a062f77b65 --- /dev/null +++ b/core/services/llo/evm/report_codec_premium_legacy.go @@ -0,0 +1,173 @@ +package evm + +import ( + "encoding/json" + "errors" + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/shopspring/decimal" + + "github.com/smartcontractkit/libocr/offchainreporting2/chains/evmutil" + "github.com/smartcontractkit/libocr/offchainreporting2/types" + ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" + v3 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v3" + "github.com/smartcontractkit/chainlink-data-streams/llo" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury" + reporttypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v3/types" + + "github.com/smartcontractkit/chainlink/v2/core/logger" + reportcodecv3 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v3/reportcodec" +) + +var ( + _ llo.ReportCodec = ReportCodecPremiumLegacy{} +) + +type ReportCodecPremiumLegacy struct{ logger.Logger } + +func NewReportCodecPremiumLegacy(lggr logger.Logger) llo.ReportCodec { + return ReportCodecPremiumLegacy{lggr.Named("ReportCodecPremiumLegacy")} +} + +type ReportFormatEVMPremiumLegacyOpts struct { + // BaseUSDFee is the cost on-chain of verifying a report + BaseUSDFee decimal.Decimal `json:"baseUSDFee"` + // Expiration window is the length of time in seconds the report is valid + // for, from the observation timestamp + ExpirationWindow uint32 `json:"expirationWindow"` + // FeedID is for compatibility with existing on-chain verifiers + FeedID common.Hash `json:"feedID"` + // Multiplier is used to scale the bid, benchmark and ask values in the + // report. If not specified, or zero is used, a multiplier of 1 is assumed. + Multiplier *ubig.Big `json:"multiplier"` +} + +func (r *ReportFormatEVMPremiumLegacyOpts) Decode(opts []byte) error { + if len(opts) == 0 { + // special case if opts are unspecified, just use the zero options rather than erroring + return nil + } + if err := json.Unmarshal(opts, r); err != nil { + return err + } + return nil +} + +func (r ReportCodecPremiumLegacy) Encode(report llo.Report, cd llotypes.ChannelDefinition) ([]byte, error) { + if report.Specimen { + return nil, errors.New("ReportCodecPremiumLegacy does not support encoding specimen reports") + } + nativePrice, linkPrice, quote, err := ExtractReportValues(report) + if err != nil { + return nil, fmt.Errorf("ReportCodecPremiumLegacy cannot encode; got unusable report; %w", err) + } + + // NOTE: It seems suboptimal to have to parse the opts on every encode but + // not sure how to avoid it. Should be negligible performance hit as long + // as Opts is small. + opts := ReportFormatEVMPremiumLegacyOpts{} + if err := (&opts).Decode(cd.Opts); err != nil { + return nil, fmt.Errorf("failed to decode opts; got: '%s'; %w", cd.Opts, err) + } + var multiplier decimal.Decimal + if opts.Multiplier == nil { + multiplier = decimal.NewFromInt(1) + } else if opts.Multiplier.IsZero() { + return nil, errors.New("multiplier, if specified in channel opts, must be non-zero") + } else { + multiplier = decimal.NewFromBigInt(opts.Multiplier.ToInt(), 0) + } + + codec := reportcodecv3.NewReportCodec(opts.FeedID, r.Logger) + + rf := v3.ReportFields{ + ValidFromTimestamp: report.ValidAfterSeconds + 1, + Timestamp: report.ObservationTimestampSeconds, + NativeFee: CalculateFee(nativePrice.Decimal(), opts.BaseUSDFee), + LinkFee: CalculateFee(linkPrice.Decimal(), opts.BaseUSDFee), + ExpiresAt: report.ObservationTimestampSeconds + opts.ExpirationWindow, + BenchmarkPrice: quote.Benchmark.Mul(multiplier).BigInt(), + Bid: quote.Bid.Mul(multiplier).BigInt(), + Ask: quote.Ask.Mul(multiplier).BigInt(), + } + return codec.BuildReport(rf) +} + +func (r ReportCodecPremiumLegacy) Decode(b []byte) (*reporttypes.Report, error) { + codec := reportcodecv3.NewReportCodec([32]byte{}, r.Logger) + return codec.Decode(b) +} + +// Pack assembles the report values into a payload for verifying on-chain +func (r ReportCodecPremiumLegacy) Pack(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 + } + reportCtx := LegacyReportContext(digest, seqNr) + rawReportCtx := evmutil.RawReportContext(reportCtx) + + payload, err := mercury.PayloadTypes.Pack(rawReportCtx, []byte(report), rs, ss, vs) + if err != nil { + return nil, fmt.Errorf("abi.Pack failed; %w", err) + } + return payload, nil +} + +// TODO: Test this +// MERC-3524 +func ExtractReportValues(report llo.Report) (nativePrice, linkPrice *llo.Decimal, quote *llo.Quote, err error) { + if len(report.Values) != 3 { + return nil, nil, nil, fmt.Errorf("ReportCodecPremiumLegacy requires exactly 3 values (NativePrice, LinkPrice, Quote{Bid, Mid, Ask}); got report.Values: %#v", report.Values) + } + var is bool + nativePrice, is = report.Values[0].(*llo.Decimal) + if nativePrice == nil { + // Missing price median will cause a zero fee + nativePrice = llo.ToDecimal(decimal.Zero) + } else if !is { + return nil, nil, nil, fmt.Errorf("ReportCodecPremiumLegacy expects first value to be of type *Decimal; got: %T", report.Values[0]) + } + linkPrice, is = report.Values[1].(*llo.Decimal) + if linkPrice == nil { + // Missing price median will cause a zero fee + linkPrice = llo.ToDecimal(decimal.Zero) + } else if !is { + return nil, nil, nil, fmt.Errorf("ReportCodecPremiumLegacy expects second value to be of type *Decimal; got: %T", report.Values[1]) + } + quote, is = report.Values[2].(*llo.Quote) + if !is { + return nil, nil, nil, fmt.Errorf("ReportCodecPremiumLegacy expects third value to be of type *Quote; got: %T", report.Values[2]) + } + return nativePrice, linkPrice, quote, nil +} + +// TODO: Consider embedding the DON ID here? +// MERC-3524 +var LLOExtraHash = common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000001") + +func LegacyReportContext(cd ocr2types.ConfigDigest, seqNr uint64) ocr2types.ReportContext { + // Simulate 256 rounds/epoch + epoch := seqNr / 256 + round := seqNr % 256 + return ocr2types.ReportContext{ + ReportTimestamp: ocr2types.ReportTimestamp{ + ConfigDigest: cd, + Epoch: uint32(epoch), + Round: uint8(round), + }, + ExtraHash: LLOExtraHash, // ExtraHash is always zero for mercury, we use LLOExtraHash here to differentiate from the legacy plugin + } +} diff --git a/core/services/llo/evm/report_codec_premium_legacy_test.go b/core/services/llo/evm/report_codec_premium_legacy_test.go new file mode 100644 index 0000000000..c22d29c340 --- /dev/null +++ b/core/services/llo/evm/report_codec_premium_legacy_test.go @@ -0,0 +1,107 @@ +package evm + +import ( + "fmt" + "math/big" + "testing" + + "github.com/shopspring/decimal" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + reporttypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v3/types" + + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" + + "github.com/smartcontractkit/chainlink-data-streams/llo" +) + +const ethMainnetChainSelector uint64 = 5009297550715157269 + +func newValidPremiumLegacyReport() llo.Report { + return llo.Report{ + ConfigDigest: types.ConfigDigest{1, 2, 3}, + SeqNr: 32, + ChannelID: llotypes.ChannelID(31), + ValidAfterSeconds: 28, + ObservationTimestampSeconds: 34, + Values: []llo.StreamValue{llo.ToDecimal(decimal.NewFromInt(35)), llo.ToDecimal(decimal.NewFromInt(36)), &llo.Quote{Bid: decimal.NewFromInt(37), Benchmark: decimal.NewFromInt(38), Ask: decimal.NewFromInt(39)}}, + Specimen: false, + } +} + +func Test_ReportCodecPremiumLegacy(t *testing.T) { + rc := ReportCodecPremiumLegacy{} + + feedID := [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} + cd := llotypes.ChannelDefinition{Opts: llotypes.ChannelOpts(fmt.Sprintf(`{"baseUSDFee":"10.50","expirationWindow":60,"feedId":"0x%x","multiplier":10}`, feedID))} + + t.Run("Encode errors if no values", func(t *testing.T) { + _, err := rc.Encode(llo.Report{}, cd) + require.Error(t, err) + + assert.Contains(t, err.Error(), "ReportCodecPremiumLegacy cannot encode; got unusable report; ReportCodecPremiumLegacy requires exactly 3 values (NativePrice, LinkPrice, Quote{Bid, Mid, Ask}); got report.Values: []llo.StreamValue(nil)") + }) + + t.Run("does not encode specimen reports", func(t *testing.T) { + report := newValidPremiumLegacyReport() + report.Specimen = true + + _, err := rc.Encode(report, cd) + require.Error(t, err) + assert.EqualError(t, err, "ReportCodecPremiumLegacy does not support encoding specimen reports") + }) + + t.Run("Encode constructs a report from observations", func(t *testing.T) { + report := newValidPremiumLegacyReport() + + encoded, err := rc.Encode(report, cd) + require.NoError(t, err) + + assert.Len(t, encoded, 288) + 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, 0x1d, 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, 0x4, 0x29, 0xd0, 0x69, 0x18, 0x9e, 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, 0x4, 0xc, 0x35, 0x49, 0xbb, 0x7d, 0x2a, 0xab, 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, 0x5e, 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, 0x7c, 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, 0x72, 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, 0x86}, encoded) + + decoded, err := reporttypes.Decode(encoded) + require.NoError(t, err) + assert.Equal(t, feedID, decoded.FeedId) + assert.Equal(t, uint32(34), decoded.ObservationsTimestamp) + assert.Equal(t, big.NewInt(380), decoded.BenchmarkPrice) + assert.Equal(t, big.NewInt(370), decoded.Bid) + assert.Equal(t, big.NewInt(390), decoded.Ask) + assert.Equal(t, uint32(29), decoded.ValidFromTimestamp) + assert.Equal(t, uint32(94), decoded.ExpiresAt) + assert.Equal(t, big.NewInt(291666666666666667), decoded.LinkFee) + assert.Equal(t, big.NewInt(300000000000000000), decoded.NativeFee) + + t.Run("Decode decodes the report", func(t *testing.T) { + decoded, err := rc.Decode(encoded) + require.NoError(t, err) + + assert.Equal(t, &reporttypes.Report{ + FeedId: [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}, + ObservationsTimestamp: 0x22, + BenchmarkPrice: big.NewInt(380), + Bid: big.NewInt(370), + Ask: big.NewInt(390), + ValidFromTimestamp: 0x1d, + ExpiresAt: uint32(94), + LinkFee: big.NewInt(291666666666666667), + NativeFee: big.NewInt(300000000000000000), + }, decoded) + }) + }) + + 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 uint32 value") + }) +} diff --git a/core/services/llo/keyring.go b/core/services/llo/keyring.go index 042b5e4625..8137a5ac3d 100644 --- a/core/services/llo/keyring.go +++ b/core/services/llo/keyring.go @@ -1,15 +1,19 @@ package llo import ( + "bytes" "fmt" + "sort" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" "github.com/smartcontractkit/libocr/offchainreporting2plus/types" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "golang.org/x/exp/maps" llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/llo/evm" ) type LLOOnchainKeyring ocr3types.OnchainKeyring[llotypes.ReportInfo] @@ -17,6 +21,11 @@ type LLOOnchainKeyring ocr3types.OnchainKeyring[llotypes.ReportInfo] var _ LLOOnchainKeyring = &onchainKeyring{} type Key interface { + // Legacy Sign/Verify methods needed for v0.3 report compatibility + // New keys can leave these stubbed + Sign(reportCtx ocrtypes.ReportContext, report ocrtypes.Report) ([]byte, error) + Verify(publicKey ocrtypes.OnchainPublicKey, reportCtx ocrtypes.ReportContext, report ocrtypes.Report, signature []byte) bool + 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 @@ -35,12 +44,29 @@ func NewOnchainKeyring(lggr logger.Logger, keys map[llotypes.ReportFormat]Key) L } func (okr *onchainKeyring) PublicKey() types.OnchainPublicKey { - // All public keys combined - var pk []byte - for _, k := range okr.keys { - pk = append(pk, k.PublicKey()...) + // All unique public keys sorted in ascending order and combined into one + // byte string + onchainPublicKey := []byte{} + + keys := maps.Values(okr.keys) + if len(keys) == 0 { + return onchainPublicKey } - return pk + sort.Slice(keys, func(i, j int) bool { + return bytes.Compare(keys[i].PublicKey(), keys[j].PublicKey()) < 0 + }) + + onchainPublicKey = append(onchainPublicKey, keys[0].PublicKey()...) + if len(keys) == 1 { + return onchainPublicKey + } + for i := 1; i < len(keys); i++ { + if keys[i] != keys[i-1] { + onchainPublicKey = append(onchainPublicKey, keys[i].PublicKey()...) + } + } + + return onchainPublicKey } func (okr *onchainKeyring) MaxSignatureLength() (n int) { @@ -52,17 +78,37 @@ func (okr *onchainKeyring) MaxSignatureLength() (n int) { } func (okr *onchainKeyring) Sign(digest types.ConfigDigest, seqNr uint64, r ocr3types.ReportWithInfo[llotypes.ReportInfo]) (signature []byte, err error) { - rf := r.Info.ReportFormat - if key, exists := okr.keys[rf]; exists { - return key.Sign3(digest, seqNr, r.Report) + switch r.Info.ReportFormat { + case llotypes.ReportFormatEVMPremiumLegacy: + rf := r.Info.ReportFormat + if key, exists := okr.keys[rf]; exists { + // NOTE: Must use legacy Sign method for compatibility with v0.3 report verification + rc := evm.LegacyReportContext(digest, seqNr) + return key.Sign(rc, r.Report) + } + default: + rf := r.Info.ReportFormat + 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 - if verifier, exists := okr.keys[rf]; exists { - return verifier.Verify3(key, digest, seqNr, r.Report, signature) + switch r.Info.ReportFormat { + case llotypes.ReportFormatEVMPremiumLegacy: + rf := r.Info.ReportFormat + if verifier, exists := okr.keys[rf]; exists { + // NOTE: Must use legacy Verify method for compatibility with v0.3 report verification + rc := evm.LegacyReportContext(digest, seqNr) + return verifier.Verify(key, rc, r.Report, signature) + } + default: + rf := r.Info.ReportFormat + 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 0000000000..44371e1496 --- /dev/null +++ b/core/services/llo/keyring_test.go @@ -0,0 +1,115 @@ +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 + sig []byte +} + +func (m *mockKey) Sign(reportCtx ocrtypes.ReportContext, report ocrtypes.Report) ([]byte, error) { + return m.sig, nil +} + +func (m *mockKey) Verify(publicKey ocrtypes.OnchainPublicKey, reportCtx ocrtypes.ReportContext, report ocrtypes.Report, signature []byte) bool { + return m.verify +} + +func (m *mockKey) Sign3(digest ocrtypes.ConfigDigest, seqNr uint64, r ocrtypes.Report) (signature []byte, err error) { + return m.sig, 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.ReportFormatEVMPremiumLegacy: &mockKey{format: llotypes.ReportFormatEVMPremiumLegacy, maxSignatureLen: 1, sig: []byte("sig-1")}, + llotypes.ReportFormatJSON: &mockKey{format: llotypes.ReportFormatJSON, maxSignatureLen: 2, sig: []byte("sig-2")}, + } + + kr := NewOnchainKeyring(lggr, ks) + + cases := []struct { + format llotypes.ReportFormat + }{ + { + llotypes.ReportFormatEVMPremiumLegacy, + }, + { + llotypes.ReportFormatJSON, + }, + } + + 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 + }) + } + }) + + t.Run("MaxSignatureLength", func(t *testing.T) { + assert.Equal(t, 2+1, kr.MaxSignatureLength()) + }) + t.Run("PublicKey", func(t *testing.T) { + b := make([]byte, 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/onchain_channel_definition_cache.go b/core/services/llo/onchain_channel_definition_cache.go index 4d3fb0e825..51cfae964d 100644 --- a/core/services/llo/onchain_channel_definition_cache.go +++ b/core/services/llo/onchain_channel_definition_cache.go @@ -1,34 +1,54 @@ package llo import ( + "bytes" "context" "database/sql" + "encoding/json" "errors" "fmt" + "io" + "io/ioutil" "maps" + "math/big" + "net/http" "strings" "sync" "time" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" + "golang.org/x/crypto/sha3" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "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/utils" + clhttp "github.com/smartcontractkit/chainlink/v2/core/utils/http" ) -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) -} +const ( + // MaxChannelDefinitionsFileSize is a sanity limit to avoid OOM for a + // maliciously large file. It should be much larger than any real expected + // channel definitions file. + MaxChannelDefinitionsFileSize = 25 * 1024 * 1024 // 25MB + // How often we query logpoller for new logs + defaultLogPollInterval = 1 * time.Second + // How often we check for failed persistence and attempt to save again + dbPersistLoopInterval = 1 * time.Second + + newChannelDefinitionEventName = "NewChannelDefinition" +) + +var ( + channelConfigStoreABI abi.ABI + topicNewChannelDefinition = (channel_config_store.ChannelConfigStoreNewChannelDefinition{}).Topic() -var channelConfigStoreABI abi.ABI + allTopics = []common.Hash{topicNewChannelDefinition} +) func init() { var err error @@ -38,82 +58,126 @@ func init() { } } +type ChannelDefinitionCacheORM interface { + // TODO: What about delete/cleanup? + // https://smartcontract-it.atlassian.net/browse/MERC-3653 + LoadChannelDefinitions(ctx context.Context, addr common.Address, donID uint32) (pd *PersistedDefinitions, err error) + StoreChannelDefinitions(ctx context.Context, addr common.Address, donID, version uint32, dfns llotypes.ChannelDefinitions, blockNum int64) (err error) +} + var _ llotypes.ChannelDefinitionCache = &channelDefinitionCache{} +type LogPoller interface { + RegisterFilter(ctx context.Context, filter logpoller.Filter) error + LatestBlock(ctx context.Context) (logpoller.LogPollerBlock, error) + LogsWithSigs(ctx context.Context, start, end int64, eventSigs []common.Hash, address common.Address) ([]logpoller.Log, error) +} + +type Option func(*channelDefinitionCache) + +func WithLogPollInterval(d time.Duration) Option { + return func(c *channelDefinitionCache) { + c.logPollInterval = d + } +} + type channelDefinitionCache struct { services.StateMachine - orm ChannelDefinitionCacheORM + orm ChannelDefinitionCacheORM + client HTTPClient + httpLimit int64 + + filterName string + lp LogPoller + logPollInterval time.Duration + addr common.Address + donID uint32 + lggr logger.SugaredLogger + initialBlockNum int64 - filterName string - lp logpoller.LogPoller - fromBlock int64 - addr common.Address - lggr logger.Logger + newLogMu sync.RWMutex + newLog *channel_config_store.ChannelConfigStoreNewChannelDefinition + newLogCh chan *channel_config_store.ChannelConfigStoreNewChannelDefinition definitionsMu sync.RWMutex definitions llotypes.ChannelDefinitions + definitionsVersion uint32 definitionsBlockNum int64 + persistMu sync.RWMutex + persistedVersion uint32 + wg sync.WaitGroup chStop chan struct{} } -var ( - topicNewChannelDefinition = (channel_config_store.ChannelConfigStoreNewChannelDefinition{}).Topic() - topicChannelDefinitionRemoved = (channel_config_store.ChannelConfigStoreChannelDefinitionRemoved{}).Topic() - - allTopics = []common.Hash{topicNewChannelDefinition, topicChannelDefinitionRemoved} -) +type HTTPClient interface { + Do(req *http.Request) (*http.Response, error) +} -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 NewChannelDefinitionCache(lggr logger.Logger, orm ChannelDefinitionCacheORM, client HTTPClient, lp logpoller.LogPoller, addr common.Address, donID uint32, fromBlock int64, options ...Option) llotypes.ChannelDefinitionCache { + filterName := logpoller.FilterName("OCR3 LLO ChannelDefinitionCachePoller", addr.String(), donID) + cdc := &channelDefinitionCache{ + orm: orm, + client: client, + httpLimit: MaxChannelDefinitionsFileSize, + filterName: filterName, + lp: lp, + logPollInterval: defaultLogPollInterval, + addr: addr, + donID: donID, + lggr: logger.Sugared(lggr).Named("ChannelDefinitionCache").With("addr", addr, "fromBlock", fromBlock), + newLogCh: make(chan *channel_config_store.ChannelConfigStoreNewChannelDefinition, 1), + initialBlockNum: fromBlock, + chStop: make(chan struct{}), + } + for _, option := range options { + option(cdc) } + return cdc } 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(ctx, logpoller.Filter{Name: c.filterName, EventSigs: allTopics, Addresses: []common.Address{c.addr}}) + donIDTopic := common.BigToHash(big.NewInt(int64(c.donID))) + err = c.lp.RegisterFilter(ctx, logpoller.Filter{Name: c.filterName, EventSigs: allTopics, Topic2: []common.Hash{donIDTopic}, Addresses: []common.Address{c.addr}}) if err != nil { return err } - if definitions, definitionsBlockNum, err := c.orm.LoadChannelDefinitions(ctx, c.addr); err != nil { + if pd, err := c.orm.LoadChannelDefinitions(ctx, c.addr, c.donID); err != nil { return err - } else if definitions != nil { - c.definitions = definitions - c.definitionsBlockNum = definitionsBlockNum + } else if pd != nil { + c.definitions = pd.Definitions + c.initialBlockNum = pd.BlockNum + 1 + c.definitionsVersion = uint32(pd.Version) } else { // ensure non-nil map ready for assignment later c.definitions = make(llotypes.ChannelDefinitions) - // leave c.definitionsBlockNum as provided fromBlock argument + // leave c.initialBlockNum as provided fromBlock argument } - c.wg.Add(1) - go c.poll() + c.wg.Add(3) + // We have three concurrent loops + // 1. Poll chain for new logs + // 2. Fetch latest definitions from URL and verify SHA, according to latest log + // 3. Retry persisting records to DB, if it failed + go c.pollChainLoop() + go c.fetchLatestLoop() + go c.failedPersistLoop() return nil }) } -// TODO: make this configurable? -const pollInterval = 1 * time.Second +//////////////////////////////////////////////////////////////////// +// Log Polling +//////////////////////////////////////////////////////////////////// -func (c *channelDefinitionCache) poll() { +// pollChainLoop periodically checks logpoller for new logs +func (c *channelDefinitionCache) pollChainLoop() { defer c.wg.Done() - pollT := services.NewTicker(pollInterval) + pollT := services.NewTicker(c.logPollInterval) defer pollT.Stop() for { @@ -121,114 +185,303 @@ func (c *channelDefinitionCache) poll() { 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 + // failures will be tried again on the next tick + if err := c.readLogs(); err != nil { 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 +func (c *channelDefinitionCache) readLogs() (err error) { ctx, cancel := services.StopChan(c.chStop).NewCtx() defer cancel() - // https://smartcontract-it.atlassian.net/browse/MERC-3653 - latest, err := c.lp.LatestBlock(ctx) + latestBlock, err := c.lp.LatestBlock(ctx) if errors.Is(err, sql.ErrNoRows) { c.lggr.Debug("Logpoller has no logs yet, skipping poll") - return 0, nil + return nil } else if err != nil { - return 0, err + return err } - toBlock := latest.BlockNumber + toBlock := latestBlock.BlockNumber - fromBlock := c.definitionsBlockNum + fromBlock := c.scanFromBlockNum() if toBlock <= fromBlock { - return 0, nil + return nil } - // NOTE: We assume that log poller returns logs in ascending order chronologically + // NOTE: We assume that log poller returns logs in order of block_num, log_index ASC logs, err := c.lp.LogsWithSigs(ctx, fromBlock, toBlock, allTopics, c.addr) if err != nil { - // TODO: retry? - // https://smartcontract-it.atlassian.net/browse/MERC-3653 - return 0, err + return err } + for _, log := range logs { - if err = c.applyLog(log); err != nil { - return 0, err + switch log.EventSig { + case topicNewChannelDefinition: + unpacked := new(channel_config_store.ChannelConfigStoreNewChannelDefinition) + + err := channelConfigStoreABI.UnpackIntoInterface(unpacked, newChannelDefinitionEventName, log.Data) + if err != nil { + return fmt.Errorf("failed to unpack log data: %w", err) + } + if len(log.Topics) < 2 { + // should never happen but must guard against unexpected panics + c.lggr.Warnw("Log missing expected topics", "log", log) + continue + } + unpacked.DonId = new(big.Int).SetBytes(log.Topics[1]) + + if unpacked.DonId.Cmp(big.NewInt(int64(c.donID))) != 0 { + c.lggr.Warnw("Got log for unexpected donID", "donID", unpacked.DonId.String(), "expectedDonID", c.donID) + // ignore logs for other donIDs + // NOTE: shouldn't happen anyway since log poller filters on + // donID + continue + } + + c.newLogMu.Lock() + if c.newLog == nil || unpacked.Version > c.newLog.Version { + // assume that donID is correct due to log poller filtering + c.lggr.Infow("Got new channel definitions from chain", "version", unpacked.Version, "blockNumber", log.BlockNumber, "sha", fmt.Sprintf("%x", unpacked.Sha), "url", unpacked.Url) + c.newLog = unpacked + c.newLogCh <- unpacked + } + c.newLogMu.Unlock() + + default: + // ignore unrecognized logs + continue } } - // 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 + return nil +} + +func (c *channelDefinitionCache) scanFromBlockNum() int64 { + c.newLogMu.RLock() + defer c.newLogMu.RUnlock() + if c.newLog != nil { + return int64(c.newLog.Raw.BlockNumber) + 1 } + return c.initialBlockNum +} - c.definitionsBlockNum = toBlock +//////////////////////////////////////////////////////////////////// +// Fetch channel definitions from URL based on latest log +//////////////////////////////////////////////////////////////////// - return len(logs), nil +// fetchLatestLoop waits for new logs and tries on a loop to fetch the channel definitions from the specified url +func (c *channelDefinitionCache) fetchLatestLoop() { + defer c.wg.Done() + + var fetchCh chan struct{} + + for { + select { + case latest := <-c.newLogCh: + // kill the old retry loop if any + if fetchCh != nil { + close(fetchCh) + } + + fetchCh = make(chan struct{}) + + c.wg.Add(1) + go c.fetchLoop(fetchCh, latest) + + case <-c.chStop: + return + } + } } -func (c *channelDefinitionCache) applyLog(log logpoller.Log) error { - switch log.EventSig { - case topicNewChannelDefinition: - unpacked := new(channel_config_store.ChannelConfigStoreNewChannelDefinition) +func (c *channelDefinitionCache) fetchLoop(closeCh chan struct{}, log *channel_config_store.ChannelConfigStoreNewChannelDefinition) { + defer c.wg.Done() + b := utils.NewHTTPFetchBackoff() + var attemptCnt int - err := channelConfigStoreABI.UnpackIntoInterface(unpacked, "NewChannelDefinition", log.Data) - if err != nil { - return fmt.Errorf("failed to unpack log data: %w", err) + ctx, cancel := services.StopChan(c.chStop).NewCtx() + defer cancel() + + err := c.fetchAndSetChannelDefinitions(ctx, log) + if err == nil { + c.lggr.Debugw("Set new channel definitions", "donID", c.donID, "version", log.Version, "url", log.Url, "sha", fmt.Sprintf("%x", log.Sha)) + return + } + c.lggr.Warnw("Error while fetching channel definitions", "donID", c.donID, "version", log.Version, "url", log.Url, "sha", fmt.Sprintf("%x", log.Sha), "err", err, "attempt", attemptCnt) + + for { + select { + case <-closeCh: + return + case <-time.After(b.Duration()): + attemptCnt++ + err := c.fetchAndSetChannelDefinitions(ctx, log) + if err != nil { + c.lggr.Warnw("Error while fetching channel definitions", "version", log.Version, "url", log.Url, "sha", fmt.Sprintf("%x", log.Sha), "err", err, "attempt", attemptCnt) + continue + } + c.lggr.Debugw("Set new channel definitions", "donID", c.donID, "version", log.Version, "url", log.Url, "sha", fmt.Sprintf("%x", log.Sha)) + return } + } +} - c.applyNewChannelDefinition(unpacked) - case topicChannelDefinitionRemoved: - unpacked := new(channel_config_store.ChannelConfigStoreChannelDefinitionRemoved) +func (c *channelDefinitionCache) fetchAndSetChannelDefinitions(ctx context.Context, log *channel_config_store.ChannelConfigStoreNewChannelDefinition) error { + c.definitionsMu.RLock() + if log.Version <= c.definitionsVersion { + c.definitionsMu.RUnlock() + return nil + } + c.definitionsMu.RUnlock() - err := channelConfigStoreABI.UnpackIntoInterface(unpacked, "ChannelDefinitionRemoved", log.Data) + cd, err := c.fetchChannelDefinitions(ctx, log.Url, log.Sha) + if err != nil { + return err + } + c.definitionsMu.Lock() + if log.Version <= c.definitionsVersion { + c.definitionsMu.Unlock() + return nil + } + c.definitions = cd + c.definitionsBlockNum = int64(log.Raw.BlockNumber) + c.definitionsVersion = log.Version + c.definitionsMu.Unlock() + + if memoryVersion, persistedVersion, err := c.persist(context.Background()); err != nil { + // If this fails, the failedPersistLoop will try again + c.lggr.Warnw("Failed to persist channel definitions", "err", err, "memoryVersion", memoryVersion, "persistedVersion", persistedVersion) + } + + return nil +} + +func (c *channelDefinitionCache) fetchChannelDefinitions(ctx context.Context, url string, expectedSha [32]byte) (llotypes.ChannelDefinitions, error) { + request, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, fmt.Errorf("failed to create http.Request; %w", err) + } + request.Header.Set("Content-Type", "application/json") + + httpRequest := clhttp.HTTPRequest{ + Client: c.client, + Request: request, + Config: clhttp.HTTPRequestConfig{SizeLimit: c.httpLimit}, + Logger: c.lggr.Named("HTTPRequest").With("url", url, "expectedSHA", fmt.Sprintf("%x", expectedSha)), + } + + reader, statusCode, _, err := httpRequest.SendRequestReader() + if err != nil { + return nil, fmt.Errorf("error making http request: %w", err) + } + defer reader.Close() + + if statusCode >= 400 { + // NOTE: Truncate the returned body here as we don't want to spam the + // logs with potentially huge messages + body := http.MaxBytesReader(nil, reader, 1024) + defer body.Close() + bodyBytes, err := ioutil.ReadAll(body) if err != nil { - return fmt.Errorf("failed to unpack log data: %w", err) + return nil, fmt.Errorf("got error from %s: (status code: %d, error reading response body: %w, response body: %s)", url, statusCode, err, bodyBytes) } + return nil, fmt.Errorf("got error from %s: (status code: %d, response body: %s)", url, statusCode, string(bodyBytes)) + } + + var buf bytes.Buffer + // Use a teeReader to avoid excessive copying + teeReader := io.TeeReader(reader, &buf) - 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()) + hash := sha3.New256() + // Stream the data directly into the hash and copy to buf as we go + if _, err := io.Copy(hash, teeReader); err != nil { + return nil, fmt.Errorf("failed to read from body: %w", err) } - return nil + + actualSha := hash.Sum(nil) + if !bytes.Equal(expectedSha[:], actualSha) { + return nil, fmt.Errorf("SHA3 mismatch: expected %x, got %x", expectedSha, actualSha) + } + + var cd llotypes.ChannelDefinitions + decoder := json.NewDecoder(&buf) + if err := decoder.Decode(&cd); err != nil { + return nil, fmt.Errorf("failed to decode JSON: %w", err) + } + + return cd, 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), +//////////////////////////////////////////////////////////////////// +// Persistence +//////////////////////////////////////////////////////////////////// + +func (c *channelDefinitionCache) persist(ctx context.Context) (memoryVersion, persistedVersion uint32, err error) { + c.persistMu.RLock() + persistedVersion = c.persistedVersion + c.persistMu.RUnlock() + + c.definitionsMu.RLock() + memoryVersion = c.definitionsVersion + dfns := c.definitions + blockNum := c.definitionsBlockNum + c.definitionsMu.RUnlock() + + if memoryVersion <= persistedVersion { + return + } + + if err = c.orm.StoreChannelDefinitions(ctx, c.addr, c.donID, memoryVersion, dfns, blockNum); err != nil { + return + } + + c.persistMu.Lock() + defer c.persistMu.Unlock() + if memoryVersion > c.persistedVersion { + persistedVersion = memoryVersion + c.persistedVersion = persistedVersion } + + // TODO: we could delete the old logs from logpoller here actually + // https://smartcontract-it.atlassian.net/browse/MERC-3653 + return } -func (c *channelDefinitionCache) applyChannelDefinitionRemoved(log *channel_config_store.ChannelConfigStoreChannelDefinitionRemoved) { - c.definitionsMu.Lock() - defer c.definitionsMu.Unlock() - delete(c.definitions, log.ChannelId) +// Checks persisted version and tries to save if necessary on a periodic timer +// Simple backup in case database persistence fails +func (c *channelDefinitionCache) failedPersistLoop() { + defer c.wg.Done() + + ctx, cancel := services.StopChan(c.chStop).NewCtx() + defer cancel() + + for { + select { + case <-time.After(dbPersistLoopInterval): + if memoryVersion, persistedVersion, err := c.persist(ctx); err != nil { + c.lggr.Warnw("Failed to persist channel definitions", "err", err, "memoryVersion", memoryVersion, "persistedVersion", persistedVersion) + } + case <-c.chStop: + // Try one final persist with a short-ish timeout, then return + ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + if memoryVersion, persistedVersion, err := c.persist(ctx); err != nil { + c.lggr.Errorw("Failed to persist channel definitions on shutdown", "err", err, "memoryVersion", memoryVersion, "persistedVersion", persistedVersion) + } + return + } + } } 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 { + // Cancel all contexts but try one final persist before closing close(c.chStop) c.wg.Wait() return nil diff --git a/core/services/llo/onchain_channel_definition_cache_test.go b/core/services/llo/onchain_channel_definition_cache_test.go index 2fbc0c1b90..5bd7eedbb1 100644 --- a/core/services/llo/onchain_channel_definition_cache_test.go +++ b/core/services/llo/onchain_channel_definition_cache_test.go @@ -1,23 +1,429 @@ package llo import ( + "bytes" + "context" + "database/sql" + "errors" + "fmt" + "io" + "math/big" + "math/rand" + "net/http" "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + llotypes "github.com/smartcontractkit/chainlink-common/pkg/types/llo" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "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/testutils" ) +type mockLogPoller struct { + latestBlock logpoller.LogPollerBlock + latestBlockErr error + logsWithSigs []logpoller.Log + logsWithSigsErr error +} + +func (m *mockLogPoller) RegisterFilter(ctx context.Context, filter logpoller.Filter) error { + return nil +} +func (m *mockLogPoller) LatestBlock(ctx context.Context) (logpoller.LogPollerBlock, error) { + return m.latestBlock, m.latestBlockErr +} +func (m *mockLogPoller) LogsWithSigs(ctx context.Context, start, end int64, eventSigs []common.Hash, address common.Address) ([]logpoller.Log, error) { + return m.logsWithSigs, m.logsWithSigsErr +} + +var _ HTTPClient = &mockHTTPClient{} + +type mockHTTPClient struct { + resp *http.Response + err error +} + +func (m *mockHTTPClient) Do(req *http.Request) (*http.Response, error) { + return m.resp, m.err +} + +var _ ChannelDefinitionCacheORM = &mockORM{} + +type mockORM struct { + err error + + lastPersistedAddr common.Address + lastPersistedDonID uint32 + lastPersistedVersion uint32 + lastPersistedDfns llotypes.ChannelDefinitions + lastPersistedBlockNum int64 +} + +func (m *mockORM) LoadChannelDefinitions(ctx context.Context, addr common.Address, donID uint32) (pd *PersistedDefinitions, err error) { + panic("not implemented") +} +func (m *mockORM) StoreChannelDefinitions(ctx context.Context, addr common.Address, donID, version uint32, dfns llotypes.ChannelDefinitions, blockNum int64) (err error) { + m.lastPersistedAddr = addr + m.lastPersistedDonID = donID + m.lastPersistedVersion = version + m.lastPersistedDfns = dfns + m.lastPersistedBlockNum = blockNum + return m.err +} + +func makeLog(t *testing.T, donID, version uint32, url string, sha [32]byte) logpoller.Log { + data := makeLogData(t, donID, version, url, sha) + return logpoller.Log{EventSig: topicNewChannelDefinition, Topics: [][]byte{topicNewChannelDefinition[:], makeDonIDTopic(donID)}, Data: data} +} + +func makeLogData(t *testing.T, donID, version uint32, url string, sha [32]byte) []byte { + event := channelConfigStoreABI.Events[newChannelDefinitionEventName] + // donID is indexed + // version, url, sha + data, err := event.Inputs.NonIndexed().Pack(version, url, sha) + require.NoError(t, err) + return data +} + +func makeDonIDTopic(donID uint32) []byte { + return common.BigToHash(big.NewInt(int64(donID))).Bytes() +} + func Test_ChannelDefinitionCache(t *testing.T) { - t.Skip("waiting on https://github.com/smartcontractkit/chainlink/pull/13780") - // 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()) - // }) + donID := rand.Uint32() + ctx := tests.Context(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), + Streams: []llotypes.Stream{{StreamID: 1, Aggregator: llotypes.AggregatorMedian}, {StreamID: 2, Aggregator: llotypes.AggregatorMode}, {StreamID: 3, Aggregator: llotypes.AggregatorQuote}}, + Opts: llotypes.ChannelOpts{1, 2, 3}, + }, + }) + + cdc := &channelDefinitionCache{definitions: dfns} + + assert.Equal(t, dfns, cdc.Definitions()) + }) + + t.Run("readLogs", func(t *testing.T) { + lp := &mockLogPoller{latestBlockErr: sql.ErrNoRows} + newLogCh := make(chan *channel_config_store.ChannelConfigStoreNewChannelDefinition, 100) + cdc := &channelDefinitionCache{donID: donID, lp: lp, lggr: logger.TestSugared(t), newLogCh: newLogCh} + + t.Run("skips if logpoller has no blocks", func(t *testing.T) { + err := cdc.readLogs() + assert.NoError(t, err) + assert.Nil(t, cdc.newLog) + }) + t.Run("returns error on LatestBlock failure", func(t *testing.T) { + lp.latestBlockErr = errors.New("test error") + + err := cdc.readLogs() + assert.EqualError(t, err, "test error") + assert.Nil(t, cdc.newLog) + }) + t.Run("does nothing if LatestBlock older or the same as current channel definitions block", func(t *testing.T) { + lp.latestBlockErr = nil + lp.latestBlock = logpoller.LogPollerBlock{BlockNumber: 42} + cdc.definitionsBlockNum = 43 + + err := cdc.readLogs() + assert.NoError(t, err) + assert.Nil(t, cdc.newLog) + }) + t.Run("returns error if LogsWithSigs fails", func(t *testing.T) { + cdc.definitionsBlockNum = 0 + lp.logsWithSigsErr = errors.New("test error 2") + + err := cdc.readLogs() + assert.EqualError(t, err, "test error 2") + assert.Nil(t, cdc.newLog) + }) + t.Run("ignores logs with different topic", func(t *testing.T) { + lp.logsWithSigsErr = nil + lp.logsWithSigs = []logpoller.Log{{EventSig: common.Hash{1, 2, 3, 4}}} + + err := cdc.readLogs() + assert.NoError(t, err) + assert.Nil(t, cdc.newLog) + }) + t.Run("returns error if log is malformed", func(t *testing.T) { + lp.logsWithSigsErr = nil + lp.logsWithSigs = []logpoller.Log{{EventSig: topicNewChannelDefinition}} + + err := cdc.readLogs() + assert.EqualError(t, err, "failed to unpack log data: abi: attempting to unmarshal an empty string while arguments are expected") + assert.Nil(t, cdc.newLog) + }) + t.Run("sets definitions and sends on channel if LogsWithSigs returns new event with a later version", func(t *testing.T) { + lp.logsWithSigsErr = nil + lp.logsWithSigs = []logpoller.Log{makeLog(t, donID, uint32(43), "http://example.com/xxx.json", [32]byte{1, 2, 3, 4})} + + err := cdc.readLogs() + require.NoError(t, err) + require.NotNil(t, cdc.newLog) + assert.Equal(t, uint32(43), cdc.newLog.Version) + assert.Equal(t, "http://example.com/xxx.json", cdc.newLog.Url) + assert.Equal(t, [32]byte{1, 2, 3, 4}, cdc.newLog.Sha) + assert.Equal(t, int64(donID), cdc.newLog.DonId.Int64()) + + func() { + for { + select { + case log := <-newLogCh: + assert.Equal(t, cdc.newLog, log) + default: + return + } + } + }() + }) + t.Run("does nothing if version older or the same as the one currently set", func(t *testing.T) { + lp.logsWithSigsErr = nil + lp.logsWithSigs = []logpoller.Log{ + makeLog(t, donID, uint32(42), "http://example.com/xxx.json", [32]byte{1, 2, 3, 4}), + makeLog(t, donID, uint32(43), "http://example.com/xxx.json", [32]byte{1, 2, 3, 4}), + } + + err := cdc.readLogs() + require.NoError(t, err) + assert.Equal(t, uint32(43), cdc.newLog.Version) + }) + t.Run("in case of multiple logs, takes the latest", func(t *testing.T) { + lp.logsWithSigsErr = nil + lp.logsWithSigs = []logpoller.Log{ + makeLog(t, donID, uint32(42), "http://example.com/xxx.json", [32]byte{1, 2, 3, 4}), + makeLog(t, donID, uint32(45), "http://example.com/xxx2.json", [32]byte{2, 2, 3, 4}), + makeLog(t, donID, uint32(44), "http://example.com/xxx3.json", [32]byte{3, 2, 3, 4}), + makeLog(t, donID, uint32(43), "http://example.com/xxx4.json", [32]byte{4, 2, 3, 4}), + } + + err := cdc.readLogs() + require.NoError(t, err) + assert.Equal(t, uint32(45), cdc.newLog.Version) + assert.Equal(t, "http://example.com/xxx2.json", cdc.newLog.Url) + assert.Equal(t, [32]byte{2, 2, 3, 4}, cdc.newLog.Sha) + assert.Equal(t, int64(donID), cdc.newLog.DonId.Int64()) + + func() { + for { + select { + case log := <-newLogCh: + assert.Equal(t, cdc.newLog, log) + default: + return + } + } + }() + }) + t.Run("ignores logs with incorrect don ID", func(t *testing.T) { + lp.logsWithSigsErr = nil + lp.logsWithSigs = []logpoller.Log{ + makeLog(t, donID+1, uint32(42), "http://example.com/xxx.json", [32]byte{1, 2, 3, 4}), + } + + err := cdc.readLogs() + require.NoError(t, err) + assert.Equal(t, uint32(45), cdc.newLog.Version) + + func() { + for { + select { + case log := <-newLogCh: + t.Fatal("did not expect log with wrong donID, got: ", log) + default: + return + } + } + }() + }) + t.Run("ignores logs with wrong number of topics", func(t *testing.T) { + lp.logsWithSigsErr = nil + lg := makeLog(t, donID, uint32(42), "http://example.com/xxx.json", [32]byte{1, 2, 3, 4}) + lg.Topics = lg.Topics[:1] + lp.logsWithSigs = []logpoller.Log{lg} + + err := cdc.readLogs() + require.NoError(t, err) + assert.Equal(t, uint32(45), cdc.newLog.Version) + + func() { + for { + select { + case log := <-newLogCh: + t.Fatal("did not expect log with missing topics, got: ", log) + default: + return + } + } + }() + }) + }) + + t.Run("fetchChannelDefinitions", func(t *testing.T) { + c := &mockHTTPClient{} + cdc := &channelDefinitionCache{ + lggr: logger.TestSugared(t), + client: c, + httpLimit: 2048, + } + + t.Run("nil ctx returns error", func(t *testing.T) { + _, err := cdc.fetchChannelDefinitions(nil, "notvalid://foos", [32]byte{}) + assert.EqualError(t, err, "failed to create http.Request; net/http: nil Context") + }) + + t.Run("networking error while making request returns error", func(t *testing.T) { + c.resp = nil + c.err = errors.New("http request failed") + + _, err := cdc.fetchChannelDefinitions(ctx, "http://example.com/definitions.json", [32]byte{}) + assert.EqualError(t, err, "error making http request: http request failed") + }) + + t.Run("server returns 500 returns error", func(t *testing.T) { + c.err = nil + c.resp = &http.Response{StatusCode: 500, Body: io.NopCloser(bytes.NewReader([]byte{1, 2, 3}))} + + _, err := cdc.fetchChannelDefinitions(ctx, "http://example.com/definitions.json", [32]byte{}) + assert.EqualError(t, err, "got error from http://example.com/definitions.json: (status code: 500, response body: \x01\x02\x03)") + }) + + var largeBody = make([]byte, 2048) + for i := range largeBody { + largeBody[i] = 'a' + } + + t.Run("server returns 404 returns error (and does not log entirety of huge response body)", func(t *testing.T) { + c.err = nil + c.resp = &http.Response{StatusCode: 404, Body: io.NopCloser(bytes.NewReader(largeBody))} + + _, err := cdc.fetchChannelDefinitions(ctx, "http://example.com/definitions.json", [32]byte{}) + assert.EqualError(t, err, "got error from http://example.com/definitions.json: (status code: 404, error reading response body: http: request body too large, response body: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)") + }) + + var hugeBody = make([]byte, 8096) + c.resp.Body = io.NopCloser(bytes.NewReader(hugeBody)) + + t.Run("server returns body that is too large", func(t *testing.T) { + c.err = nil + c.resp = &http.Response{StatusCode: 200, Body: io.NopCloser(bytes.NewReader(hugeBody))} + + _, err := cdc.fetchChannelDefinitions(ctx, "http://example.com/definitions.json", [32]byte{}) + assert.EqualError(t, err, "failed to read from body: http: request body too large") + }) + + t.Run("server returns invalid JSON returns error", func(t *testing.T) { + c.err = nil + c.resp = &http.Response{StatusCode: 200, Body: io.NopCloser(bytes.NewReader([]byte{1, 2, 3}))} + + _, err := cdc.fetchChannelDefinitions(ctx, "http://example.com/definitions.json", common.HexToHash("0xfd1780a6fc9ee0dab26ceb4b3941ab03e66ccd970d1db91612c66df4515b0a0a")) + assert.EqualError(t, err, "failed to decode JSON: invalid character '\\x01' looking for beginning of value") + }) + + t.Run("SHA mismatch returns error", func(t *testing.T) { + c.err = nil + c.resp = &http.Response{StatusCode: 200, Body: io.NopCloser(bytes.NewReader([]byte(`{"foo":"bar"}`)))} + + _, err := cdc.fetchChannelDefinitions(ctx, "http://example.com/definitions.json", [32]byte{}) + assert.EqualError(t, err, "SHA3 mismatch: expected 0000000000000000000000000000000000000000000000000000000000000000, got 4d3304d0d87c27a031cbb6bdf95da79b7b4552c3d0bef2e5a94f50810121e1e0") + }) + + t.Run("valid JSON matching SHA returns channel definitions", func(t *testing.T) { + chainSelector := 4949039107694359620 // arbitrum mainnet + feedID := [32]byte{00, 03, 107, 74, 167, 229, 124, 167, 182, 138, 225, 191, 69, 101, 63, 86, 182, 86, 253, 58, 163, 53, 239, 127, 174, 105, 107, 102, 63, 27, 132, 114} + expirationWindow := 3600 + multiplier := big.NewInt(1e18) + baseUSDFee := 10 + valid := fmt.Sprintf(` +{ + "42": { + "reportFormat": %d, + "chainSelector": %d, + "streams": [{"streamId": 52, "aggregator": %d}, {"streamId": 53, "aggregator": %d}, {"streamId": 55, "aggregator": %d}], + "opts": { + "feedId": "0x%x", + "expirationWindow": %d, + "multiplier": "%s", + "baseUSDFee": "%d" + } + } +}`, llotypes.ReportFormatEVMPremiumLegacy, chainSelector, llotypes.AggregatorMedian, llotypes.AggregatorMedian, llotypes.AggregatorQuote, feedID, expirationWindow, multiplier.String(), baseUSDFee) + + c.err = nil + c.resp = &http.Response{StatusCode: 200, Body: io.NopCloser(bytes.NewReader([]byte(valid)))} + + cd, err := cdc.fetchChannelDefinitions(ctx, "http://example.com/definitions.json", common.HexToHash("0x367bbc75f7b6c9fc66a98ea99f837ea7ac4a3c2d6a9ee284de018bd02c41b52d")) + assert.NoError(t, err) + assert.Equal(t, llotypes.ChannelDefinitions(llotypes.ChannelDefinitions{0x2a: llotypes.ChannelDefinition{ReportFormat: 0x1, Streams: []llotypes.Stream{llotypes.Stream{StreamID: 0x34, Aggregator: 0x1}, llotypes.Stream{StreamID: 0x35, Aggregator: 0x1}, llotypes.Stream{StreamID: 0x37, Aggregator: 0x3}}, Opts: llotypes.ChannelOpts{0x7b, 0x22, 0x62, 0x61, 0x73, 0x65, 0x55, 0x53, 0x44, 0x46, 0x65, 0x65, 0x22, 0x3a, 0x22, 0x31, 0x30, 0x22, 0x2c, 0x22, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x22, 0x3a, 0x33, 0x36, 0x30, 0x30, 0x2c, 0x22, 0x66, 0x65, 0x65, 0x64, 0x49, 0x64, 0x22, 0x3a, 0x22, 0x30, 0x78, 0x30, 0x30, 0x30, 0x33, 0x36, 0x62, 0x34, 0x61, 0x61, 0x37, 0x65, 0x35, 0x37, 0x63, 0x61, 0x37, 0x62, 0x36, 0x38, 0x61, 0x65, 0x31, 0x62, 0x66, 0x34, 0x35, 0x36, 0x35, 0x33, 0x66, 0x35, 0x36, 0x62, 0x36, 0x35, 0x36, 0x66, 0x64, 0x33, 0x61, 0x61, 0x33, 0x33, 0x35, 0x65, 0x66, 0x37, 0x66, 0x61, 0x65, 0x36, 0x39, 0x36, 0x62, 0x36, 0x36, 0x33, 0x66, 0x31, 0x62, 0x38, 0x34, 0x37, 0x32, 0x22, 0x2c, 0x22, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x69, 0x65, 0x72, 0x22, 0x3a, 0x22, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x22, 0x7d}}}), cd) + }) + }) + + t.Run("persist", func(t *testing.T) { + cdc := &channelDefinitionCache{ + lggr: logger.TestSugared(t), + orm: nil, + addr: testutils.NewAddress(), + donID: donID, + definitions: llotypes.ChannelDefinitions{ + 1: { + ReportFormat: llotypes.ReportFormat(43), + Streams: []llotypes.Stream{{StreamID: 1, Aggregator: llotypes.AggregatorMedian}, {StreamID: 2, Aggregator: llotypes.AggregatorMode}, {StreamID: 3, Aggregator: llotypes.AggregatorQuote}}, + Opts: llotypes.ChannelOpts{1, 2, 3}, + }, + }, + definitionsBlockNum: 142, + } + + t.Run("does nothing if persisted version is up-to-date", func(t *testing.T) { + cdc.definitionsVersion = 42 + cdc.persistedVersion = 42 + + memoryVersion, persistedVersion, err := cdc.persist(ctx) + assert.NoError(t, err) + assert.Equal(t, uint32(42), memoryVersion) + assert.Equal(t, uint32(42), persistedVersion) + assert.Equal(t, uint32(42), cdc.persistedVersion) + }) + + orm := &mockORM{} + cdc.orm = orm + + t.Run("returns error on db failure and does not update persisted version", func(t *testing.T) { + cdc.persistedVersion = 42 + cdc.definitionsVersion = 43 + orm.err = errors.New("test error") + + memoryVersion, persistedVersion, err := cdc.persist(ctx) + assert.EqualError(t, err, "test error") + assert.Equal(t, uint32(43), memoryVersion) + assert.Equal(t, uint32(42), persistedVersion) + assert.Equal(t, uint32(42), cdc.persistedVersion) + }) + + t.Run("updates persisted version on success", func(t *testing.T) { + cdc.definitionsVersion = 43 + orm.err = nil + + memoryVersion, persistedVersion, err := cdc.persist(ctx) + assert.NoError(t, err) + assert.Equal(t, uint32(43), memoryVersion) + assert.Equal(t, uint32(43), persistedVersion) + assert.Equal(t, uint32(43), cdc.persistedVersion) + + assert.Equal(t, cdc.addr, orm.lastPersistedAddr) + assert.Equal(t, cdc.donID, orm.lastPersistedDonID) + assert.Equal(t, cdc.persistedVersion, orm.lastPersistedVersion) + assert.Equal(t, cdc.definitions, orm.lastPersistedDfns) + assert.Equal(t, cdc.definitionsBlockNum, orm.lastPersistedBlockNum) + }) + }) } diff --git a/core/services/llo/onchain_config.go b/core/services/llo/onchain_config.go deleted file mode 100644 index 7b5cfffaa9..0000000000 --- a/core/services/llo/onchain_config.go +++ /dev/null @@ -1,21 +0,0 @@ -package llo - -type OnchainConfig struct{} - -type OnchainConfigCodec interface { - Encode(OnchainConfig) ([]byte, error) - Decode([]byte) (OnchainConfig, error) -} - -var _ OnchainConfigCodec = &JSONOnchainConfigCodec{} - -// TODO: Replace this with protobuf, if it is actually used for something -type JSONOnchainConfigCodec struct{} - -func (c *JSONOnchainConfigCodec) Encode(OnchainConfig) ([]byte, error) { - return nil, nil -} - -func (c *JSONOnchainConfigCodec) Decode([]byte) (OnchainConfig, error) { - return OnchainConfig{}, nil -} diff --git a/core/services/llo/orm.go b/core/services/llo/orm.go index 6b14e54326..acf6e8c721 100644 --- a/core/services/llo/orm.go +++ b/core/services/llo/orm.go @@ -3,10 +3,9 @@ package llo import ( "context" "database/sql" - "encoding/json" "errors" "fmt" - "math/big" + "time" "github.com/ethereum/go-ethereum/common" @@ -18,46 +17,50 @@ type ORM interface { ChannelDefinitionCacheORM } +type PersistedDefinitions struct { + ChainSelector uint64 `db:"chain_selector"` + Address common.Address `db:"addr"` + Definitions llotypes.ChannelDefinitions `db:"definitions"` + // The block number in which the log for this definitions was emitted + BlockNum int64 `db:"block_num"` + DonID uint32 `db:"don_id"` + Version uint32 `db:"version"` + UpdatedAt time.Time `db:"updated_at"` +} + var _ ORM = &orm{} type orm struct { - ds sqlutil.DataSource - evmChainID *big.Int + ds sqlutil.DataSource + chainSelector uint64 } -func NewORM(ds sqlutil.DataSource, evmChainID *big.Int) ORM { - return &orm{ds, evmChainID} +func NewORM(ds sqlutil.DataSource, chainSelector uint64) ORM { + return &orm{ds, chainSelector} } -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.ds.GetContext(ctx, &scanned, "SELECT definitions, block_num FROM channel_definitions WHERE evm_chain_id = $1 AND addr = $2", o.evmChainID.String(), addr) +func (o *orm) LoadChannelDefinitions(ctx context.Context, addr common.Address, donID uint32) (pd *PersistedDefinitions, err error) { + pd = new(PersistedDefinitions) + err = o.ds.GetContext(ctx, pd, "SELECT * FROM channel_definitions WHERE chain_selector = $1 AND addr = $2 AND don_id = $3", o.chainSelector, addr, donID) if errors.Is(err, sql.ErrNoRows) { - return dfns, blockNum, nil + return nil, 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 nil, fmt.Errorf("failed to LoadChannelDefinitions; %w", err) } - return dfns, scanned.BlockNum, nil + return pd, 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 { +// StoreChannelDefinitions will store a ChannelDefinitions list for a given chain_selector, addr, don_id +// It only updates if the new version is greater than the existing record +func (o *orm) StoreChannelDefinitions(ctx context.Context, addr common.Address, donID, version uint32, dfns llotypes.ChannelDefinitions, blockNum int64) error { _, err := o.ds.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) +INSERT INTO channel_definitions (chain_selector, addr, don_id, definitions, block_num, version, updated_at) +VALUES ($1, $2, $3, $4, $5, $6, NOW()) +ON CONFLICT (chain_selector, addr, don_id) DO UPDATE +SET definitions = $4, block_num = $5, version = $6, updated_at = NOW() +WHERE EXCLUDED.version > channel_definitions.version +`, o.chainSelector, addr, donID, dfns, blockNum, version) if err != nil { return fmt.Errorf("StoreChannelDefinitions failed: %w", err) } diff --git a/core/services/llo/orm_test.go b/core/services/llo/orm_test.go index bc2d88130e..ec3c06e6e6 100644 --- a/core/services/llo/orm_test.go +++ b/core/services/llo/orm_test.go @@ -1,91 +1,156 @@ 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) { - t.Skip("waiting on https://github.com/smartcontractkit/chainlink/pull/13780") - // 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) - // }) - // }) + const ETHMainnetChainSelector uint64 = 5009297550715157269 + const OtherChainSelector uint64 = 1234567890 + + db := pgtest.NewSqlxDB(t) + orm := NewORM(db, ETHMainnetChainSelector) + ctx := testutils.Context(t) + + addr1 := testutils.NewAddress() + addr2 := testutils.NewAddress() + addr3 := testutils.NewAddress() + + donID1 := uint32(1) + donID2 := uint32(2) + + t.Run("LoadChannelDefinitions", func(t *testing.T) { + t.Run("returns zero values if nothing in database", func(t *testing.T) { + pd, err := orm.LoadChannelDefinitions(ctx, addr1, donID1) + assert.NoError(t, err) + assert.Nil(t, pd) + }) + t.Run("loads channel definitions from database for the given don ID", func(t *testing.T) { + expectedBlockNum := rand.Int63() + expectedBlockNum2 := rand.Int63() + cid1 := rand.Uint32() + cid2 := rand.Uint32() + + channelDefsJSON := fmt.Sprintf(` +{ + "%d": { + "reportFormat": 42, + "chainSelector": 142, + "streams": [{"streamId": 1, "aggregator": "median"}, {"streamId": 2, "aggregator": "mode"}], + "opts": {"foo":"bar"} + }, + "%d": { + "reportFormat": 43, + "chainSelector": 142, + "streams": [{"streamId": 1, "aggregator": "median"}, {"streamId": 3, "aggregator": "quote"}] + } +} + `, cid1, cid2) + pgtest.MustExec(t, db, ` + INSERT INTO channel_definitions(addr, chain_selector, don_id, definitions, block_num, version, updated_at) + VALUES ($1, $2, $3, $4, $5, $6, NOW()) + `, addr1, ETHMainnetChainSelector, 1, channelDefsJSON, expectedBlockNum, 1) + + pgtest.MustExec(t, db, ` + INSERT INTO channel_definitions(addr, chain_selector, don_id, definitions, block_num, version, updated_at) + VALUES ($1, $2, $3, $4, $5, $6, NOW()) + `, addr2, ETHMainnetChainSelector, 1, `{}`, expectedBlockNum2, 1) + + { + // alternative chain selector; we expect these ones to be ignored + pgtest.MustExec(t, db, ` + INSERT INTO channel_definitions(addr, chain_selector, don_id, definitions, block_num, version, updated_at) + VALUES ($1, $2, $3, $4, $5, $6, NOW()) + `, addr1, OtherChainSelector, 1, channelDefsJSON, expectedBlockNum, 1) + pgtest.MustExec(t, db, ` + INSERT INTO channel_definitions(addr, chain_selector, don_id, definitions, block_num, version, updated_at) + VALUES ($1, $2, $3, $4, $5, $6, NOW()) + `, addr3, OtherChainSelector, 1, channelDefsJSON, expectedBlockNum, 1) + } + + pd, err := orm.LoadChannelDefinitions(ctx, addr1, donID1) + require.NoError(t, err) + + assert.Equal(t, ETHMainnetChainSelector, pd.ChainSelector) + assert.Equal(t, addr1, pd.Address) + assert.Equal(t, expectedBlockNum, pd.BlockNum) + assert.Equal(t, donID1, pd.DonID) + assert.Equal(t, uint32(1), pd.Version) + assert.Equal(t, llotypes.ChannelDefinitions{ + cid1: llotypes.ChannelDefinition{ + ReportFormat: 42, + Streams: []llotypes.Stream{{StreamID: 1, Aggregator: llotypes.AggregatorMedian}, {StreamID: 2, Aggregator: llotypes.AggregatorMode}}, + Opts: []byte(`{"foo":"bar"}`), + }, + cid2: llotypes.ChannelDefinition{ + ReportFormat: 43, + Streams: []llotypes.Stream{{StreamID: 1, Aggregator: llotypes.AggregatorMedian}, {StreamID: 3, Aggregator: llotypes.AggregatorQuote}}, + }, + }, pd.Definitions) + + // does not load erroneously for a different address + pd, err = orm.LoadChannelDefinitions(ctx, addr2, donID1) + require.NoError(t, err) + + assert.Equal(t, llotypes.ChannelDefinitions{}, pd.Definitions) + assert.Equal(t, expectedBlockNum2, pd.BlockNum) + + // does not load erroneously for a different don ID + pd, err = orm.LoadChannelDefinitions(ctx, addr1, donID2) + require.NoError(t, err) + + assert.Equal(t, (*PersistedDefinitions)(nil), pd) + }) + }) + + t.Run("StoreChannelDefinitions", func(t *testing.T) { + expectedBlockNum := rand.Int63() + cid1 := rand.Uint32() + cid2 := rand.Uint32() + defs := llotypes.ChannelDefinitions{ + cid1: llotypes.ChannelDefinition{ + ReportFormat: llotypes.ReportFormatJSON, + Streams: []llotypes.Stream{{StreamID: 1, Aggregator: llotypes.AggregatorMedian}, {StreamID: 2, Aggregator: llotypes.AggregatorMode}}, + Opts: []byte(`{"foo":"bar"}`), + }, + cid2: llotypes.ChannelDefinition{ + ReportFormat: llotypes.ReportFormatEVMPremiumLegacy, + Streams: []llotypes.Stream{{StreamID: 1, Aggregator: llotypes.AggregatorMedian}, {StreamID: 3, Aggregator: llotypes.AggregatorQuote}}, + }, + } + + t.Run("stores channel definitions in the database", func(t *testing.T) { + err := orm.StoreChannelDefinitions(ctx, addr1, donID1, 42, defs, expectedBlockNum) + require.NoError(t, err) + + pd, err := orm.LoadChannelDefinitions(ctx, addr1, donID1) + require.NoError(t, err) + assert.Equal(t, ETHMainnetChainSelector, pd.ChainSelector) + assert.Equal(t, addr1, pd.Address) + assert.Equal(t, expectedBlockNum, pd.BlockNum) + assert.Equal(t, donID1, pd.DonID) + assert.Equal(t, uint32(42), pd.Version) + assert.Equal(t, defs, pd.Definitions) + }) + t.Run("does not update if version is older than the database persisted version", func(t *testing.T) { + // try to update with an older version + err := orm.StoreChannelDefinitions(ctx, addr1, donID1, 41, llotypes.ChannelDefinitions{}, expectedBlockNum) + require.NoError(t, err) + + pd, err := orm.LoadChannelDefinitions(ctx, addr1, donID1) + require.NoError(t, err) + assert.Equal(t, uint32(42), pd.Version) + assert.Equal(t, defs, pd.Definitions) + }) + }) } diff --git a/core/services/llo/static_channel_definitions_cache.go b/core/services/llo/static_channel_definitions_cache.go index 98ef3642cb..980625bd59 100644 --- a/core/services/llo/static_channel_definitions_cache.go +++ b/core/services/llo/static_channel_definitions_cache.go @@ -7,7 +7,7 @@ import ( "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-common/pkg/logger" ) // A CDC that loads a static JSON of channel definitions; useful for @@ -27,7 +27,7 @@ func NewStaticChannelDefinitionCache(lggr logger.Logger, dfnstr string) (llotype if err := json.Unmarshal([]byte(dfnstr), &definitions); err != nil { return nil, err } - return &staticCDC{services.StateMachine{}, lggr.Named("StaticChannelDefinitionCache"), definitions}, nil + return &staticCDC{services.StateMachine{}, logger.Named(lggr, "StaticChannelDefinitionCache"), definitions}, nil } func (s *staticCDC) Start(context.Context) error { diff --git a/core/services/llo/transmitter.go b/core/services/llo/transmitter.go index b8cfb86de3..fe6f26c317 100644 --- a/core/services/llo/transmitter.go +++ b/core/services/llo/transmitter.go @@ -3,19 +3,19 @@ package llo import ( "context" "crypto/ed25519" - "errors" "fmt" - "github.com/ethereum/go-ethereum/accounts/abi" "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/logger" "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/llo/evm" "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 @@ -34,25 +34,6 @@ const ( // 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 @@ -97,7 +78,32 @@ func (t *transmitter) Transmit( report ocr3types.ReportWithInfo[llotypes.ReportInfo], sigs []types.AttributedOnchainSignature, ) (err error) { - return errors.New("not implemented") + var payload []byte + + switch report.Info.ReportFormat { + case llotypes.ReportFormatJSON: + // TODO: exactly how to handle JSON here? + // https://smartcontract-it.atlassian.net/browse/MERC-3659 + fallthrough + case llotypes.ReportFormatEVMPremiumLegacy: + payload, err = evm.ReportCodecPremiumLegacy{}.Pack(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 } // FromAccount returns the stringified (hex) CSA public key diff --git a/core/services/nurse.go b/core/services/nurse.go index a9069b5181..7f3cad13e7 100644 --- a/core/services/nurse.go +++ b/core/services/nurse.go @@ -3,6 +3,7 @@ package services import ( "bytes" "compress/gzip" + "context" "fmt" "io/fs" "os" @@ -19,22 +20,21 @@ import ( commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/timeutil" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) type Nurse struct { - services.StateMachine + services.Service + eng *services.Engine cfg Config - log logger.Logger checks map[string]CheckFunc checksMu sync.RWMutex chGather chan gatherRequest - chStop chan struct{} - wgDone sync.WaitGroup } type Config interface { @@ -66,85 +66,63 @@ const ( ) func NewNurse(cfg Config, log logger.Logger) *Nurse { - return &Nurse{ + n := &Nurse{ cfg: cfg, - log: log.Named("Nurse"), checks: make(map[string]CheckFunc), chGather: make(chan gatherRequest, 1), - chStop: make(chan struct{}), } + n.Service, n.eng = services.Config{ + Name: "Nurse", + Start: n.start, + }.NewServiceEngine(log) + + return n } -func (n *Nurse) Start() error { - return n.StartOnce("Nurse", func() error { - // This must be set *once*, and it must occur as early as possible - if n.cfg.MemProfileRate() != runtime.MemProfileRate { - runtime.MemProfileRate = n.cfg.BlockProfileRate() - } +func (n *Nurse) start(_ context.Context) error { + // This must be set *once*, and it must occur as early as possible + if n.cfg.MemProfileRate() != runtime.MemProfileRate { + runtime.MemProfileRate = n.cfg.BlockProfileRate() + } - n.log.Debugf("Starting nurse with config %+v", n.cfg) - runtime.SetCPUProfileRate(n.cfg.CPUProfileRate()) - runtime.SetBlockProfileRate(n.cfg.BlockProfileRate()) - runtime.SetMutexProfileFraction(n.cfg.MutexProfileFraction()) + n.eng.Debugf("Starting nurse with config %+v", n.cfg) + runtime.SetCPUProfileRate(n.cfg.CPUProfileRate()) + runtime.SetBlockProfileRate(n.cfg.BlockProfileRate()) + runtime.SetMutexProfileFraction(n.cfg.MutexProfileFraction()) - err := utils.EnsureDirAndMaxPerms(n.cfg.ProfileRoot(), 0744) - if err != nil { - return err - } + err := utils.EnsureDirAndMaxPerms(n.cfg.ProfileRoot(), 0744) + if err != nil { + return err + } - n.AddCheck("mem", n.checkMem) - n.AddCheck("goroutines", n.checkGoroutines) - - n.wgDone.Add(1) - // Checker - go func() { - defer n.wgDone.Done() - for { - select { - case <-n.chStop: - return - case <-time.After(n.cfg.PollInterval().Duration()): - } - - func() { - n.checksMu.RLock() - defer n.checksMu.RUnlock() - for reason, checkFunc := range n.checks { - if unwell, meta := checkFunc(); unwell { - n.GatherVitals(reason, meta) - break - } - } - }() - } - }() - - n.wgDone.Add(1) - // Responder - go func() { - defer n.wgDone.Done() - for { - select { - case <-n.chStop: - return - case req := <-n.chGather: - n.gatherVitals(req.reason, req.meta) - } - } - }() + n.AddCheck("mem", n.checkMem) + n.AddCheck("goroutines", n.checkGoroutines) - return nil + // Checker + n.eng.GoTick(timeutil.NewTicker(n.cfg.PollInterval().Duration), func(ctx context.Context) { + n.checksMu.RLock() + defer n.checksMu.RUnlock() + for reason, checkFunc := range n.checks { + if unwell, meta := checkFunc(); unwell { + n.GatherVitals(ctx, reason, meta) + break + } + } }) -} -func (n *Nurse) Close() error { - return n.StopOnce("Nurse", func() error { - n.log.Debug("Nurse closing...") - defer n.log.Debug("Nurse closed") - close(n.chStop) - n.wgDone.Wait() - return nil + // Responder + n.eng.Go(func(ctx context.Context) { + for { + select { + case <-ctx.Done(): + return + case req := <-n.chGather: + n.gatherVitals(req.reason, req.meta) + } + } }) + + return nil } func (n *Nurse) AddCheck(reason string, checkFunc CheckFunc) { @@ -153,9 +131,9 @@ func (n *Nurse) AddCheck(reason string, checkFunc CheckFunc) { n.checks[reason] = checkFunc } -func (n *Nurse) GatherVitals(reason string, meta Meta) { +func (n *Nurse) GatherVitals(ctx context.Context, reason string, meta Meta) { select { - case <-n.chStop: + case <-ctx.Done(): case n.chGather <- gatherRequest{reason, meta}: default: } @@ -189,14 +167,14 @@ func (n *Nurse) checkGoroutines() (bool, Meta) { func (n *Nurse) gatherVitals(reason string, meta Meta) { loggerFields := (logger.Fields{"reason": reason}).Merge(logger.Fields(meta)) - n.log.Debugw("Nurse is gathering vitals", loggerFields.Slice()...) + n.eng.Debugw("Nurse is gathering vitals", loggerFields.Slice()...) size, err := n.totalProfileBytes() if err != nil { - n.log.Errorw("could not fetch total profile bytes", loggerFields.With("err", err).Slice()...) + n.eng.Errorw("could not fetch total profile bytes", loggerFields.With("err", err).Slice()...) return } else if size >= uint64(n.cfg.MaxProfileSize()) { - n.log.Warnw("cannot write pprof profile, total profile size exceeds configured PPROF_MAX_PROFILE_SIZE", + n.eng.Warnw("cannot write pprof profile, total profile size exceeds configured PPROF_MAX_PROFILE_SIZE", loggerFields.With("total", size, "max", n.cfg.MaxProfileSize()).Slice()..., ) return @@ -206,7 +184,7 @@ func (n *Nurse) gatherVitals(reason string, meta Meta) { err = n.appendLog(now, reason, meta) if err != nil { - n.log.Warnw("cannot write pprof profile", loggerFields.With("err", err).Slice()...) + n.eng.Warnw("cannot write pprof profile", loggerFields.With("err", err).Slice()...) return } var wg sync.WaitGroup @@ -227,7 +205,7 @@ func (n *Nurse) gatherVitals(reason string, meta Meta) { wg.Add(1) go n.gather("heap", now, &wg) } else { - n.log.Info("skipping heap collection because runtime.MemProfileRate = 0") + n.eng.Info("skipping heap collection because runtime.MemProfileRate = 0") } wg.Add(1) @@ -236,15 +214,13 @@ func (n *Nurse) gatherVitals(reason string, meta Meta) { go n.gather("threadcreate", now, &wg) ch := make(chan struct{}) - n.wgDone.Add(1) - go func() { - defer n.wgDone.Done() + n.eng.Go(func(ctx context.Context) { defer close(ch) wg.Wait() - }() + }) select { - case <-n.chStop: + case <-n.eng.StopChan: case <-ch: } } @@ -252,7 +228,7 @@ func (n *Nurse) gatherVitals(reason string, meta Meta) { func (n *Nurse) appendLog(now time.Time, reason string, meta Meta) error { filename := filepath.Join(n.cfg.ProfileRoot(), "nurse.log") - n.log.Debugf("creating nurse log %s", filename) + n.eng.Debugf("creating nurse log %s", filename) file, err := os.Create(filename) if err != nil { @@ -288,34 +264,34 @@ func (n *Nurse) appendLog(now time.Time, reason string, meta Meta) error { func (n *Nurse) gatherCPU(now time.Time, wg *sync.WaitGroup) { defer wg.Done() - n.log.Debugf("gather cpu %d ...", now.UnixMicro()) - defer n.log.Debugf("gather cpu %d done", now.UnixMicro()) + n.eng.Debugf("gather cpu %d ...", now.UnixMicro()) + defer n.eng.Debugf("gather cpu %d done", now.UnixMicro()) wc, err := n.createFile(now, cpuProfName, false) if err != nil { - n.log.Errorw("could not write cpu profile", "err", err) + n.eng.Errorw("could not write cpu profile", "err", err) return } defer wc.Close() err = pprof.StartCPUProfile(wc) if err != nil { - n.log.Errorw("could not start cpu profile", "err", err) + n.eng.Errorw("could not start cpu profile", "err", err) return } select { - case <-n.chStop: - n.log.Debug("gather cpu received stop") + case <-n.eng.StopChan: + n.eng.Debug("gather cpu received stop") case <-time.After(n.cfg.GatherDuration().Duration()): - n.log.Debugf("gather cpu duration elapsed %s. stoping profiling.", n.cfg.GatherDuration().Duration().String()) + n.eng.Debugf("gather cpu duration elapsed %s. stoping profiling.", n.cfg.GatherDuration().Duration().String()) } pprof.StopCPUProfile() err = wc.Close() if err != nil { - n.log.Errorw("could not close cpu profile", "err", err) + n.eng.Errorw("could not close cpu profile", "err", err) return } } @@ -323,23 +299,23 @@ func (n *Nurse) gatherCPU(now time.Time, wg *sync.WaitGroup) { func (n *Nurse) gatherTrace(now time.Time, wg *sync.WaitGroup) { defer wg.Done() - n.log.Debugf("gather trace %d ...", now.UnixMicro()) - defer n.log.Debugf("gather trace %d done", now.UnixMicro()) + n.eng.Debugf("gather trace %d ...", now.UnixMicro()) + defer n.eng.Debugf("gather trace %d done", now.UnixMicro()) wc, err := n.createFile(now, traceProfName, true) if err != nil { - n.log.Errorw("could not write trace profile", "err", err) + n.eng.Errorw("could not write trace profile", "err", err) return } defer wc.Close() err = trace.Start(wc) if err != nil { - n.log.Errorw("could not start trace profile", "err", err) + n.eng.Errorw("could not start trace profile", "err", err) return } select { - case <-n.chStop: + case <-n.eng.StopChan: case <-time.After(n.cfg.GatherTraceDuration().Duration()): } @@ -347,7 +323,7 @@ func (n *Nurse) gatherTrace(now time.Time, wg *sync.WaitGroup) { err = wc.Close() if err != nil { - n.log.Errorw("could not close trace profile", "err", err) + n.eng.Errorw("could not close trace profile", "err", err) return } } @@ -355,18 +331,18 @@ func (n *Nurse) gatherTrace(now time.Time, wg *sync.WaitGroup) { func (n *Nurse) gather(typ string, now time.Time, wg *sync.WaitGroup) { defer wg.Done() - n.log.Debugf("gather %s %d ...", typ, now.UnixMicro()) - n.log.Debugf("gather %s %d done", typ, now.UnixMicro()) + n.eng.Debugf("gather %s %d ...", typ, now.UnixMicro()) + n.eng.Debugf("gather %s %d done", typ, now.UnixMicro()) p := pprof.Lookup(typ) if p == nil { - n.log.Errorf("Invariant violation: pprof type '%v' does not exist", typ) + n.eng.Errorf("Invariant violation: pprof type '%v' does not exist", typ) return } p0, err := collectProfile(p) if err != nil { - n.log.Errorw(fmt.Sprintf("could not collect %v profile", typ), "err", err) + n.eng.Errorw(fmt.Sprintf("could not collect %v profile", typ), "err", err) return } @@ -374,14 +350,14 @@ func (n *Nurse) gather(typ string, now time.Time, wg *sync.WaitGroup) { defer t.Stop() select { - case <-n.chStop: + case <-n.eng.StopChan: return case <-t.C: } p1, err := collectProfile(p) if err != nil { - n.log.Errorw(fmt.Sprintf("could not collect %v profile", typ), "err", err) + n.eng.Errorw(fmt.Sprintf("could not collect %v profile", typ), "err", err) return } ts := p1.TimeNanos @@ -391,7 +367,7 @@ func (n *Nurse) gather(typ string, now time.Time, wg *sync.WaitGroup) { p1, err = profile.Merge([]*profile.Profile{p0, p1}) if err != nil { - n.log.Errorw(fmt.Sprintf("could not compute delta for %v profile", typ), "err", err) + n.eng.Errorw(fmt.Sprintf("could not compute delta for %v profile", typ), "err", err) return } @@ -400,19 +376,19 @@ func (n *Nurse) gather(typ string, now time.Time, wg *sync.WaitGroup) { wc, err := n.createFile(now, typ, false) if err != nil { - n.log.Errorw(fmt.Sprintf("could not write %v profile", typ), "err", err) + n.eng.Errorw(fmt.Sprintf("could not write %v profile", typ), "err", err) return } defer wc.Close() err = p1.Write(wc) if err != nil { - n.log.Errorw(fmt.Sprintf("could not write %v profile", typ), "err", err) + n.eng.Errorw(fmt.Sprintf("could not write %v profile", typ), "err", err) return } err = wc.Close() if err != nil { - n.log.Errorw(fmt.Sprintf("could not close file for %v profile", typ), "err", err) + n.eng.Errorw(fmt.Sprintf("could not close file for %v profile", typ), "err", err) return } } @@ -437,7 +413,7 @@ func (n *Nurse) createFile(now time.Time, typ string, shouldGzip bool) (*utils.D filename += ".gz" } fullpath := filepath.Join(n.cfg.ProfileRoot(), filename) - n.log.Debugf("creating file %s", fullpath) + n.eng.Debugf("creating file %s", fullpath) file, err := os.Create(fullpath) if err != nil { diff --git a/core/services/nurse_test.go b/core/services/nurse_test.go index 4597eeb456..ed6f6872dc 100644 --- a/core/services/nurse_test.go +++ b/core/services/nurse_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/require" commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -102,7 +103,7 @@ func TestNurse(t *testing.T) { nrse := NewNurse(newMockConfig(t), l) nrse.AddCheck("test", func() (bool, Meta) { return true, Meta{} }) - require.NoError(t, nrse.Start()) + require.NoError(t, nrse.Start(tests.Context(t))) defer func() { require.NoError(t, nrse.Close()) }() require.NoError(t, nrse.appendLog(time.Now(), "test", Meta{})) diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 699c552030..f6410c5b28 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -47,7 +47,6 @@ 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/ccip" @@ -775,7 +774,7 @@ func (d *Delegate) newServicesGenericPlugin( } keyBundles[name] = os } - onchainKeyringAdapter, err = ocrcommon.NewOCR3OnchainKeyringMultiChainAdapter(keyBundles, lggr) + onchainKeyringAdapter, err = ocrcommon.NewOCR3OnchainKeyringMultiChainAdapter(keyBundles, kb.PublicKey(), lggr) if err != nil { return nil, err } @@ -956,6 +955,7 @@ func (d *Delegate) newServicesLLO( return nil, err } + // Handle key bundle IDs explicitly specified in job spec kbm := make(map[llotypes.ReportFormat]llo.Key) for rfStr, kbid := range pluginCfg.KeyBundleIDs { k, err3 := d.ks.Get(kbid) @@ -968,26 +968,19 @@ func (d *Delegate) newServicesLLO( } 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 - // + + // Use the default key bundle if not specified + // NOTE: Only JSON and EVMPremiumLegacy supported for now // 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) - } + for _, rf := range []llotypes.ReportFormat{llotypes.ReportFormatJSON, llotypes.ReportFormatEVMPremiumLegacy} { if _, exists := kbm[rf]; !exists { // Use the first if unspecified - kbs, err4 := d.ks.GetAllOfType(s) - if err4 != nil { - return nil, err4 + kbs, err3 := d.ks.GetAllOfType("evm") + if err3 != nil { + return nil, err3 } if len(kbs) == 0 { - // unsupported key type - continue + return nil, fmt.Errorf("no on-chain signing keys found for report format %s", "evm") } else if len(kbs) > 1 { lggr.Debugf("Multiple on-chain signing keys found for report format %s, using the first", rf.String()) } diff --git a/core/services/ocr2/plugins/ccip/ccipcommit/initializers.go b/core/services/ocr2/plugins/ccip/ccipcommit/initializers.go index b7543df155..57c84bd7b4 100644 --- a/core/services/ocr2/plugins/ccip/ccipcommit/initializers.go +++ b/core/services/ocr2/plugins/ccip/ccipcommit/initializers.go @@ -117,7 +117,7 @@ func NewCommitServices(ctx context.Context, ds sqlutil.DataSource, srcProvider c onRampAddress, ) - orm, err := cciporm.NewORM(ds) + orm, err := cciporm.NewObservedORM(ds, lggr) if err != nil { return nil, err } diff --git a/core/services/ocr2/plugins/ccip/exportinternal.go b/core/services/ocr2/plugins/ccip/exportinternal.go index be39346984..ffd770c2ae 100644 --- a/core/services/ocr2/plugins/ccip/exportinternal.go +++ b/core/services/ocr2/plugins/ccip/exportinternal.go @@ -10,12 +10,12 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/google/uuid" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/internal/gethwrappers2/generated/offchainaggregator" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipcalc" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata" diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/batchreader/token_pool_batch_reader.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/batchreader/token_pool_batch_reader.go index 57e8df1bde..32ec1b24ac 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/batchreader/token_pool_batch_reader.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/batchreader/token_pool_batch_reader.go @@ -12,8 +12,8 @@ import ( cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + "github.com/smartcontractkit/chainlink-common/pkg/logger" type_and_version "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/type_and_version_interface_wrapper" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers" ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipcalc" diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/ccipdataprovider/provider.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/ccipdataprovider/provider.go index d1666d548a..42a7369c7e 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/ccipdataprovider/provider.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/ccipdataprovider/provider.go @@ -3,10 +3,10 @@ package ccipdataprovider import ( "context" + "github.com/smartcontractkit/chainlink-common/pkg/logger" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/factory" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/observability" ) diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/commit_store.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/commit_store.go index c5f32ba91c..963f7bc4dc 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/commit_store.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/commit_store.go @@ -6,12 +6,12 @@ import ( cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + "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/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store_1_0_0" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers" ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipcalc" diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/offramp.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/offramp.go index 5d9b751d0b..9debbba465 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/offramp.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/offramp.go @@ -10,13 +10,13 @@ import ( cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp_1_0_0" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers" ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipcalc" diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/onramp.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/onramp.go index e82584ac7c..cb9e0015ca 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/onramp.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/onramp.go @@ -5,9 +5,9 @@ import ( cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + "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/logger" ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipcalc" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata" diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/price_registry.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/price_registry.go index f1fa7c4e81..3cae9670ee 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/price_registry.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/factory/price_registry.go @@ -7,9 +7,9 @@ import ( cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + "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/logger" ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipcalc" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipcommon" diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/reader.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/reader.go index 3f57d419e1..8655bc98ac 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/reader.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/reader.go @@ -6,10 +6,10 @@ import ( "github.com/ethereum/go-ethereum/core/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) const ( diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/usdc_reader.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/usdc_reader.go index 51ce0db7c0..428ec546f5 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/usdc_reader.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/usdc_reader.go @@ -10,9 +10,9 @@ import ( "github.com/patrickmn/go-cache" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers" ) diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_0_0/commit_store.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_0_0/commit_store.go index 3e58143a28..f865be86a4 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_0_0/commit_store.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_0_0/commit_store.go @@ -18,12 +18,12 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types/query" "github.com/smartcontractkit/chainlink-common/pkg/types/query/primitives" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store_1_0_0" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers" ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipcalc" diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_0_0/offramp.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_0_0/offramp.go index b5625c59d0..cc41c94394 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_0_0/offramp.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_0_0/offramp.go @@ -23,13 +23,13 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipcalc" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/logpollerutil" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp_1_0_0" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers" ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/cache" diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_0_0/onramp.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_0_0/onramp.go index d6f3094af7..048d4c5340 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_0_0/onramp.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_0_0/onramp.go @@ -10,12 +10,12 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/smartcontractkit/chainlink-common/pkg/hashutil" + "github.com/smartcontractkit/chainlink-common/pkg/logger" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" "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/ccip/generated/evm_2_evm_onramp_1_0_0" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_contract" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/cache" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata" diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_0_0/price_registry.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_0_0/price_registry.go index d2104f985b..e36bb6e5d7 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_0_0/price_registry.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_0_0/price_registry.go @@ -15,12 +15,12 @@ import ( cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + "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" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/price_registry_1_0_0" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/erc20" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/cache" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipcalc" diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_1_0/onramp.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_1_0/onramp.go index d4d73219fc..124d7f84f8 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_1_0/onramp.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_1_0/onramp.go @@ -8,10 +8,10 @@ import ( cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + "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/ccip/generated/evm_2_evm_onramp_1_1_0" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_0_0" ) diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/commit_store.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/commit_store.go index ecc8acb576..6bd85ee833 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/commit_store.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/commit_store.go @@ -18,12 +18,12 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types/query" "github.com/smartcontractkit/chainlink-common/pkg/types/query/primitives" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store_1_2_0" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers" ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipcalc" diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/offramp.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/offramp.go index f853adfb6f..2dd0994338 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/offramp.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/offramp.go @@ -15,12 +15,12 @@ import ( cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp_1_2_0" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers" ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipcalc" diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/onramp.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/onramp.go index 2de2b104c9..c53956d71b 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/onramp.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/onramp.go @@ -11,12 +11,12 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/smartcontractkit/chainlink-common/pkg/hashutil" + "github.com/smartcontractkit/chainlink-common/pkg/logger" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" "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/ccip/generated/evm_2_evm_onramp_1_2_0" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_contract" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/cache" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata" diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/price_registry.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/price_registry.go index 9aac30e612..91d94b278d 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/price_registry.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0/price_registry.go @@ -7,11 +7,11 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink-common/pkg/logger" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" "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/ccip/generated/price_registry_1_2_0" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipcalc" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_0_0" diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_5_0/commit_store.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_5_0/commit_store.go index d5545174cb..a403139a01 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_5_0/commit_store.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_5_0/commit_store.go @@ -10,10 +10,10 @@ import ( cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + "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/ccip/generated/commit_store" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_2_0" ) diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_5_0/offramp.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_5_0/offramp.go index 11e7be1a55..d5e220c27b 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_5_0/offramp.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_5_0/offramp.go @@ -9,13 +9,13 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-common/pkg/logger" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers" ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/cache" diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_5_0/onramp.go b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_5_0/onramp.go index 5a9377858d..d9739644b8 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_5_0/onramp.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdata/v1_5_0/onramp.go @@ -13,11 +13,11 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/hashutil" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + "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/ccip/generated/evm_2_evm_onramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/rmn_contract" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/cache" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipcalc" diff --git a/core/services/ocr2/plugins/ccip/internal/ccipdb/price_service_test.go b/core/services/ocr2/plugins/ccip/internal/ccipdb/price_service_test.go index 0468c3addb..e741d82c73 100644 --- a/core/services/ocr2/plugins/ccip/internal/ccipdb/price_service_test.go +++ b/core/services/ocr2/plugins/ccip/internal/ccipdb/price_service_test.go @@ -802,7 +802,7 @@ func setupORM(t *testing.T) cciporm.ORM { t.Helper() db := pgtest.NewSqlxDB(t) - orm, err := cciporm.NewORM(db) + orm, err := cciporm.NewORM(db, logger.NullLogger) require.NoError(t, err) diff --git a/core/services/ocr2/plugins/ccip/internal/pricegetter/pipeline.go b/core/services/ocr2/plugins/ccip/internal/pricegetter/pipeline.go index 34977eda9f..b9effffda1 100644 --- a/core/services/ocr2/plugins/ccip/internal/pricegetter/pipeline.go +++ b/core/services/ocr2/plugins/ccip/internal/pricegetter/pipeline.go @@ -10,8 +10,8 @@ import ( "github.com/google/uuid" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-common/pkg/logger" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipcalc" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/parseutil" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" diff --git a/core/services/ocr2/plugins/ccip/internal/rpclib/evm.go b/core/services/ocr2/plugins/ccip/internal/rpclib/evm.go index 876d9fa782..9fac459546 100644 --- a/core/services/ocr2/plugins/ccip/internal/rpclib/evm.go +++ b/core/services/ocr2/plugins/ccip/internal/rpclib/evm.go @@ -13,7 +13,7 @@ import ( "github.com/pkg/errors" "golang.org/x/sync/errgroup" - "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink-common/pkg/logger" ) var ErrEmptyOutput = errors.New("rpc call output is empty (make sure that the contract method exists and rpc is healthy)") diff --git a/core/services/ocr2/plugins/ccip/prices/da_price_estimator.go b/core/services/ocr2/plugins/ccip/prices/da_price_estimator.go index 34239398c8..83b0a8d453 100644 --- a/core/services/ocr2/plugins/ccip/prices/da_price_estimator.go +++ b/core/services/ocr2/plugins/ccip/prices/da_price_estimator.go @@ -19,6 +19,9 @@ type DAGasPriceEstimator struct { feeEstimatorConfig ccipdata.FeeEstimatorConfigReader priceEncodingLength uint daDeviationPPB int64 + daOverheadGas int64 + gasPerDAByte int64 + daMultiplier int64 } func NewDAGasPriceEstimator( diff --git a/core/services/ocr2/plugins/ccip/prices/da_price_estimator_test.go b/core/services/ocr2/plugins/ccip/prices/da_price_estimator_test.go index 52ef8c3800..7bef3c1d33 100644 --- a/core/services/ocr2/plugins/ccip/prices/da_price_estimator_test.go +++ b/core/services/ocr2/plugins/ccip/prices/da_price_estimator_test.go @@ -422,7 +422,10 @@ func TestDAPriceEstimator_EstimateMsgCostUSD(t *testing.T) { SourceTokenData: [][]byte{}, }, }, - expUSD: execCostUSD, + daOverheadGas: 100_000, + gasPerDAByte: 16, + daMultiplier: 10_000, // 1x multiplier + expUSD: execCostUSD, }, { name: "double native price", @@ -492,23 +495,25 @@ func TestDAPriceEstimator_EstimateMsgCostUSD(t *testing.T) { Return(tc.execEstimatorResponse...) } - g := DAGasPriceEstimator{ - execEstimator: execEstimator, - l1Oracle: nil, - priceEncodingLength: daGasPriceEncodingLength, - feeEstimatorConfig: feeEstimatorConfig, - } - - costUSD, err := g.EstimateMsgCostUSD(tc.gasPrice, tc.wrappedNativePrice, tc.msg) - - switch { - case len(tc.execEstimatorResponse) == 4 && tc.execEstimatorResponse[3] != nil, - tc.execEstimatorErr != nil: - assert.Error(t, err) - default: - assert.NoError(t, err) - assert.Equal(t, tc.expUSD, costUSD) - } + t.Run(tc.name, func(t *testing.T) { + g := DAGasPriceEstimator{ + execEstimator: execEstimator, + l1Oracle: nil, + priceEncodingLength: daGasPriceEncodingLength, + feeEstimatorConfig: feeEstimatorConfig, + } + + costUSD, err := g.EstimateMsgCostUSD(tc.gasPrice, tc.wrappedNativePrice, tc.msg) + + switch { + case len(tc.execEstimatorResponse) == 4 && tc.execEstimatorResponse[3] != nil, + tc.execEstimatorErr != nil: + assert.Error(t, err) + default: + assert.NoError(t, err) + assert.Equal(t, tc.expUSD, costUSD) + } + }) }) } } diff --git a/core/services/ocr2/plugins/ccip/tokendata/usdc/usdc.go b/core/services/ocr2/plugins/ccip/tokendata/usdc/usdc.go index fe3a86d2af..2818c98f64 100644 --- a/core/services/ocr2/plugins/ccip/tokendata/usdc/usdc.go +++ b/core/services/ocr2/plugins/ccip/tokendata/usdc/usdc.go @@ -19,8 +19,8 @@ import ( cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipcalc" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/internal/ccipdata" diff --git a/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go b/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go index 639d8ec04d..104e9f4da6 100644 --- a/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go +++ b/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go @@ -29,6 +29,7 @@ import ( ocrtypes2 "github.com/smartcontractkit/libocr/offchainreporting2plus/types" commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" + "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" diff --git a/core/services/ocr2/plugins/llo/config/config.go b/core/services/ocr2/plugins/llo/config/config.go index 892229c46c..2460fecd72 100644 --- a/core/services/ocr2/plugins/llo/config/config.go +++ b/core/services/ocr2/plugins/llo/config/config.go @@ -39,6 +39,8 @@ type PluginConfig struct { // KeyBundleIDs maps supported keys to their respective bundle IDs // Key must match llo's ReportFormat KeyBundleIDs map[string]string `json:"keyBundleIDs" toml:"keyBundleIDs"` + + DonID uint32 `json:"donID" toml:"donID"` } func (p *PluginConfig) Unmarshal(data []byte) error { @@ -46,8 +48,12 @@ func (p *PluginConfig) Unmarshal(data []byte) error { } func (p PluginConfig) Validate() (merr error) { + if p.DonID == 0 { + merr = errors.Join(merr, errors.New("llo: DonID must be specified and not zero")) + } + if p.RawServerURL == "" { - merr = errors.New("llo: ServerURL must be specified") + merr = errors.Join(merr, errors.New("llo: ServerURL must be specified")) } else { var normalizedURI string if schemeRegexp.MatchString(p.RawServerURL) { @@ -57,9 +63,9 @@ func (p PluginConfig) Validate() (merr error) { } uri, err := url.ParseRequestURI(normalizedURI) if err != nil { - merr = fmt.Errorf("llo: invalid value for ServerURL: %w", err) + merr = errors.Join(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) + merr = errors.Join(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)) } } @@ -74,6 +80,8 @@ func (p PluginConfig) Validate() (merr error) { if err := json.Unmarshal([]byte(p.ChannelDefinitions), &cd); err != nil { merr = errors.Join(merr, fmt.Errorf("channelDefinitions is invalid JSON: %w", err)) } + // TODO: Verify Opts format here? + // MERC-3524 } else { if p.ChannelDefinitionsContractAddress == (common.Address{}) { merr = errors.Join(merr, errors.New("llo: ChannelDefinitionsContractAddress is required if ChannelDefinitions is not specified")) diff --git a/core/services/ocr2/plugins/llo/config/config_test.go b/core/services/ocr2/plugins/llo/config/config_test.go index 136fac87a5..5152dc839b 100644 --- a/core/services/ocr2/plugins/llo/config/config_test.go +++ b/core/services/ocr2/plugins/llo/config/config_test.go @@ -62,6 +62,7 @@ func Test_Config(t *testing.T) { rawToml := fmt.Sprintf(` ServerURL = "example.com:80" ServerPubKey = "724ff6eae9e900270edfff233e16322a70ec06e1a6e62a81ef13921f398f6c93" + DonID = 12345 ChannelDefinitions = """ %s """`, cdjson) @@ -73,6 +74,7 @@ func Test_Config(t *testing.T) { assert.Equal(t, "example.com:80", mc.RawServerURL) assert.Equal(t, "724ff6eae9e900270edfff233e16322a70ec06e1a6e62a81ef13921f398f6c93", mc.ServerPubKey.String()) assert.JSONEq(t, cdjson, mc.ChannelDefinitions) + assert.Equal(t, uint32(12345), mc.DonID) assert.False(t, mc.BenchmarkMode) err = mc.Validate() @@ -80,6 +82,7 @@ func Test_Config(t *testing.T) { }) t.Run("with only channelDefinitions contract details", func(t *testing.T) { rawToml := ` + DonID = 12345 ServerURL = "example.com:80" ServerPubKey = "724ff6eae9e900270edfff233e16322a70ec06e1a6e62a81ef13921f398f6c93" ChannelDefinitionsContractAddress = "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"` @@ -91,6 +94,7 @@ func Test_Config(t *testing.T) { 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, uint32(12345), mc.DonID) assert.False(t, mc.BenchmarkMode) err = mc.Validate() @@ -99,7 +103,9 @@ func Test_Config(t *testing.T) { t.Run("with missing ChannelDefinitionsContractAddress", func(t *testing.T) { rawToml := ` ServerURL = "example.com:80" - ServerPubKey = "724ff6eae9e900270edfff233e16322a70ec06e1a6e62a81ef13921f398f6c93"` + ServerPubKey = "724ff6eae9e900270edfff233e16322a70ec06e1a6e62a81ef13921f398f6c93" + DonID = 12345 + ` var mc PluginConfig err := toml.Unmarshal([]byte(rawToml), &mc) @@ -107,6 +113,7 @@ func Test_Config(t *testing.T) { assert.Equal(t, "example.com:80", mc.RawServerURL) assert.Equal(t, "724ff6eae9e900270edfff233e16322a70ec06e1a6e62a81ef13921f398f6c93", mc.ServerPubKey.String()) + assert.Equal(t, uint32(12345), mc.DonID) assert.False(t, mc.BenchmarkMode) err = mc.Validate() 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 0000000000..23a0cc7064 --- /dev/null +++ b/core/services/ocr2/plugins/llo/helpers_test.go @@ -0,0 +1,532 @@ +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" + "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/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/chains/evm/assets" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/destination_verifier" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/destination_verifier_proxy" + "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" + "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" + "github.com/smartcontractkit/chainlink/v2/core/utils/testutils/heavyweight" +) + +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 + ObservedLogs *observer.ObservedLogs +} + +func (node *Node) AddStreamJob(t *testing.T, spec string) (id int32) { + job, err := streams.ValidatedStreamSpec(spec) + require.NoError(t, err) + err = node.App.AddJobV2(testutils.Context(t), &job) + require.NoError(t, err) + return job.ID +} + +func (node *Node) DeleteJob(t *testing.T, id int32) { + err := node.App.DeleteJob(testutils.Context(t), id) + require.NoError(t, err) +} + +func (node *Node) AddLLOJob(t *testing.T, spec string) { + c := node.App.GetConfig() + job, err := validate.ValidatedOracleSpecToml(testutils.Context(t), c.OCR2(), c.Insecure(), spec, nil) + 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) + if backend != nil { + app = cltest.NewApplicationWithConfigV2OnSimulatedBlockchain(t, config, backend, p2pKey, ocr2kb, csaKey, lggr.Named(dbName)) + } else { + app = cltest.NewApplicationWithConfig(t, config, 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 addSingleDecimalStreamJob( + t *testing.T, + node Node, + streamID uint32, + bridgeName string, +) (id int32) { + return 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 -> price1_parse; +""" + + `, + streamID, + streamID, + bridgeName, + )) +} + +func addQuoteStreamJob( + t *testing.T, + node Node, + streamID uint32, + benchmarkBridgeName string, + bidBridgeName string, + askBridgeName string, +) (id int32) { + return 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" index=0]; + + price1 -> price1_parse; + + // Bid + price2 [type=bridge name="%s" requestData="{\\"data\\":{\\"data\\":\\"foo\\"}}"]; + price2_parse [type=jsonparse path="result" index=1]; + + price2 -> price2_parse; + + // Ask + price3 [type=bridge name="%s" requestData="{\\"data\\":{\\"data\\":\\"foo\\"}}"]; + price3_parse [type=jsonparse path="result" index=2]; + + price3 -> price3_parse; +""" + + `, + streamID, + streamID, + benchmarkBridgeName, + bidBridgeName, + askBridgeName, + )) +} +func addBootstrapJob(t *testing.T, bootstrapNode Node, verifierAddress common.Address, name string, relayType, relayConfig string) { + bootstrapNode.AddBootstrapJob(t, fmt.Sprintf(` +type = "bootstrap" +relay = "%s" +schemaVersion = 1 +name = "boot-%s" +contractID = "%s" +contractConfigTrackerPollInterval = "1s" + +[relayConfig] +%s +providerType = "llo"`, relayType, name, verifierAddress.Hex(), relayConfig)) +} + +func addLLOJob( + t *testing.T, + node Node, + verifierAddress common.Address, + bootstrapPeerID string, + bootstrapNodePort int, + clientPubKey ed25519.PublicKey, + jobName string, + pluginConfig, + relayType, + relayConfig string, +) { + node.AddLLOJob(t, fmt.Sprintf(` +type = "offchainreporting2" +schemaVersion = 1 +name = "%s" +forwardingAllowed = false +maxTaskDuration = "1s" +contractID = "%s" +contractConfigTrackerPollInterval = "1s" +ocrKeyBundleID = "%s" +p2pv2Bootstrappers = [ + "%s" +] +relay = "%s" +pluginType = "llo" +transmitterID = "%x" + +[pluginConfig] +%s + +[relayConfig] +%s`, + jobName, + verifierAddress.Hex(), + node.KeyBundle.ID(), + fmt.Sprintf("%s@127.0.0.1:%d", bootstrapPeerID, bootstrapNodePort), + relayType, + clientPubKey, + pluginConfig, + relayConfig, + )) +} + +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, + pluginConfig, + relayType, + relayConfig string) (streamJobIDs []int32) { + + // Add OCR jobs - one per feed on each node + for i, node := range nodes { + for j, strm := range streams { + bmBridge := createBridge(t, fmt.Sprintf("benchmarkprice-%d-%d", strm.id, j), i, strm.baseBenchmarkPrice, node.App.BridgeORM()) + jobID := addSingleDecimalStreamJob( + t, + node, + strm.id, + bmBridge, + ) + streamJobIDs = append(streamJobIDs, jobID) + } + addLLOJob( + t, + node, + verifierAddress, + bootstrapPeerID, + bootstrapNodePort, + clientPubKeys[i], + "feed-1", + pluginConfig, + relayType, + relayConfig, + ) + } + return streamJobIDs +} + +func createBridge(t *testing.T, name string, i int, p decimal.Decimal, borm bridges.ORM) (bridgeName string) { + ctx := testutils.Context(t) + 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 := p.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(ctx, &bridges.BridgeType{ + Name: bridges.BridgeName(bridgeName), + URL: models.WebURL(*u), + })) + + return bridgeName +} + +func createErroringBridge(t *testing.T, name string, i int, borm bridges.ORM) (bridgeName string) { + ctx := testutils.Context(t) + bridge := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + res.WriteHeader(http.StatusInternalServerError) + })) + t.Cleanup(bridge.Close) + u, _ := url.Parse(bridge.URL) + bridgeName = fmt.Sprintf("bridge-%s-%d", name, i) + require.NoError(t, borm.CreateBridgeType(ctx, &bridges.BridgeType{ + Name: bridges.BridgeName(bridgeName), + URL: models.WebURL(*u), + })) + + return bridgeName +} + +func addOCRJobsEVMPremiumLegacy( + 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, + pluginConfig, + relayType, + relayConfig string) (jobIDs map[int]map[uint32]int32) { + // node idx => stream id => job id + jobIDs = make(map[int]map[uint32]int32) + // Add OCR jobs - one per feed on each node + for i, node := range nodes { + if jobIDs[i] == nil { + jobIDs[i] = make(map[uint32]int32) + } + for j, strm := range streams { + // assume that streams are native, link and quote + if j < 2 { + var name string + if j == 0 { + name = "nativeprice" + } else { + name = "linkprice" + } + name = fmt.Sprintf("%s-%d-%d", name, strm.id, j) + bmBridge := createBridge(t, name, i, strm.baseBenchmarkPrice, node.App.BridgeORM()) + jobID := addSingleDecimalStreamJob( + t, + node, + strm.id, + bmBridge, + ) + jobIDs[i][strm.id] = jobID + } else if j == 2 { + bmBridge := createBridge(t, fmt.Sprintf("benchmarkprice-%d-%d", strm.id, j), i, strm.baseBenchmarkPrice, node.App.BridgeORM()) + bidBridge := createBridge(t, fmt.Sprintf("bid-%d-%d", strm.id, j), i, strm.baseBid, node.App.BridgeORM()) + askBridge := createBridge(t, fmt.Sprintf("ask-%d-%d", strm.id, j), i, strm.baseAsk, node.App.BridgeORM()) + jobID := addQuoteStreamJob( + t, + node, + strm.id, + bmBridge, + bidBridge, + askBridge, + ) + jobIDs[i][strm.id] = jobID + } else { + panic("unexpected stream") + } + } + addLLOJob( + t, + node, + verifierAddress, + bootstrapPeerID, + bootstrapNodePort, + clientPubKeys[i], + "feed-1", + pluginConfig, + relayType, + relayConfig, + ) + } + return jobIDs +} + +func setupV03Blockchain(t *testing.T) (*bind.TransactOpts, *backends.SimulatedBackend, *destination_verifier.DestinationVerifier, *destination_verifier_proxy.DestinationVerifierProxy, 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() // ensure starting block number at least 1 + + // Deploy verifier proxy + verifierProxyAddr, _, verifierProxy, err := destination_verifier_proxy.DeployDestinationVerifierProxy(steve, backend) + require.NoError(t, err) + backend.Commit() + + // Deploy verifier + verifierAddress, _, verifier, err := destination_verifier.DeployDestinationVerifier(steve, backend, verifierProxyAddr) + require.NoError(t, err) + backend.Commit() + + // Set verifier on proxy + _, err = verifierProxy.SetVerifier(steve, verifierAddress) + require.NoError(t, err) + backend.Commit() + + return steve, backend, verifier, verifierProxy, verifierProxyAddr +} 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 0000000000..787946b6ad --- /dev/null +++ b/core/services/ocr2/plugins/llo/integration_test.go @@ -0,0 +1,463 @@ +package llo_test + +import ( + "crypto/ed25519" + "encoding/hex" + "encoding/json" + "fmt" + "math/big" + "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/common/hexutil" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/eth/ethconfig" + "github.com/hashicorp/consul/sdk/freeport" + "github.com/shopspring/decimal" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/libocr/offchainreporting2/chains/evmutil" + "github.com/smartcontractkit/libocr/offchainreporting2/types" + "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" + 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/destination_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" + lloevm "github.com/smartcontractkit/chainlink/v2/core/services/llo/evm" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" + mercuryverifier "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/verifier" +) + +var ( + fNodes = uint8(1) + nNodes = 4 // number of nodes (not including bootstrap) +) + +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 decimal.Decimal + baseBid decimal.Decimal + baseAsk decimal.Decimal +} + +var ( + btcStream = Stream{ + id: 51, + baseBenchmarkPrice: decimal.NewFromFloat32(56_114.41), + } + ethStream = Stream{ + id: 52, + baseBenchmarkPrice: decimal.NewFromFloat32(2_976.39), + } + linkStream = Stream{ + id: 53, + baseBenchmarkPrice: decimal.NewFromFloat32(13.25), + } + dogeStream = Stream{ + id: 54, + baseBenchmarkPrice: decimal.NewFromFloat32(0.10960935), + } + quoteStream = Stream{ + id: 55, + baseBenchmarkPrice: decimal.NewFromFloat32(1000.1212), + baseBid: decimal.NewFromFloat32(998.5431), + baseAsk: decimal.NewFromFloat32(1001.6999), + } +) + +func generateConfig(t *testing.T, oracles []confighelper.OracleIdentityExtra) ( + signers []types.OnchainPublicKey, + transmitters []types.Account, + f uint8, + onchainConfig []byte, + offchainConfigVersion uint64, + offchainConfig []byte, +) { + rawReportingPluginConfig := datastreamsllo.OffchainConfig{} + reportingPluginConfig, err := rawReportingPluginConfig.Encode() + require.NoError(t, err) + + offchainConfig = []byte{} + + signers, transmitters, f, onchainConfig, 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(oracles)}, // S + oracles, + reportingPluginConfig, // reportingPluginConfig []byte, + 0, // maxDurationQuery + 250*time.Millisecond, // maxDurationObservation + 0, // maxDurationShouldAcceptAttestedReport + 0, // maxDurationShouldTransmitAcceptedReport + int(fNodes), // f + onchainConfig, // encoded onchain config + ) + + require.NoError(t, err) + + return +} + +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 { + signers, _, _, _, offchainConfigVersion, offchainConfig := generateConfig(t, oracles) + + 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() + + l, err := verifierContract.LatestConfigDigestAndEpoch(&bind.CallOpts{}) + require.NoError(t, err) + + return l.ConfigDigest +} + +// On-chain format is not finalized yet so use the dummy relayer for testing +func TestIntegration_LLO_Dummy(t *testing.T) { + testStartTimeStamp := time.Now() + + 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 + } + + // Setup bootstrap + bootstrapCSAKey := csakey.MustNewV2XXXTestingOnly(big.NewInt(-1)) + bootstrapNodePort := freeport.GetOne(t) + appBootstrap, bootstrapPeerID, _, bootstrapKb, _ := setupNode(t, bootstrapNodePort, "bootstrap_llo", nil, bootstrapCSAKey) + bootstrapNode := Node{App: appBootstrap, KeyBundle: bootstrapKb} + + t.Run("produces reports in v0.3 format", func(t *testing.T) { + streams := []Stream{ethStream, linkStream, quoteStream} + streamMap := make(map[uint32]Stream) + for _, strm := range streams { + streamMap[strm.id] = strm + } + + // Setup oracle nodes + var ( + oracles []confighelper.OracleIdentityExtra + nodes []Node + ) + ports := freeport.GetN(t, nNodes) + for i := 0; i < nNodes; i++ { + app, peerID, transmitter, kb, observedLogs := setupNode(t, ports[i], fmt.Sprintf("oracle_streams_%d", i), nil, clientCSAKeys[i]) + + nodes = append(nodes, Node{ + app, transmitter, kb, observedLogs, + }) + 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(), + }) + } + + verifierAddress := common.Address{} + chainID := "llo-dummy" + relayType := "dummy" + cd := ocr2types.ConfigDigest{0x01, 0x02, 0x03, 0x04} + signers, transmitters, f, onchainConfig, offchainConfigVersion, offchainConfig := generateConfig(t, oracles) + var signersMarshalled, transmittersMarshalled []byte + { + var err error + signersHex := make([]string, len(signers)) + for i, signer := range signers { + signersHex[i] = fmt.Sprintf("0x%x", signer) + } + signersMarshalled, err = json.Marshal(signersHex) + require.NoError(t, err) + + transmittersMarshalled, err = json.Marshal(transmitters) + require.NoError(t, err) + } + + relayConfig := fmt.Sprintf(`chainID = "%s" +configTracker = { + configDigest = "0x%x", + configCount = 0, + signers = %s, + transmitters = %s, + f = %d, + onchainConfig = "0x%x", + offchainConfigVersion = %d, + offchainConfig = "0x%x", + blockHeight = 10 +}`, chainID, cd[:], string(signersMarshalled), string(transmittersMarshalled), f, onchainConfig, offchainConfigVersion, offchainConfig) + addBootstrapJob(t, bootstrapNode, verifierAddress, "job-2", relayType, relayConfig) + + serverKey := csakey.MustNewV2XXXTestingOnly(big.NewInt(-1)) + serverPubKey := serverKey.PublicKey + serverURL := "foo" + configStoreAddress := common.Address{} + + chainSelector := 4949039107694359620 // arbitrum mainnet + + feedID := [32]byte{00, 03, 107, 74, 167, 229, 124, 167, 182, 138, 225, 191, 69, 101, 63, 86, 182, 86, 253, 58, 163, 53, 239, 127, 174, 105, 107, 102, 63, 27, 132, 114} + expirationWindow := 3600 + multiplier := big.NewInt(1e18) + baseUSDFee := 10 + // 52 = eth, 53 = link, 55 = quote + channelDefinitions := fmt.Sprintf(` +{ + "42": { + "reportFormat": %d, + "chainSelector": %d, + "streams": [{"streamId": 52, "aggregator": %d}, {"streamId": 53, "aggregator": %d}, {"streamId": 55, "aggregator": %d}], + "opts": { + "feedId": "0x%x", + "expirationWindow": %d, + "multiplier": "%s", + "baseUSDFee": "%d" + } + } +}`, llotypes.ReportFormatEVMPremiumLegacy, chainSelector, llotypes.AggregatorMedian, llotypes.AggregatorMedian, llotypes.AggregatorQuote, feedID, expirationWindow, multiplier.String(), baseUSDFee) + + pluginConfig := fmt.Sprintf(`serverURL = "foo" +donID = 42 +serverPubKey = "%x" +channelDefinitions = %q`, serverPubKey, channelDefinitions) + jobIDs := addOCRJobsEVMPremiumLegacy(t, streams, serverPubKey, serverURL, verifierAddress, bootstrapPeerID, bootstrapNodePort, nodes, configStoreAddress, clientPubKeys, pluginConfig, relayType, relayConfig) + + steve, backend, verifier, verifierProxy, _ := setupV03Blockchain(t) + + // Set config + recipientAddressesAndWeights := []destination_verifier.CommonAddressAndWeight{} + signerAddresses := make([]common.Address, len(oracles)) + for i, oracle := range oracles { + signerAddresses[i] = common.BytesToAddress(oracle.OracleIdentity.OnchainPublicKey) + } + + _, err := verifier.SetConfig(steve, signerAddresses, f, recipientAddressesAndWeights) + require.NoError(t, err) + backend.Commit() + + for i, node := range nodes { + le := testutils.WaitForLogMessage(t, node.ObservedLogs, "Transmit") + fields := le.ContextMap() + assert.Equal(t, hexutil.Encode(cd[:]), "0x"+fields["digest"].(string)) + assert.Equal(t, llotypes.ReportInfo{LifeCycleStage: "production", ReportFormat: llotypes.ReportFormatEVMPremiumLegacy}, fields["report.Info"]) + + if fields["report.Report"] == nil { + t.Fatal("FAIL: expected log fields to contain 'report.Report'") + } + binaryReport := fields["report.Report"].(types.Report) + report, err := (lloevm.ReportCodecPremiumLegacy{}).Decode(binaryReport) + require.NoError(t, err) + assert.Equal(t, feedID, report.FeedId) + assert.GreaterOrEqual(t, report.ObservationsTimestamp, uint32(testStartTimeStamp.Unix())) + assert.Equal(t, quoteStream.baseBenchmarkPrice.Mul(decimal.NewFromBigInt(multiplier, 0)).String(), report.BenchmarkPrice.String()) + assert.Equal(t, quoteStream.baseBid.Mul(decimal.NewFromBigInt(multiplier, 0)).String(), report.Bid.String()) + assert.Equal(t, quoteStream.baseAsk.Mul(decimal.NewFromBigInt(multiplier, 0)).String(), report.Ask.String()) + assert.GreaterOrEqual(t, report.ValidFromTimestamp, uint32(testStartTimeStamp.Unix())) + assert.Equal(t, report.ObservationsTimestamp+uint32(expirationWindow), report.ExpiresAt) + assert.Equal(t, big.NewInt(754716981132075472), report.LinkFee) + assert.Equal(t, big.NewInt(3359774760700043), report.NativeFee) + + seqNr := fields["seqNr"].(uint64) + assert.Greater(t, int(seqNr), 0) + + sigs := fields["sigs"].([]types.AttributedOnchainSignature) + + t.Run(fmt.Sprintf("emulate mercury server verifying report (local verification) - node %d", i), func(t *testing.T) { + 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 { + panic("error in SplitSignature") + } + rs = append(rs, r) + ss = append(ss, s) + vs[i] = v + } + rc := lloevm.LegacyReportContext(cd, seqNr) + rawReportCtx := evmutil.RawReportContext(rc) + rv := mercuryverifier.NewVerifier() + + reportSigners, err := rv.Verify(mercuryverifier.SignedReport{ + RawRs: rs, + RawSs: ss, + RawVs: vs, + ReportContext: rawReportCtx, + Report: binaryReport, + }, f, signerAddresses) + require.NoError(t, err) + assert.GreaterOrEqual(t, len(reportSigners), int(f+1)) + assert.Subset(t, signerAddresses, reportSigners) + }) + + t.Run(fmt.Sprintf("test on-chain verification - node %d", i), func(t *testing.T) { + signedReport, err := lloevm.ReportCodecPremiumLegacy{}.Pack(cd, seqNr, binaryReport, sigs) + require.NoError(t, err) + + _, err = verifierProxy.Verify(steve, signedReport, []byte{}) + require.NoError(t, err) + + }) + } + + t.Run("if link/eth stream specs start failing, uses 0 for the fee", func(t *testing.T) { + t.Run("link/eth stream specs are missing", func(t *testing.T) { + // delete eth/link stream specs + for idx, strmIDs := range jobIDs { + for strmID, jobID := range strmIDs { + if strmID == ethStream.id || strmID == linkStream.id { + nodes[idx].DeleteJob(t, jobID) + } + } + } + + for _, node := range nodes { + node.ObservedLogs.TakeAll() + + le := testutils.WaitForLogMessage(t, node.ObservedLogs, "Observation failed for streams") + fields := le.ContextMap() + assert.Equal(t, []interface{}{ethStream.id, linkStream.id}, fields["failedStreamIDs"]) + assert.Equal(t, []interface{}{"StreamID: 52; Reason: missing stream: 52", "StreamID: 53; Reason: missing stream: 53"}, fields["errors"]) + + le = testutils.WaitForLogMessage(t, node.ObservedLogs, "Transmit") + fields = le.ContextMap() + assert.Equal(t, hexutil.Encode(cd[:]), "0x"+fields["digest"].(string)) + assert.Equal(t, llotypes.ReportInfo{LifeCycleStage: "production", ReportFormat: llotypes.ReportFormatEVMPremiumLegacy}, fields["report.Info"]) + + if fields["report.Report"] == nil { + t.Fatal("FAIL: expected log fields to contain 'report.Report'") + } + binaryReport := fields["report.Report"].(types.Report) + report, err := (lloevm.ReportCodecPremiumLegacy{}).Decode(binaryReport) + require.NoError(t, err) + assert.Equal(t, feedID, report.FeedId) + assert.GreaterOrEqual(t, report.ObservationsTimestamp, uint32(testStartTimeStamp.Unix())) + assert.Equal(t, quoteStream.baseBenchmarkPrice.Mul(decimal.NewFromBigInt(multiplier, 0)).String(), report.BenchmarkPrice.String()) + assert.Equal(t, quoteStream.baseBid.Mul(decimal.NewFromBigInt(multiplier, 0)).String(), report.Bid.String()) + assert.Equal(t, quoteStream.baseAsk.Mul(decimal.NewFromBigInt(multiplier, 0)).String(), report.Ask.String()) + assert.GreaterOrEqual(t, report.ValidFromTimestamp, uint32(testStartTimeStamp.Unix())) + assert.Equal(t, report.ObservationsTimestamp+uint32(expirationWindow), report.ExpiresAt) + assert.Equal(t, "0", report.LinkFee.String()) + assert.Equal(t, "0", report.NativeFee.String()) + } + }) + + t.Run("link/eth stream specs have EAs that return error", func(t *testing.T) { + // add new stream specs that will fail + for i, node := range nodes { + for j, strm := range streams { + if strm.id == ethStream.id || strm.id == linkStream.id { + var name string + if j == 0 { + name = "nativeprice" + } else { + name = "linkprice" + } + name = fmt.Sprintf("%s-%d-%d-erroring", name, strm.id, j) + bmBridge := createErroringBridge(t, name, i, node.App.BridgeORM()) + addSingleDecimalStreamJob( + t, + node, + strm.id, + bmBridge, + ) + } + } + } + + for _, node := range nodes { + node.ObservedLogs.TakeAll() + + le := testutils.WaitForLogMessage(t, node.ObservedLogs, "Observation failed for streams") + fields := le.ContextMap() + assert.Equal(t, []interface{}{ethStream.id, linkStream.id}, fields["failedStreamIDs"]) + assert.Len(t, fields["errors"], 2) + for _, err := range fields["errors"].([]interface{}) { + assert.Contains(t, err.(string), "Reason: failed to extract big.Int") + assert.Contains(t, err.(string), "status code 500") + } + + le = testutils.WaitForLogMessage(t, node.ObservedLogs, "Transmit") + fields = le.ContextMap() + assert.Equal(t, hexutil.Encode(cd[:]), "0x"+fields["digest"].(string)) + assert.Equal(t, llotypes.ReportInfo{LifeCycleStage: "production", ReportFormat: llotypes.ReportFormatEVMPremiumLegacy}, fields["report.Info"]) + + if fields["report.Report"] == nil { + t.Fatal("FAIL: expected log fields to contain 'report.Report'") + } + binaryReport := fields["report.Report"].(types.Report) + report, err := (lloevm.ReportCodecPremiumLegacy{}).Decode(binaryReport) + require.NoError(t, err) + assert.Equal(t, feedID, report.FeedId) + assert.GreaterOrEqual(t, report.ObservationsTimestamp, uint32(testStartTimeStamp.Unix())) + assert.Equal(t, quoteStream.baseBenchmarkPrice.Mul(decimal.NewFromBigInt(multiplier, 0)).String(), report.BenchmarkPrice.String()) + assert.Equal(t, quoteStream.baseBid.Mul(decimal.NewFromBigInt(multiplier, 0)).String(), report.Bid.String()) + assert.Equal(t, quoteStream.baseAsk.Mul(decimal.NewFromBigInt(multiplier, 0)).String(), report.Ask.String()) + assert.GreaterOrEqual(t, report.ValidFromTimestamp, uint32(testStartTimeStamp.Unix())) + assert.Equal(t, int(report.ObservationsTimestamp+uint32(expirationWindow)), int(report.ExpiresAt)) + assert.Equal(t, "0", report.LinkFee.String()) + assert.Equal(t, "0", report.NativeFee.String()) + } + }) + }) + + t.Run("deleting LLO jobs cleans up resources", func(t *testing.T) { + t.Skip("TODO - https://smartcontract-it.atlassian.net/browse/MERC-3653") + }) + }) +} 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 9d2d52ce50..d084e51531 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 @@ -1,215 +1,384 @@ package llo_test import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "math/rand/v2" + "net/http" + "sync" "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/eth/ethconfig" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap/zapcore" + "golang.org/x/crypto/sha3" + + "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/headtracker" + "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" ) +type mockHTTPClient struct { + resp *http.Response + err error + mu sync.Mutex +} + +func (h *mockHTTPClient) Do(req *http.Request) (*http.Response, error) { + h.mu.Lock() + defer h.mu.Unlock() + return h.resp, h.err +} + +func (h *mockHTTPClient) SetResponse(resp *http.Response, err error) { + h.mu.Lock() + defer h.mu.Unlock() + h.resp = resp + h.err = err +} + +type MockReadCloser struct { + data []byte + mu sync.RWMutex + reader *bytes.Reader +} + +func NewMockReadCloser(data []byte) *MockReadCloser { + return &MockReadCloser{ + data: data, + reader: bytes.NewReader(data), + } +} + +// Read reads from the underlying data +func (m *MockReadCloser) Read(p []byte) (int, error) { + m.mu.RLock() + defer m.mu.RUnlock() + return m.reader.Read(p) +} + +// Close resets the reader to the beginning of the data +func (m *MockReadCloser) Close() error { + m.mu.Lock() + defer m.mu.Unlock() + m.reader.Seek(0, io.SeekStart) + return nil +} + func Test_ChannelDefinitionCache_Integration(t *testing.T) { - t.Skip("waiting on https://github.com/smartcontractkit/chainlink/pull/13780") - // 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) { - // lpOpts := logpoller.Opts{ - // PollPeriod: 100 * time.Millisecond, - // FinalityDepth: 1, - // BackfillBatchSize: 3, - // RpcBatchSize: 2, - // KeepFinalizedBlocksDepth: 1000, - // } - // ht := headtracker.NewSimulatedHeadTracker(ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) - // lp := logpoller.NewLogPoller( - // logpoller.NewORM(testutils.SimulatedChainID, db, lggr), ethClient, lggr, ht, lpOpts) - // 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 - // lpOpts := logpoller.Opts{ - // PollPeriod: 100 * time.Millisecond, - // FinalityDepth: 1, - // BackfillBatchSize: 3, - // RpcBatchSize: 2, - // KeepFinalizedBlocksDepth: 1000, - // } - // ht := headtracker.NewSimulatedHeadTracker(ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) - // lp := &mockLogPoller{ - // LogPoller: logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr), ethClient, lggr, ht, lpOpts), - // LatestBlockFn: func(ctx context.Context) (int64, error) { - // return 0, nil - // }, - // LogsWithSigsFn: func(ctx context.Context, start, end int64, eventSigs []common.Hash, address common.Address) ([]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) { - // lpOpts := logpoller.Opts{ - // PollPeriod: 100 * time.Millisecond, - // FinalityDepth: 1, - // BackfillBatchSize: 3, - // RpcBatchSize: 2, - // KeepFinalizedBlocksDepth: 1000, - // } - // ht := headtracker.NewSimulatedHeadTracker(ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) - // lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.SimulatedChainID, db, lggr), ethClient, lggr, ht, lpOpts) - // 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(ctx context.Context) (int64, error) - // LogsWithSigsFn func(ctx context.Context, start, end int64, eventSigs []common.Hash, address common.Address) ([]logpoller.Log, error) - // } - - // func (p *mockLogPoller) LogsWithSigs(ctx context.Context, start, end int64, eventSigs []common.Hash, address common.Address) ([]logpoller.Log, error) { - // return p.LogsWithSigsFn(ctx, start, end, eventSigs, address) - // } - // - // func (p *mockLogPoller) LatestBlock(ctx context.Context) (logpoller.LogPollerBlock, error) { - // block, err := p.LatestBlockFn(ctx) - // return logpoller.LogPollerBlock{BlockNumber: block}, err + var ( + invalidDefinitions = []byte(`{{{`) + invalidDefinitionsSHA = sha3.Sum256(invalidDefinitions) + + sampleDefinitions = llotypes.ChannelDefinitions{ + 1: { + ReportFormat: llotypes.ReportFormatJSON, + Streams: []llotypes.Stream{ + { + StreamID: 1, + Aggregator: llotypes.AggregatorMedian, + }, + { + StreamID: 2, + Aggregator: llotypes.AggregatorMode, + }, + }, + }, + 2: { + ReportFormat: llotypes.ReportFormatEVMPremiumLegacy, + Streams: []llotypes.Stream{ + { + StreamID: 1, + Aggregator: llotypes.AggregatorMedian, + }, + { + StreamID: 2, + Aggregator: llotypes.AggregatorMedian, + }, + { + StreamID: 3, + Aggregator: llotypes.AggregatorQuote, + }, + }, + Opts: llotypes.ChannelOpts([]byte(`{"baseUSDFee":"0.1","expirationWindow":86400,"feedId":"0x0003aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","multiplier":"1000000000000000000"}`)), + }, + } + ) + + sampleDefinitionsJSON, err := json.MarshalIndent(sampleDefinitions, "", " ") + require.NoError(t, err) + sampleDefinitionsSHA := sha3.Sum256(sampleDefinitionsJSON) + + lggr, observedLogs := logger.TestLoggerObserved(t, zapcore.DebugLevel) + db := pgtest.NewSqlxDB(t) + const ETHMainnetChainSelector uint64 = 5009297550715157269 + orm := llo.NewORM(db, ETHMainnetChainSelector) + + 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) + + lpOpts := logpoller.Opts{ + PollPeriod: 100 * time.Millisecond, + FinalityDepth: 1, + BackfillBatchSize: 3, + RpcBatchSize: 2, + KeepFinalizedBlocksDepth: 1000, + } + ht := headtracker.NewSimulatedHeadTracker(ethClient, lpOpts.UseFinalityTag, lpOpts.FinalityDepth) + lp := logpoller.NewLogPoller( + logpoller.NewORM(testutils.SimulatedChainID, db, lggr), ethClient, lggr, ht, lpOpts) + servicetest.Run(t, lp) + + client := &mockHTTPClient{} + donID := rand.Uint32() + + cdc := llo.NewChannelDefinitionCache(lggr, orm, client, lp, configStoreAddress, donID, 0, llo.WithLogPollInterval(100*time.Millisecond)) + servicetest.Run(t, cdc) + + t.Run("before any logs, returns empty Definitions", func(t *testing.T) { + assert.Empty(t, cdc.Definitions()) + }) + + { + rc := NewMockReadCloser(invalidDefinitions) + client.SetResponse(&http.Response{ + StatusCode: 200, + Body: rc, + }, nil) + + url := "http://example.com/foo" + require.NoError(t, utils.JustError(configStoreContract.SetChannelDefinitions(steve, donID, url, sampleDefinitionsSHA))) + + backend.Commit() + } + + t.Run("with sha mismatch, should not update", func(t *testing.T) { + // clear the log messages + t.Cleanup(func() { observedLogs.TakeAll() }) + + testutils.WaitForLogMessage(t, observedLogs, "Got new channel definitions from chain") + le := testutils.WaitForLogMessage(t, observedLogs, "Error while fetching channel definitions") + fields := le.ContextMap() + assert.Contains(t, fields, "err") + assert.Equal(t, fmt.Sprintf("SHA3 mismatch: expected %x, got %x", sampleDefinitionsSHA, invalidDefinitionsSHA), fields["err"]) + + assert.Empty(t, cdc.Definitions()) + }) + + { + rc := NewMockReadCloser(invalidDefinitions) + client.SetResponse(&http.Response{ + StatusCode: 200, + Body: rc, + }, nil) + + url := "http://example.com/foo" + require.NoError(t, utils.JustError(configStoreContract.SetChannelDefinitions(steve, donID, url, invalidDefinitionsSHA))) + backend.Commit() + } + + t.Run("after correcting sha with new channel definitions set on-chain, but with invalid JSON at url, should not update", func(t *testing.T) { + // clear the log messages + t.Cleanup(func() { observedLogs.TakeAll() }) + + testutils.WaitForLogMessage(t, observedLogs, "Got new channel definitions from chain") + testutils.WaitForLogMessageWithField(t, observedLogs, "Error while fetching channel definitions", "err", "failed to decode JSON: invalid character '{' looking for beginning of object key string") + + assert.Empty(t, cdc.Definitions()) + }) + + { + rc := NewMockReadCloser([]byte("not found")) + client.SetResponse(&http.Response{ + StatusCode: 404, + Body: rc, + }, nil) + + url := "http://example.com/foo3" + require.NoError(t, utils.JustError(configStoreContract.SetChannelDefinitions(steve, donID, url, sampleDefinitionsSHA))) + backend.Commit() + } + + t.Run("if server returns 404, should not update", func(t *testing.T) { + // clear the log messages + t.Cleanup(func() { observedLogs.TakeAll() }) + + testutils.WaitForLogMessageWithField(t, observedLogs, "Error while fetching channel definitions", "err", "got error from http://example.com/foo3: (status code: 404, response body: not found)") + }) + + { + rc := NewMockReadCloser([]byte{}) + client.SetResponse(&http.Response{ + StatusCode: 200, + Body: rc, + }, nil) + } + + t.Run("if server starts returning empty body, still does not update", func(t *testing.T) { + // clear the log messages + t.Cleanup(func() { observedLogs.TakeAll() }) + + testutils.WaitForLogMessageWithField(t, observedLogs, "Error while fetching channel definitions", "err", fmt.Sprintf("SHA3 mismatch: expected %x, got %x", sampleDefinitionsSHA, sha3.Sum256([]byte{}))) + }) + + { + rc := NewMockReadCloser(sampleDefinitionsJSON) + client.SetResponse(&http.Response{ + StatusCode: 200, + Body: rc, + }, nil) + } + + t.Run("when URL starts returning valid JSON, updates even without needing new logs", func(t *testing.T) { + // clear the log messages + t.Cleanup(func() { observedLogs.TakeAll() }) + + le := testutils.WaitForLogMessage(t, observedLogs, "Set new channel definitions") + fields := le.ContextMap() + assert.Contains(t, fields, "version") + assert.Contains(t, fields, "url") + assert.Contains(t, fields, "sha") + assert.Contains(t, fields, "donID") + assert.NotContains(t, fields, "err") + + assert.Equal(t, uint32(3), fields["version"]) + assert.Equal(t, "http://example.com/foo3", fields["url"]) + assert.Equal(t, fmt.Sprintf("%x", sampleDefinitionsSHA), fields["sha"]) + assert.Equal(t, donID, fields["donID"]) + + assert.Equal(t, sampleDefinitions, cdc.Definitions()) + + t.Run("latest channel definitions are persisted", func(t *testing.T) { + pd, err := orm.LoadChannelDefinitions(testutils.Context(t), configStoreAddress, donID) + require.NoError(t, err) + assert.Equal(t, ETHMainnetChainSelector, pd.ChainSelector) + assert.Equal(t, configStoreAddress, pd.Address) + assert.Equal(t, sampleDefinitions, pd.Definitions) + assert.Equal(t, donID, pd.DonID) + assert.Equal(t, uint32(3), pd.Version) + }) + + t.Run("new cdc with same config should load from DB", func(t *testing.T) { + // fromBlock far in the future to ensure logs are not used + cdc2 := llo.NewChannelDefinitionCache(lggr, orm, client, lp, configStoreAddress, donID, 1000) + servicetest.Run(t, cdc2) + + assert.Equal(t, sampleDefinitions, cdc.Definitions()) + }) + }) + + { + url := "not a real URL" + require.NoError(t, utils.JustError(configStoreContract.SetChannelDefinitions(steve, donID, url, sampleDefinitionsSHA))) + + backend.Commit() + + client.SetResponse(nil, errors.New("failed; not a real URL")) + } + + t.Run("new log with invalid channel definitions URL does not affect old channel definitions", func(t *testing.T) { + // clear the log messages + t.Cleanup(func() { observedLogs.TakeAll() }) + + le := testutils.WaitForLogMessage(t, observedLogs, "Error while fetching channel definitions") + fields := le.ContextMap() + assert.Contains(t, fields, "err") + assert.Equal(t, "error making http request: failed; not a real URL", fields["err"]) + }) + + { + // add a new definition, it should get loaded + sampleDefinitions[3] = llotypes.ChannelDefinition{ + ReportFormat: llotypes.ReportFormatJSON, + Streams: []llotypes.Stream{ + { + StreamID: 6, + Aggregator: llotypes.AggregatorMedian, + }, + }, + } + var err error + sampleDefinitionsJSON, err = json.MarshalIndent(sampleDefinitions, "", " ") + require.NoError(t, err) + sampleDefinitionsSHA = sha3.Sum256(sampleDefinitionsJSON) + rc := NewMockReadCloser(sampleDefinitionsJSON) + client.SetResponse(&http.Response{ + StatusCode: 200, + Body: rc, + }, nil) + + url := "http://example.com/foo5" + require.NoError(t, utils.JustError(configStoreContract.SetChannelDefinitions(steve, donID, url, sampleDefinitionsSHA))) + + backend.Commit() + } + + t.Run("successfully updates to new channel definitions with new log", func(t *testing.T) { + t.Cleanup(func() { observedLogs.TakeAll() }) + + le := testutils.WaitForLogMessage(t, observedLogs, "Set new channel definitions") + fields := le.ContextMap() + assert.Contains(t, fields, "version") + assert.Contains(t, fields, "url") + assert.Contains(t, fields, "sha") + assert.Contains(t, fields, "donID") + assert.NotContains(t, fields, "err") + + assert.Equal(t, uint32(5), fields["version"]) + assert.Equal(t, "http://example.com/foo5", fields["url"]) + assert.Equal(t, fmt.Sprintf("%x", sampleDefinitionsSHA), fields["sha"]) + assert.Equal(t, donID, fields["donID"]) + + assert.Equal(t, sampleDefinitions, cdc.Definitions()) + }) + + t.Run("latest channel definitions are persisted and overwrite previous value", func(t *testing.T) { + pd, err := orm.LoadChannelDefinitions(testutils.Context(t), configStoreAddress, donID) + require.NoError(t, err) + assert.Equal(t, ETHMainnetChainSelector, pd.ChainSelector) + assert.Equal(t, configStoreAddress, pd.Address) + assert.Equal(t, sampleDefinitions, pd.Definitions) + assert.Equal(t, donID, pd.DonID) + assert.Equal(t, uint32(5), pd.Version) + }) +} + +type mockLogPoller struct { + logpoller.LogPoller + LatestBlockFn func(ctx context.Context) (int64, error) + LogsWithSigsFn func(ctx context.Context, start, end int64, eventSigs []common.Hash, address common.Address) ([]logpoller.Log, error) +} + +func (p *mockLogPoller) LogsWithSigs(ctx context.Context, start, end int64, eventSigs []common.Hash, address common.Address) ([]logpoller.Log, error) { + return p.LogsWithSigsFn(ctx, start, end, eventSigs, address) +} +func (p *mockLogPoller) LatestBlock(ctx context.Context) (logpoller.LogPollerBlock, error) { + block, err := p.LatestBlockFn(ctx) + return logpoller.LogPollerBlock{BlockNumber: block}, err } diff --git a/core/services/ocr2/plugins/mercury/config/config.go b/core/services/ocr2/plugins/mercury/config/config.go index 5763b883ac..40854bd8c0 100644 --- a/core/services/ocr2/plugins/mercury/config/config.go +++ b/core/services/ocr2/plugins/mercury/config/config.go @@ -108,7 +108,7 @@ func ValidatePluginConfig(config PluginConfig, feedID mercuryutils.FeedID) (merr if config.NativeFeedID != nil { merr = errors.Join(merr, errors.New("nativeFeedID may not be specified for v1 jobs")) } - case 2, 3: + case 2, 3, 4: if config.LinkFeedID == nil { merr = errors.Join(merr, fmt.Errorf("linkFeedID must be specified for v%d jobs", feedID.Version())) } @@ -119,7 +119,7 @@ func ValidatePluginConfig(config PluginConfig, feedID mercuryutils.FeedID) (merr merr = errors.Join(merr, fmt.Errorf("initialBlockNumber may not be specified for v%d jobs", feedID.Version())) } default: - merr = errors.Join(merr, fmt.Errorf("got unsupported schema version %d; supported versions are 1,2,3", feedID.Version())) + merr = errors.Join(merr, fmt.Errorf("got unsupported schema version %d; supported versions are 1,2,3,4", feedID.Version())) } return merr diff --git a/core/services/ocr2/plugins/mercury/helpers_test.go b/core/services/ocr2/plugins/mercury/helpers_test.go index 3637b806de..6a01bcf8c0 100644 --- a/core/services/ocr2/plugins/mercury/helpers_test.go +++ b/core/services/ocr2/plugins/mercury/helpers_test.go @@ -27,6 +27,7 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -121,6 +122,7 @@ type Feed struct { baseBenchmarkPrice *big.Int baseBid *big.Int baseAsk *big.Int + baseMarketStatus uint32 } func randomFeedID(version uint16) [32]byte { @@ -467,3 +469,97 @@ chainID = 1337 nativeFeedID, )) } + +func addV4MercuryJob( + t *testing.T, + node Node, + i int, + verifierAddress common.Address, + bootstrapPeerID string, + bootstrapNodePort int, + bmBridge, + bidBridge, + askBridge, + marketStatusBridge string, + servers map[string]string, + clientPubKey ed25519.PublicKey, + feedName string, + feedID [32]byte, + linkFeedID [32]byte, + nativeFeedID [32]byte, +) { + srvs := make([]string, 0, len(servers)) + for u, k := range servers { + srvs = append(srvs, fmt.Sprintf("%q = %q", u, k)) + } + serversStr := fmt.Sprintf("{ %s }", strings.Join(srvs, ", ")) + + node.AddJob(t, fmt.Sprintf(` +type = "offchainreporting2" +schemaVersion = 1 +name = "mercury-%[1]d-%[11]s" +forwardingAllowed = false +maxTaskDuration = "1s" +contractID = "%[2]s" +feedID = "0x%[10]x" +contractConfigTrackerPollInterval = "1s" +ocrKeyBundleID = "%[3]s" +p2pv2Bootstrappers = [ + "%[4]s" +] +relay = "evm" +pluginType = "mercury" +transmitterID = "%[9]x" +observationSource = """ + // Benchmark Price + price1 [type=bridge name="%[5]s" timeout="50ms" requestData="{\\"data\\":{\\"from\\":\\"ETH\\",\\"to\\":\\"USD\\"}}"]; + price1_parse [type=jsonparse path="result"]; + price1_multiply [type=multiply times=100000000 index=0]; + + price1 -> price1_parse -> price1_multiply; + + // Bid + bid [type=bridge name="%[6]s" timeout="50ms" requestData="{\\"data\\":{\\"from\\":\\"ETH\\",\\"to\\":\\"USD\\"}}"]; + bid_parse [type=jsonparse path="result"]; + bid_multiply [type=multiply times=100000000 index=1]; + + bid -> bid_parse -> bid_multiply; + + // Ask + ask [type=bridge name="%[7]s" timeout="50ms" requestData="{\\"data\\":{\\"from\\":\\"ETH\\",\\"to\\":\\"USD\\"}}"]; + ask_parse [type=jsonparse path="result"]; + ask_multiply [type=multiply times=100000000 index=2]; + + ask -> ask_parse -> ask_multiply; + + // Market Status + marketstatus [type=bridge name="%[14]s" timeout="50ms" requestData="{\\"data\\":{\\"from\\":\\"ETH\\",\\"to\\":\\"USD\\"}}"]; + marketstatus_parse [type=jsonparse path="result" index=3]; + + marketstatus -> marketstatus_parse; +""" + +[pluginConfig] +servers = %[8]s +linkFeedID = "0x%[12]x" +nativeFeedID = "0x%[13]x" + +[relayConfig] +chainID = 1337 + `, + i, + verifierAddress, + node.KeyBundle.ID(), + fmt.Sprintf("%s@127.0.0.1:%d", bootstrapPeerID, bootstrapNodePort), + bmBridge, + bidBridge, + askBridge, + serversStr, + clientPubKey, + feedID, + feedName, + linkFeedID, + nativeFeedID, + marketStatusBridge, + )) +} diff --git a/core/services/ocr2/plugins/mercury/integration_test.go b/core/services/ocr2/plugins/mercury/integration_test.go index 832a39237e..6f9f373256 100644 --- a/core/services/ocr2/plugins/mercury/integration_test.go +++ b/core/services/ocr2/plugins/mercury/integration_test.go @@ -24,25 +24,24 @@ import ( "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/hashicorp/consul/sdk/freeport" "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/libocr/offchainreporting2plus/confighelper" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/wsrpc/credentials" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest/observer" mercurytypes "github.com/smartcontractkit/chainlink-common/pkg/types/mercury" v1 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v1" v2 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v2" v3 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v3" + v4 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v4" datastreamsmercury "github.com/smartcontractkit/chainlink-data-streams/mercury" - "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" - token "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" "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" @@ -56,6 +55,7 @@ import ( reportcodecv1 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v1/reportcodec" reportcodecv2 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v2/reportcodec" reportcodecv3 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v3/reportcodec" + reportcodecv4 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v4/reportcodec" "github.com/smartcontractkit/chainlink/v2/core/store/models" ) @@ -98,11 +98,11 @@ func setupBlockchain(t *testing.T) (*bind.TransactOpts, *backends.SimulatedBacke t.Cleanup(stopMining) // Deploy contracts - linkTokenAddress, _, linkToken, err := token.DeployLinkToken(steve, backend) + linkTokenAddress, _, linkToken, err := link_token_interface.DeployLinkToken(steve, backend) require.NoError(t, err) _, err = linkToken.Transfer(steve, steve.From, big.NewInt(1000)) require.NoError(t, err) - nativeTokenAddress, _, nativeToken, err := token.DeployLinkToken(steve, backend) + nativeTokenAddress, _, nativeToken, err := link_token_interface.DeployLinkToken(steve, backend) require.NoError(t, err) _, err = nativeToken.Transfer(steve, steve.From, big.NewInt(1000)) require.NoError(t, err) @@ -146,9 +146,9 @@ func integration_MercuryV1(t *testing.T) { pError := atomic.Int64{} // feeds - btcFeed := Feed{"BTC/USD", randomFeedID(1), big.NewInt(20_000 * multiplier), big.NewInt(19_997 * multiplier), big.NewInt(20_004 * multiplier)} - ethFeed := Feed{"ETH/USD", randomFeedID(1), big.NewInt(1_568 * multiplier), big.NewInt(1_566 * multiplier), big.NewInt(1_569 * multiplier)} - linkFeed := Feed{"LINK/USD", randomFeedID(1), big.NewInt(7150 * multiplier / 1000), big.NewInt(7123 * multiplier / 1000), big.NewInt(7177 * multiplier / 1000)} + btcFeed := Feed{"BTC/USD", randomFeedID(1), big.NewInt(20_000 * multiplier), big.NewInt(19_997 * multiplier), big.NewInt(20_004 * multiplier), 0} + ethFeed := Feed{"ETH/USD", randomFeedID(1), big.NewInt(1_568 * multiplier), big.NewInt(1_566 * multiplier), big.NewInt(1_569 * multiplier), 0} + linkFeed := Feed{"LINK/USD", randomFeedID(1), big.NewInt(7150 * multiplier / 1000), big.NewInt(7123 * multiplier / 1000), big.NewInt(7177 * multiplier / 1000), 0} feeds := []Feed{btcFeed, ethFeed, linkFeed} feedM := make(map[[32]byte]Feed, len(feeds)) for i := range feeds { @@ -1036,3 +1036,308 @@ func integration_MercuryV3(t *testing.T) { } }) } + +func TestIntegration_MercuryV4(t *testing.T) { + t.Parallel() + + integration_MercuryV4(t) +} + +func integration_MercuryV4(t *testing.T) { + ctx := testutils.Context(t) + var logObservers []*observer.ObservedLogs + t.Cleanup(func() { + detectPanicLogs(t, logObservers) + }) + + testStartTimeStamp := uint32(time.Now().Unix()) + + // test vars + // pError is the probability that an EA will return an error instead of a result, as integer percentage + // pError = 0 means it will never return error + pError := atomic.Int64{} + + // feeds + btcFeed := Feed{ + name: "BTC/USD", + id: randomFeedID(4), + baseBenchmarkPrice: big.NewInt(20_000 * multiplier), + baseBid: big.NewInt(19_997 * multiplier), + baseAsk: big.NewInt(20_004 * multiplier), + baseMarketStatus: 1, + } + ethFeed := Feed{ + name: "ETH/USD", + id: randomFeedID(4), + baseBenchmarkPrice: big.NewInt(1_568 * multiplier), + baseBid: big.NewInt(1_566 * multiplier), + baseAsk: big.NewInt(1_569 * multiplier), + baseMarketStatus: 2, + } + linkFeed := Feed{ + name: "LINK/USD", + id: randomFeedID(4), + baseBenchmarkPrice: big.NewInt(7150 * multiplier / 1000), + baseBid: big.NewInt(7123 * multiplier / 1000), + baseAsk: big.NewInt(7177 * multiplier / 1000), + baseMarketStatus: 3, + } + feeds := []Feed{btcFeed, ethFeed, linkFeed} + feedM := make(map[[32]byte]Feed, len(feeds)) + for i := range feeds { + feedM[feeds[i].id] = feeds[i] + } + + clientCSAKeys := make([]csakey.KeyV2, n+1) + clientPubKeys := make([]ed25519.PublicKey, n+1) + for i := 0; i < n+1; i++ { + k := big.NewInt(int64(i)) + key := csakey.MustNewV2XXXTestingOnly(k) + clientCSAKeys[i] = key + clientPubKeys[i] = key.PublicKey + } + + // Test multi-send to three servers + const nSrvs = 3 + reqChs := make([]chan request, nSrvs) + servers := make(map[string]string) + for i := 0; i < nSrvs; i++ { + k := csakey.MustNewV2XXXTestingOnly(big.NewInt(int64(-(i + 1)))) + reqs := make(chan request, 100) + srv := NewMercuryServer(t, ed25519.PrivateKey(k.Raw()), reqs, func() []byte { + report, err := (&reportcodecv4.ReportCodec{}).BuildReport(v4.ReportFields{BenchmarkPrice: big.NewInt(234567), Bid: big.NewInt(1), Ask: big.NewInt(1), LinkFee: big.NewInt(1), NativeFee: big.NewInt(1), MarketStatus: 1}) + if err != nil { + panic(err) + } + return report + }) + serverURL := startMercuryServer(t, srv, clientPubKeys) + reqChs[i] = reqs + servers[serverURL] = fmt.Sprintf("%x", k.PublicKey) + } + chainID := testutils.SimulatedChainID + + steve, backend, verifier, verifierAddress := setupBlockchain(t) + + // Setup bootstrap + oracle nodes + bootstrapNodePort := freeport.GetOne(t) + appBootstrap, bootstrapPeerID, _, bootstrapKb, observedLogs := setupNode(t, bootstrapNodePort, "bootstrap_mercury", backend, clientCSAKeys[n]) + bootstrapNode := Node{App: appBootstrap, KeyBundle: bootstrapKb} + logObservers = append(logObservers, observedLogs) + + // Commit blocks to finality depth to ensure LogPoller has finalized blocks to read from + ch, err := bootstrapNode.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() + } + + // Set up n oracles + var ( + oracles []confighelper.OracleIdentityExtra + nodes []Node + ) + ports := freeport.GetN(t, n) + for i := 0; i < n; i++ { + app, peerID, transmitter, kb, observedLogs := setupNode(t, ports[i], fmt.Sprintf("oracle_mercury%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(), + }) + logObservers = append(logObservers, observedLogs) + } + + for _, feed := range feeds { + addBootstrapJob(t, bootstrapNode, chainID, verifierAddress, feed.name, feed.id) + } + + createBridge := func(name string, i int, p *big.Int, marketStatus uint32, borm bridges.ORM) (bridgeName string) { + bridge := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + b, herr := io.ReadAll(req.Body) + require.NoError(t, herr) + require.Equal(t, `{"data":{"from":"ETH","to":"USD"}}`, string(b)) + + r := rand.Int63n(101) + if r > pError.Load() { + res.WriteHeader(http.StatusOK) + + var val string + if p != nil { + val = decimal.NewFromBigInt(p, 0).Div(decimal.NewFromInt(multiplier)).Add(decimal.NewFromInt(int64(i)).Div(decimal.NewFromInt(100))).String() + } else { + val = fmt.Sprintf("%d", marketStatus) + } + + resp := fmt.Sprintf(`{"result": %s}`, val) + _, herr = res.Write([]byte(resp)) + require.NoError(t, herr) + } else { + res.WriteHeader(http.StatusInternalServerError) + resp := `{"error": "pError test error"}` + _, herr = res.Write([]byte(resp)) + require.NoError(t, herr) + } + })) + t.Cleanup(bridge.Close) + u, _ := url.Parse(bridge.URL) + bridgeName = fmt.Sprintf("bridge-%s-%d", name, i) + require.NoError(t, borm.CreateBridgeType(ctx, &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, feed := range feeds { + bmBridge := createBridge(fmt.Sprintf("benchmarkprice-%d", j), i, feed.baseBenchmarkPrice, 0, node.App.BridgeORM()) + bidBridge := createBridge(fmt.Sprintf("bid-%d", j), i, feed.baseBid, 0, node.App.BridgeORM()) + askBridge := createBridge(fmt.Sprintf("ask-%d", j), i, feed.baseAsk, 0, node.App.BridgeORM()) + marketStatusBridge := createBridge(fmt.Sprintf("marketstatus-%d", j), i, nil, feed.baseMarketStatus, node.App.BridgeORM()) + + addV4MercuryJob( + t, + node, + i, + verifierAddress, + bootstrapPeerID, + bootstrapNodePort, + bmBridge, + bidBridge, + askBridge, + marketStatusBridge, + servers, + clientPubKeys[i], + feed.name, + feed.id, + randomFeedID(2), + randomFeedID(2), + ) + } + } + + // Setup config on contract + onchainConfig, err := (datastreamsmercury.StandardOnchainConfigCodec{}).Encode(rawOnchainConfig) + require.NoError(t, err) + + reportingPluginConfig, err := json.Marshal(rawReportingPluginConfig) + require.NoError(t, err) + + signers, _, _, onchainConfig, offchainConfigVersion, offchainConfig, err := ocr3confighelper.ContractSetConfigArgsForTestsMercuryV02( + 2*time.Second, // DeltaProgress + 20*time.Second, // DeltaResend + 400*time.Millisecond, // DeltaInitial + 100*time.Millisecond, // DeltaRound + 0, // DeltaGrace + 300*time.Millisecond, // DeltaCertifiedCommitRequest + 1*time.Minute, // DeltaStage + 100, // rMax + []int{len(nodes)}, // S + oracles, + reportingPluginConfig, // reportingPluginConfig []byte, + 250*time.Millisecond, // Max duration observation + int(f), // f + onchainConfig, + ) + + require.NoError(t, err) + signerAddresses, err := evm.OnchainPublicKeyToAddress(signers) + require.NoError(t, err) + + offchainTransmitters := make([][32]byte, n) + for i := 0; i < n; i++ { + offchainTransmitters[i] = nodes[i].ClientPubKey + } + + for _, feed := range feeds { + _, ferr := verifier.SetConfig( + steve, + feed.id, + signerAddresses, + offchainTransmitters, + f, + onchainConfig, + offchainConfigVersion, + offchainConfig, + nil, + ) + require.NoError(t, ferr) + backend.Commit() + } + + runTestSetup := func(reqs chan request) { + // Expect at least one report per feed from each oracle, per server + seen := make(map[[32]byte]map[credentials.StaticSizedPublicKey]struct{}) + for i := range feeds { + // feedID will be deleted when all n oracles have reported + seen[feeds[i].id] = make(map[credentials.StaticSizedPublicKey]struct{}, n) + } + + for req := range reqs { + v := make(map[string]interface{}) + err := mercury.PayloadTypes.UnpackIntoMap(v, req.req.Payload) + require.NoError(t, err) + report, exists := v["report"] + if !exists { + t.Fatalf("expected payload %#v to contain 'report'", v) + } + reportElems := make(map[string]interface{}) + err = reportcodecv4.ReportTypes.UnpackIntoMap(reportElems, report.([]byte)) + require.NoError(t, err) + + feedID := reportElems["feedId"].([32]uint8) + feed, exists := feedM[feedID] + require.True(t, exists) + + if _, exists := seen[feedID]; !exists { + continue // already saw all oracles for this feed + } + + expectedFee := datastreamsmercury.CalculateFee(big.NewInt(234567), rawReportingPluginConfig.BaseUSDFee) + expectedExpiresAt := reportElems["observationsTimestamp"].(uint32) + rawReportingPluginConfig.ExpirationWindow + + assert.GreaterOrEqual(t, int(reportElems["observationsTimestamp"].(uint32)), int(testStartTimeStamp)) + assert.InDelta(t, feed.baseBenchmarkPrice.Int64(), reportElems["benchmarkPrice"].(*big.Int).Int64(), 5000000) + assert.InDelta(t, feed.baseBid.Int64(), reportElems["bid"].(*big.Int).Int64(), 5000000) + assert.InDelta(t, feed.baseAsk.Int64(), reportElems["ask"].(*big.Int).Int64(), 5000000) + assert.NotZero(t, reportElems["validFromTimestamp"].(uint32)) + assert.GreaterOrEqual(t, reportElems["observationsTimestamp"].(uint32), reportElems["validFromTimestamp"].(uint32)) + assert.Equal(t, expectedExpiresAt, reportElems["expiresAt"].(uint32)) + assert.Equal(t, expectedFee, reportElems["linkFee"].(*big.Int)) + assert.Equal(t, expectedFee, reportElems["nativeFee"].(*big.Int)) + assert.Equal(t, feed.baseMarketStatus, reportElems["marketStatus"].(uint32)) + + t.Logf("oracle %x reported for feed %s (0x%x)", req.pk, feed.name, feed.id) + + seen[feedID][req.pk] = struct{}{} + if len(seen[feedID]) == n { + t.Logf("all oracles reported for feed %s (0x%x)", feed.name, feed.id) + delete(seen, feedID) + if len(seen) == 0 { + break // saw all oracles; success! + } + } + } + } + + t.Run("receives at least one report per feed for every server from each oracle when EAs are at 100% reliability", func(t *testing.T) { + for i := 0; i < nSrvs; i++ { + reqs := reqChs[i] + runTestSetup(reqs) + } + }) +} diff --git a/core/services/ocr2/plugins/mercury/plugin.go b/core/services/ocr2/plugins/mercury/plugin.go index c5eba78b0d..0898c1821e 100644 --- a/core/services/ocr2/plugins/mercury/plugin.go +++ b/core/services/ocr2/plugins/mercury/plugin.go @@ -13,6 +13,7 @@ import ( relaymercuryv1 "github.com/smartcontractkit/chainlink-data-streams/mercury/v1" relaymercuryv2 "github.com/smartcontractkit/chainlink-data-streams/mercury/v2" relaymercuryv3 "github.com/smartcontractkit/chainlink-data-streams/mercury/v3" + relaymercuryv4 "github.com/smartcontractkit/chainlink-data-streams/mercury/v4" "github.com/smartcontractkit/chainlink-common/pkg/loop" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" @@ -29,6 +30,7 @@ import ( mercuryv1 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v1" mercuryv2 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v2" mercuryv3 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v3" + mercuryv4 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v4" "github.com/smartcontractkit/chainlink/v2/plugins" ) @@ -136,6 +138,13 @@ func NewServices( return nil, fmt.Errorf("failed to create mercury v3 factory: %w", err) } srvs = append(srvs, factoryServices...) + case 4: + factory, factoryServices, err = newv4factory(fCfg) + if err != nil { + abort() + return nil, fmt.Errorf("failed to create mercury v4 factory: %w", err) + } + srvs = append(srvs, factoryServices...) default: return nil, errors.Errorf("unknown Mercury report schema version: %d", feedID.Version()) } @@ -162,6 +171,43 @@ type factoryCfg struct { feedID utils.FeedID } +func newv4factory(factoryCfg factoryCfg) (ocr3types.MercuryPluginFactory, []job.ServiceCtx, error) { + var factory ocr3types.MercuryPluginFactory + srvs := make([]job.ServiceCtx, 0) + + ds := mercuryv4.NewDataSource( + factoryCfg.orm, + factoryCfg.pipelineRunner, + factoryCfg.jb, + *factoryCfg.jb.PipelineSpec, + factoryCfg.feedID, + factoryCfg.lggr, + factoryCfg.saver, + factoryCfg.chEnhancedTelem, + factoryCfg.ocr2Provider.MercuryServerFetcher(), + *factoryCfg.reportingPluginConfig.LinkFeedID, + *factoryCfg.reportingPluginConfig.NativeFeedID, + ) + + loopCmd := env.MercuryPlugin.Cmd.Get() + loopEnabled := loopCmd != "" + + if loopEnabled { + cmdFn, opts, mercuryLggr, err := initLoop(loopCmd, factoryCfg.cfg, factoryCfg.feedID, factoryCfg.lggr) + if err != nil { + return nil, nil, fmt.Errorf("failed to init loop for feed %s: %w", factoryCfg.feedID, err) + } + // in loop mode, the factory is grpc server, and we need to handle the server lifecycle + factoryServer := loop.NewMercuryV4Service(mercuryLggr, opts, cmdFn, factoryCfg.ocr2Provider, ds) + srvs = append(srvs, factoryServer) + // adapt the grpc server to the vanilla mercury plugin factory interface used by the oracle + factory = factoryServer + } else { + factory = relaymercuryv4.NewFactory(ds, factoryCfg.lggr, factoryCfg.ocr2Provider.OnchainConfigCodec(), factoryCfg.ocr2Provider.ReportCodecV4()) + } + return factory, srvs, nil +} + func newv3factory(factoryCfg factoryCfg) (ocr3types.MercuryPluginFactory, []job.ServiceCtx, error) { var factory ocr3types.MercuryPluginFactory srvs := make([]job.ServiceCtx, 0) diff --git a/core/services/ocr2/plugins/mercury/plugin_test.go b/core/services/ocr2/plugins/mercury/plugin_test.go index 95aaabec14..f9bef4a3f1 100644 --- a/core/services/ocr2/plugins/mercury/plugin_test.go +++ b/core/services/ocr2/plugins/mercury/plugin_test.go @@ -21,6 +21,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" + v4 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v4" mercuryocr2 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/mercury" @@ -37,6 +38,7 @@ var ( v1FeedId = [32]uint8{00, 01, 107, 74, 167, 229, 124, 167, 182, 138, 225, 191, 69, 101, 63, 86, 182, 86, 253, 58, 163, 53, 239, 127, 174, 105, 107, 102, 63, 27, 132, 114} v2FeedId = [32]uint8{00, 02, 107, 74, 167, 229, 124, 167, 182, 138, 225, 191, 69, 101, 63, 86, 182, 86, 253, 58, 163, 53, 239, 127, 174, 105, 107, 102, 63, 27, 132, 114} v3FeedId = [32]uint8{00, 03, 107, 74, 167, 229, 124, 167, 182, 138, 225, 191, 69, 101, 63, 86, 182, 86, 253, 58, 163, 53, 239, 127, 174, 105, 107, 102, 63, 27, 132, 114} + v4FeedId = [32]uint8{00, 04, 107, 74, 167, 229, 124, 167, 182, 138, 225, 191, 69, 101, 63, 86, 182, 86, 253, 58, 163, 53, 239, 127, 174, 105, 107, 102, 63, 27, 132, 114} testArgsNoPlugin = libocr2.MercuryOracleArgs{ LocalConfig: libocr2types.LocalConfig{ @@ -66,6 +68,13 @@ var ( "nativeFeedID": "0x00036b4aa7e57ca7b68ae1bf45653f56b656fd3aa335ef7fae696b663f1b8472", } + v4jsonCfg = job.JSONConfig{ + "serverURL": "example.com:80", + "serverPubKey": "724ff6eae9e900270edfff233e16322a70ec06e1a6e62a81ef13921f398f6c93", + "linkFeedID": "0x00026b4aa7e57ca7b68ae1bf45653f56b656fd3aa335ef7fae696b663f1b8472", + "nativeFeedID": "0x00036b4aa7e57ca7b68ae1bf45653f56b656fd3aa335ef7fae696b663f1b8472", + } + testJob = job.Job{ ID: 1, ExternalJobID: uuid.Must(uuid.NewRandom()), @@ -135,6 +144,15 @@ func TestNewServices(t *testing.T) { wantServiceCnt: expectedEmbeddedServiceCnt, wantErr: false, }, + { + name: "v4 legacy", + args: args{ + pluginConfig: v4jsonCfg, + feedID: v4FeedId, + }, + wantServiceCnt: expectedEmbeddedServiceCnt, + wantErr: false, + }, { name: "v1 loop", loopMode: true, @@ -168,6 +186,17 @@ func TestNewServices(t *testing.T) { wantErr: false, wantLoopFactory: &loop.MercuryV3Service{}, }, + { + name: "v4 loop", + loopMode: true, + args: args{ + pluginConfig: v4jsonCfg, + feedID: v4FeedId, + }, + wantServiceCnt: expectedLoopServiceCnt, + wantErr: false, + wantLoopFactory: &loop.MercuryV4Service{}, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -259,6 +288,9 @@ func (*testProvider) ReportCodecV2() v2.ReportCodec { return nil } // ReportCodecV3 implements types.MercuryProvider. func (*testProvider) ReportCodecV3() v3.ReportCodec { return nil } +// ReportCodecV4 implements types.MercuryProvider. +func (*testProvider) ReportCodecV4() v4.ReportCodec { return nil } + // Start implements types.MercuryProvider. func (*testProvider) Start(context.Context) error { panic("unimplemented") } diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/block_subscriber.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/block_subscriber.go index d07af8a8de..a5a0054217 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/block_subscriber.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/block_subscriber.go @@ -9,14 +9,13 @@ import ( "github.com/ethereum/go-ethereum/common" - ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" - + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -74,7 +73,7 @@ func NewBlockSubscriber(hb httypes.HeadBroadcaster, lp logpoller.LogPoller, fina blockSize: lookbackDepth, finalityDepth: finalityDepth, latestBlock: atomic.Pointer[ocr2keepers.BlockKey]{}, - lggr: lggr.Named("BlockSubscriber"), + lggr: logger.Named(lggr, "BlockSubscriber"), } } diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/gasprice/gasprice.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/gasprice/gasprice.go index 36460683d4..095972fbf5 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/gasprice/gasprice.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/gasprice/gasprice.go @@ -6,10 +6,11 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/v2/core/cbor" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" ) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer_v1.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer_v1.go index e58d5ad9c9..00a56496a0 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer_v1.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer_v1.go @@ -6,8 +6,9 @@ import ( "sync" "sync/atomic" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "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" ) @@ -79,7 +80,7 @@ type logBuffer struct { func NewLogBuffer(lggr logger.Logger, lookback, blockRate, logLimit uint32) LogBuffer { return &logBuffer{ - lggr: lggr.Named("KeepersRegistry.LogEventBufferV1"), + lggr: logger.Sugared(lggr).Named("KeepersRegistry").Named("LogEventBufferV1"), opts: newLogBufferOptions(lookback, blockRate, logLimit), lastBlockSeen: new(atomic.Int64), queueIDs: []string{}, @@ -313,7 +314,7 @@ type upkeepLogQueue struct { func newUpkeepLogQueue(lggr logger.Logger, id *big.Int, opts *logBufferOptions) *upkeepLogQueue { return &upkeepLogQueue{ - lggr: lggr.With("upkeepID", id.String()), + lggr: logger.With(lggr, "upkeepID", id.String()), id: id, opts: opts, logs: map[int64][]logpoller.Log{}, diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer_v1_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer_v1_test.go index f742d39689..4c46b9b3fe 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer_v1_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer_v1_test.go @@ -54,8 +54,6 @@ func TestLogEventBufferV1_SyncFilters(t *testing.T) { type readableLogger struct { logger.Logger DebugwFn func(msg string, keysAndValues ...interface{}) - NamedFn func(name string) logger.Logger - WithFn func(args ...interface{}) logger.Logger } func (l *readableLogger) Debugw(msg string, keysAndValues ...interface{}) { @@ -74,6 +72,7 @@ func TestLogEventBufferV1_EnqueueViolations(t *testing.T) { t.Run("enqueuing logs for a block older than latest seen logs a message", func(t *testing.T) { logReceived := false readableLogger := &readableLogger{ + Logger: logger.TestLogger(t), DebugwFn: func(msg string, keysAndValues ...interface{}) { if msg == "enqueuing logs from a block older than latest seen block" { logReceived = true @@ -103,6 +102,7 @@ func TestLogEventBufferV1_EnqueueViolations(t *testing.T) { t.Run("enqueuing logs for the same block over multiple calls logs a message", func(t *testing.T) { logReceived := false readableLogger := &readableLogger{ + Logger: logger.TestLogger(t), DebugwFn: func(msg string, keysAndValues ...interface{}) { if msg == "enqueuing logs again for a previously seen block" { logReceived = true diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/factory.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/factory.go index 7ec65ff474..57b48841a2 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/factory.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/factory.go @@ -4,9 +4,10 @@ import ( "math/big" "time" + "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/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" ) @@ -74,7 +75,7 @@ func (o *LogTriggersOptions) Defaults(finalityDepth int64) { func (o *LogTriggersOptions) defaultBlockRate() uint32 { switch o.chainID.Int64() { - case 42161, 421613, 421614: // Arbitrum + case 42161, 421613, 421614: // Arbitrum, Arb Goerli, Arb Sepolia return 2 default: return 1 @@ -83,10 +84,10 @@ func (o *LogTriggersOptions) defaultBlockRate() uint32 { func (o *LogTriggersOptions) defaultLogLimit() uint32 { switch o.chainID.Int64() { - case 1, 4, 5, 42, 11155111: // Eth + case 1, 4, 5, 42, 11155111: // Eth, Rinkeby, Goerli, Kovan, Sepolia return 20 - case 10, 420, 56, 97, 137, 80001, 43113, 43114, 8453, 84531: // Optimism, BSC, Polygon, Avax, Base - return 5 + case 10, 420, 11155420, 56, 97, 137, 80001, 80002, 43114, 43113, 8453, 84531, 84532: // Optimism, OP Goerli, OP Sepolia, BSC, BSC Test, Polygon, Mumbai, Amoy, Avax, Avax Fuji, Base, Base Goerli, Base Sepolia + return 4 default: return 1 } 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 f1de1ef512..50b2ebc0d0 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider.go @@ -16,13 +16,12 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" - + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" ac "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_compatible_utils" - "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/utils" @@ -111,7 +110,7 @@ type logEventProvider struct { func NewLogProvider(lggr logger.Logger, poller logpoller.LogPoller, chainID *big.Int, packer LogDataPacker, filterStore UpkeepFilterStore, opts LogTriggersOptions) *logEventProvider { return &logEventProvider{ threadCtrl: utils.NewThreadControl(), - lggr: lggr.Named("KeepersRegistry.LogEventProvider"), + lggr: logger.Named(lggr, "KeepersRegistry.LogEventProvider"), packer: packer, buffer: NewLogBuffer(lggr, uint32(opts.LookbackBlocks), opts.BlockRate, opts.LogLimit), poller: poller, @@ -135,7 +134,7 @@ func (p *logEventProvider) SetConfig(cfg ocr2keepers.LogEventProviderConfig) { logLimit = p.opts.defaultLogLimit() } - p.lggr.With("where", "setConfig").Infow("setting config ", "bockRate", blockRate, "logLimit", logLimit) + p.lggr.Infow("setting config", "where", "setConfig", "bockRate", blockRate, "logLimit", logLimit) atomic.StoreUint32(&p.opts.BlockRate, blockRate) atomic.StoreUint32(&p.opts.LogLimit, logLimit) @@ -156,7 +155,7 @@ func (p *logEventProvider) Start(context.Context) error { } p.threadCtrl.Go(func(ctx context.Context) { - lggr := p.lggr.With("where", "scheduler") + lggr := logger.With(p.lggr, "where", "scheduler") p.scheduleReadJobs(ctx, func(ids []*big.Int) { select { @@ -369,7 +368,7 @@ func (p *logEventProvider) startReader(pctx context.Context, readQ <-chan []*big ctx, cancel := context.WithCancel(pctx) defer cancel() - lggr := p.lggr.With("where", "reader") + lggr := logger.With(p.lggr, "where", "reader") for { select { diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider_life_cycle.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider_life_cycle.go index db47ac2ecd..cbd493bf2e 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider_life_cycle.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider_life_cycle.go @@ -10,6 +10,8 @@ import ( "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" ) @@ -106,7 +108,7 @@ func (p *logEventProvider) register(ctx context.Context, lpFilter logpoller.Filt if err != nil { return fmt.Errorf("failed to get latest block while registering filter: %w", err) } - lggr := p.lggr.With("upkeepID", ufilter.upkeepID.String()) + lggr := logger.With(p.lggr, "upkeepID", ufilter.upkeepID.String()) logPollerHasFilter := p.poller.HasFilter(lpFilter.Name) filterStoreHasFilter := p.filterStore.Has(ufilter.upkeepID) if filterStoreHasFilter { 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 9e41008ed8..984856bf3c 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/recoverer.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/recoverer.go @@ -14,18 +14,17 @@ import ( "sync/atomic" "time" - "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - "github.com/ethereum/go-ethereum/common" - "github.com/smartcontractkit/chainlink-automation/pkg/v3/random" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/services" ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" - "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/random" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" - "github.com/smartcontractkit/chainlink/v2/core/logger" "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/utils" @@ -71,7 +70,7 @@ type logRecoverer struct { services.StateMachine threadCtrl utils.ThreadControl - lggr logger.Logger + lggr logger.SugaredLogger lookbackBlocks *atomic.Int64 blockTime *atomic.Int64 @@ -96,7 +95,7 @@ var _ LogRecoverer = &logRecoverer{} func NewLogRecoverer(lggr logger.Logger, poller logpoller.LogPoller, client client.Client, stateStore core.UpkeepStateReader, packer LogDataPacker, filterStore UpkeepFilterStore, opts LogTriggersOptions) *logRecoverer { rec := &logRecoverer{ - lggr: lggr.Named(LogRecovererServiceName), + lggr: logger.Sugared(lggr).Named(LogRecovererServiceName), threadCtrl: utils.NewThreadControl(), diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go index 5a4b701f61..1700593921 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go @@ -15,11 +15,11 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/patrickmn/go-cache" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" autov2common "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_automation_v21_plus_common" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" @@ -119,7 +119,7 @@ func (s *streams) Lookup(ctx context.Context, checkResults []ocr2keepers.CheckRe // buildResult checks if the upkeep is allowed by Mercury and builds a streams lookup request from the check result func (s *streams) buildResult(ctx context.Context, i int, checkResult ocr2keepers.CheckResult, checkResults []ocr2keepers.CheckResult, lookups map[int]*mercury.StreamsLookup) { - lookupLggr := s.lggr.With("where", "StreamsLookup") + lookupLggr := logger.Sugared(s.lggr).With("where", "StreamsLookup") if checkResult.IneligibilityReason != uint8(encoding.UpkeepFailureReasonTargetCheckReverted) { // Streams Lookup only works when upkeep target check reverts prommetrics.AutomationStreamsLookupError.WithLabelValues(prommetrics.StreamsLookupErrorReasonNotReverted).Inc() diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/request.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/request.go index 5e954475a8..c02b7c10de 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/request.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/request.go @@ -11,14 +11,14 @@ import ( "strconv" "time" - automationTypes "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/avast/retry-go/v4" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/avast/retry-go/v4" - "github.com/ethereum/go-ethereum/common/hexutil" + automationTypes "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/prommetrics" diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/request.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/request.go index 39a26b6b5d..c2ffb2172b 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/request.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/request.go @@ -10,14 +10,14 @@ import ( "strings" "time" - automationTypes "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/avast/retry-go/v4" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/avast/retry-go/v4" - "github.com/ethereum/go-ethereum/common/hexutil" + automationTypes "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/prommetrics" diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/payload_builder.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/payload_builder.go index 7f29cb3b7a..c6262899a3 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/payload_builder.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/payload_builder.go @@ -3,11 +3,11 @@ package evm import ( "context" - "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - + "github.com/smartcontractkit/chainlink-common/pkg/logger" ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" - "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "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/logprovider" ) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry.go index 16b8627cf7..25bd7a445e 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry.go @@ -10,10 +10,6 @@ import ( "sync" "time" - types2 "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - - "github.com/smartcontractkit/chainlink-common/pkg/types" - "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" @@ -22,19 +18,20 @@ import ( "github.com/pkg/errors" "go.uber.org/multierr" - evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" - + "github.com/smartcontractkit/chainlink-common/pkg/types" ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" + types2 "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" ac "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_automation_v21_plus_common" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider" @@ -98,7 +95,7 @@ func NewEvmRegistry( return &EvmRegistry{ stopCh: make(chan struct{}), threadCtrl: utils.NewThreadControl(), - lggr: lggr.Named(RegistryServiceName), + lggr: logger.Sugared(lggr).Named(RegistryServiceName), poller: client.LogPoller(), addr: addr, client: client.Client(), @@ -175,7 +172,7 @@ func (c *MercuryConfig) SetPluginRetry(k string, v interface{}, d time.Duration) type EvmRegistry struct { services.StateMachine threadCtrl utils.ThreadControl - lggr logger.Logger + lggr logger.SugaredLogger poller logpoller.LogPoller addr common.Address client client.Client diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline_test.go index 6f8785fda7..cb014e1d3e 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline_test.go @@ -8,10 +8,6 @@ import ( "sync/atomic" "testing" - types3 "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - - types2 "github.com/smartcontractkit/chainlink-common/pkg/types" - "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/rpc" @@ -20,8 +16,12 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + types2 "github.com/smartcontractkit/chainlink-common/pkg/types" ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" + types3 "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + evmClientMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" gasMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" @@ -30,7 +30,6 @@ import ( ac "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_automation_v21_plus_common" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/streams_lookup_compatible_interface" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mocks" @@ -82,7 +81,7 @@ func TestRegistry_GetBlockAndUpkeepId(t *testing.T) { } func TestRegistry_VerifyCheckBlock(t *testing.T) { - lggr := logger.TestLogger(t) + lggr := logger.Test(t) upkeepId := ocr2keepers.UpkeepIdentifier{} upkeepId.FromBigInt(big.NewInt(12345)) tests := []struct { @@ -197,7 +196,7 @@ func TestRegistry_VerifyCheckBlock(t *testing.T) { } bs.latestBlock.Store(tc.latestBlock) e := &EvmRegistry{ - lggr: lggr, + lggr: logger.Sugared(lggr), bs: bs, poller: tc.poller, } @@ -229,7 +228,7 @@ func (p *mockLogPoller) IndexedLogs(ctx context.Context, eventSig common.Hash, a } func TestRegistry_VerifyLogExists(t *testing.T) { - lggr := logger.TestLogger(t) + lggr := logger.Test(t) upkeepId := ocr2keepers.UpkeepIdentifier{} upkeepId.FromBigInt(big.NewInt(12345)) @@ -351,7 +350,7 @@ func TestRegistry_VerifyLogExists(t *testing.T) { blocks: tc.blocks, } e := &EvmRegistry{ - lggr: lggr, + lggr: logger.Sugared(lggr), bs: bs, } @@ -379,7 +378,7 @@ func TestRegistry_VerifyLogExists(t *testing.T) { } func TestRegistry_CheckUpkeeps(t *testing.T) { - lggr := logger.TestLogger(t) + lggr := logger.Test(t) uid0 := core.GenUpkeepID(types3.UpkeepType(0), "p0") uid1 := core.GenUpkeepID(types3.UpkeepType(1), "p1") uid2 := core.GenUpkeepID(types3.UpkeepType(1), "p2") @@ -509,7 +508,7 @@ func TestRegistry_CheckUpkeeps(t *testing.T) { } bs.latestBlock.Store(tc.latestBlock) e := &EvmRegistry{ - lggr: lggr, + lggr: logger.Sugared(lggr), bs: bs, poller: tc.poller, } @@ -669,7 +668,7 @@ func TestRegistry_SimulatePerformUpkeeps(t *testing.T) { // setups up an evm registry for tests. func setupEVMRegistry(t *testing.T) *EvmRegistry { - lggr := logger.TestLogger(t) + lggr := logger.Test(t) addr := common.HexToAddress("0x6cA639822c6C241Fa9A7A6b5032F6F7F1C513CAD") keeperRegistryABI, err := abi.JSON(strings.NewReader(ac.IAutomationV21PlusCommonABI)) require.Nil(t, err, "need registry abi") @@ -682,7 +681,7 @@ func setupEVMRegistry(t *testing.T) *EvmRegistry { ge := gasMocks.NewEvmFeeEstimator(t) r := &EvmRegistry{ - lggr: lggr, + lggr: logger.Sugared(lggr), poller: logPoller, addr: addr, client: client, diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_test.go index ab530f877a..1a3f103dd1 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_test.go @@ -15,19 +15,19 @@ import ( "github.com/stretchr/testify/mock" types2 "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" types3 "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" ac "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_compatible_utils" autov2common "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_automation_v21_plus_common" - "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider" @@ -546,7 +546,7 @@ func TestRegistry_refreshLogTriggerUpkeeps(t *testing.T) { } { t.Run(tc.name, func(t *testing.T) { ctx := tests.Context(t) - lggr := logger.TestLogger(t) + lggr := logger.Test(t) var hb types3.HeadBroadcaster var lp logpoller.LogPoller @@ -560,7 +560,7 @@ func TestRegistry_refreshLogTriggerUpkeeps(t *testing.T) { bs: bs, registry: tc.registry, packer: tc.packer, - lggr: lggr, + lggr: logger.Sugared(lggr), } err := registry.refreshLogTriggerUpkeeps(ctx, tc.ids) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/event_provider.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/event_provider.go index f1a6468804..697f56c866 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/event_provider.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/event_provider.go @@ -6,18 +6,17 @@ import ( "fmt" "sync" - "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/services" ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" - "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" ac "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_automation_v21_plus_common" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" ) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/scanner.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/scanner.go index d11970864a..27a35ddff1 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/scanner.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/scanner.go @@ -8,10 +8,11 @@ import ( "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ac "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_automation_v21_plus_common" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider" ) @@ -43,7 +44,7 @@ func NewPerformedEventsScanner( finalityDepth uint32, ) *performedEventsScanner { return &performedEventsScanner{ - lggr: lggr.Named("EventsScanner"), + lggr: logger.Named(lggr, "EventsScanner"), poller: poller, registryAddress: registryAddress, finalityDepth: finalityDepth, diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/store.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/store.go index e6486ca56a..27cac24a9f 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/store.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/store.go @@ -8,13 +8,12 @@ import ( "sync" "time" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" ocr2keepers "github.com/smartcontractkit/chainlink-common/pkg/types/automation" - "github.com/smartcontractkit/chainlink-common/pkg/services" - ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -84,7 +83,7 @@ type upkeepStateStore struct { func NewUpkeepStateStore(orm ORM, lggr logger.Logger, scanner PerformedLogsScanner) *upkeepStateStore { return &upkeepStateStore{ orm: orm, - lggr: lggr.Named(UpkeepStateStoreServiceName), + lggr: logger.Named(lggr, UpkeepStateStoreServiceName), cache: map[string]*upkeepStateRecord{}, scanner: scanner, retention: CacheExpiration, diff --git a/core/services/ocr2/validate/validate.go b/core/services/ocr2/validate/validate.go index 9b25705c05..e4dffe137d 100644 --- a/core/services/ocr2/validate/validate.go +++ b/core/services/ocr2/validate/validate.go @@ -200,18 +200,6 @@ func (o *OCR2OnchainSigningStrategy) IsMultiChain() bool { return o.StrategyName == "multi-chain" } -func (o *OCR2OnchainSigningStrategy) PublicKey() (string, error) { - pk, ok := o.Config["publicKey"] - if !ok { - return "", nil - } - pkString, ok := pk.(string) - if !ok { - return "", fmt.Errorf("expected string publicKey value, but got: %T", pk) - } - return pkString, nil -} - func (o *OCR2OnchainSigningStrategy) ConfigCopy() job.JSONConfig { copiedConfig := make(job.JSONConfig) for k, v := range o.Config { @@ -254,13 +242,6 @@ func validateGenericPluginSpec(ctx context.Context, spec *job.OCR2OracleSpec, rc if err != nil { return err } - pk, ossErr := onchainSigningStrategy.PublicKey() - if ossErr != nil { - return ossErr - } - if pk == "" { - return errors.New("generic config invalid: must provide public key for the onchain signing strategy") - } } plugEnv := env.NewPlugin(p.PluginName) diff --git a/core/services/ocr2/validate/validate_test.go b/core/services/ocr2/validate/validate_test.go index b352445bf4..b461d0bc84 100644 --- a/core/services/ocr2/validate/validate_test.go +++ b/core/services/ocr2/validate/validate_test.go @@ -52,7 +52,6 @@ chainID = 1337 strategyName = "single-chain" [onchainSigningStrategy.config] evm = "" -publicKey = "0x1234567890123456789012345678901234567890" [pluginConfig] juelsPerFeeCoinSource = """ ds1 [type=bridge name=voter_turnout]; @@ -108,7 +107,6 @@ chainID = 1337 strategyName = "single-chain" [onchainSigningStrategy.config] evm = "" -publicKey = "0x1234567890123456789012345678901234567890" [pluginConfig] juelsPerFeeCoinSource = """ ds1 [type=bridge name=voter_turnout]; @@ -153,7 +151,6 @@ chainID = 1337 strategyName = "single-chain" [onchainSigningStrategy.config] evm = "" -publicKey = "0x1234567890123456789012345678901234567890" [pluginConfig] `, assertion: func(t *testing.T, os job.Job, err error) { @@ -177,7 +174,6 @@ chainID = 1337 strategyName = "single-chain" [onchainSigningStrategy.config] evm = "" -publicKey = "0x1234567890123456789012345678901234567890" [pluginConfig] `, assertion: func(t *testing.T, os job.Job, err error) { @@ -203,7 +199,6 @@ chainID = 1337 strategyName = "single-chain" [onchainSigningStrategy.config] evm = "" -publicKey = "0x1234567890123456789012345678901234567890" [pluginConfig] `, assertion: func(t *testing.T, os job.Job, err error) { @@ -229,7 +224,6 @@ chainID = 1337 strategyName = "single-chain" [onchainSigningStrategy.config] evm = "" -publicKey = "0x1234567890123456789012345678901234567890" [pluginConfig] `, assertion: func(t *testing.T, os job.Job, err error) { @@ -256,7 +250,6 @@ chainID = 1337 strategyName = "single-chain" [onchainSigningStrategy.config] evm = "" -publicKey = "0x1234567890123456789012345678901234567890" [pluginConfig] `, assertion: func(t *testing.T, os job.Job, err error) { @@ -282,7 +275,6 @@ chainID = 1337 strategyName = "single-chain" [onchainSigningStrategy.config] evm = "" -publicKey = "0x1234567890123456789012345678901234567890" [pluginConfig] `, assertion: func(t *testing.T, os job.Job, err error) { @@ -306,7 +298,6 @@ chainID = 1337 strategyName = "single-chain" [onchainSigningStrategy.config] evm = "" -publicKey = "0x1234567890123456789012345678901234567890" [pluginConfig] `, assertion: func(t *testing.T, os job.Job, err error) { @@ -347,7 +338,6 @@ answer1 [type=median index=0]; strategyName = "single-chain" [onchainSigningStrategy.config] evm = "" -publicKey = "0x1234567890123456789012345678901234567890" [pluginConfig] juelsPerFeeCoinSource = """ ds1 [type=bridge name=voter_turnout]; @@ -386,7 +376,6 @@ answer1 [type=median index=0]; strategyName = "single-chain" [onchainSigningStrategy.config] evm = "" -publicKey = "0x1234567890123456789012345678901234567890" [pluginConfig] juelsPerFeeCoinSource = """ -> @@ -418,7 +407,6 @@ answer1 [type=median index=0]; strategyName = "single-chain" [onchainSigningStrategy.config] evm = "" -publicKey = "0x1234567890123456789012345678901234567890" [pluginConfig] juelsPerFeeCoinSource = """ ds1 [type=bridge name=voter_turnout]; @@ -778,7 +766,6 @@ chainID = 4 strategyName = "single-chain" [onchainSigningStrategy.config] evm = "" -publicKey = "0x1234567890123456789012345678901234567890" [pluginConfig] `, @@ -810,7 +797,6 @@ chainID = 4 strategyName = "single-chain" [onchainSigningStrategy.config] evm = "" -publicKey = "0x1234567890123456789012345678901234567890" [pluginConfig] PluginName="some random name" @@ -844,7 +830,6 @@ chainID = 4 strategyName = "single-chain" [onchainSigningStrategy.config] evm = "" -publicKey = "0x1234567890123456789012345678901234567890" [pluginConfig] PluginName="some random name" @@ -879,7 +864,6 @@ chainID = 4 strategyName = "single-chain" [onchainSigningStrategy.config] evm = "" -publicKey = "0x1234567890123456789012345678901234567890" [pluginConfig] PluginName="some random name" @@ -997,7 +981,6 @@ func TestOCR2OnchainSigningStrategy_Unmarshal(t *testing.T) { strategyName = "single-chain" [onchainSigningStrategy.config] evm = "08d14c6eed757414d72055d28de6caf06535806c6a14e450f3a2f1c854420e17" -publicKey = "0x1234567890123456789012345678901234567890" ` oss := &envelope2{} tree, err := toml.Load(payload) @@ -1010,12 +993,9 @@ publicKey = "0x1234567890123456789012345678901234567890" err = json.Unmarshal(b, oss) require.NoError(t, err) - pk, err := oss.OnchainSigningStrategy.PublicKey() - require.NoError(t, err) kbID, err := oss.OnchainSigningStrategy.KeyBundleID("evm") require.NoError(t, err) assert.False(t, oss.OnchainSigningStrategy.IsMultiChain()) - assert.Equal(t, "0x1234567890123456789012345678901234567890", pk) assert.Equal(t, "08d14c6eed757414d72055d28de6caf06535806c6a14e450f3a2f1c854420e17", kbID) } diff --git a/core/services/ocrcommon/adapters.go b/core/services/ocrcommon/adapters.go index 372d9e37f1..53e62be9a0 100644 --- a/core/services/ocrcommon/adapters.go +++ b/core/services/ocrcommon/adapters.go @@ -87,12 +87,11 @@ type OCR3OnchainKeyringMultiChainAdapter struct { lggr logger.Logger } -func NewOCR3OnchainKeyringMultiChainAdapter(ost map[string]ocr2key.KeyBundle, lggr logger.Logger) (*OCR3OnchainKeyringMultiChainAdapter, error) { +func NewOCR3OnchainKeyringMultiChainAdapter(ost map[string]ocr2key.KeyBundle, publicKey ocrtypes.OnchainPublicKey, lggr logger.Logger) (*OCR3OnchainKeyringMultiChainAdapter, error) { if len(ost) == 0 { return nil, errors.New("no key bundles provided") } - // We don't need to check for the existence of `publicKey` in the keyBundles map because it is required on validation on `validate/validate.go` - return &OCR3OnchainKeyringMultiChainAdapter{ost, ost["publicKey"].PublicKey(), lggr}, nil + return &OCR3OnchainKeyringMultiChainAdapter{ost, publicKey, lggr}, nil } func (a *OCR3OnchainKeyringMultiChainAdapter) PublicKey() ocrtypes.OnchainPublicKey { diff --git a/core/services/ocrcommon/adapters_test.go b/core/services/ocrcommon/adapters_test.go index fed854b0b3..e7d4562729 100644 --- a/core/services/ocrcommon/adapters_test.go +++ b/core/services/ocrcommon/adapters_test.go @@ -162,9 +162,9 @@ publicKey = "pub-key" keyBundles[name] = os } - adapter, err := ocrcommon.NewOCR3OnchainKeyringMultiChainAdapter(keyBundles, logger.TestLogger(t)) + adapter, err := ocrcommon.NewOCR3OnchainKeyringMultiChainAdapter(keyBundles, pk, logger.TestLogger(t)) require.NoError(t, err) - _, err = ocrcommon.NewOCR3OnchainKeyringMultiChainAdapter(map[string]ocr2key.KeyBundle{}, logger.TestLogger(t)) + _, err = ocrcommon.NewOCR3OnchainKeyringMultiChainAdapter(map[string]ocr2key.KeyBundle{}, pk, logger.TestLogger(t)) require.Error(t, err, "no key bundles provided") sig, err := adapter.Sign(configDigest, seqNr, reportInfo) diff --git a/core/services/ocrcommon/arbitrum_block_translator.go b/core/services/ocrcommon/arbitrum_block_translator.go index 1b7c371238..9179fe3227 100644 --- a/core/services/ocrcommon/arbitrum_block_translator.go +++ b/core/services/ocrcommon/arbitrum_block_translator.go @@ -10,9 +10,10 @@ import ( "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -33,7 +34,7 @@ type ArbitrumBlockTranslator struct { func NewArbitrumBlockTranslator(ethClient evmclient.Client, lggr logger.Logger) *ArbitrumBlockTranslator { return &ArbitrumBlockTranslator{ ethClient, - lggr.Named("ArbitrumBlockTranslator"), + logger.Named(lggr, "ArbitrumBlockTranslator"), make(map[int64]int64), sync.RWMutex{}, utils.KeyedMutex{}, diff --git a/core/services/ocrcommon/block_translator.go b/core/services/ocrcommon/block_translator.go index d7ceffc5ea..fa44d79c2d 100644 --- a/core/services/ocrcommon/block_translator.go +++ b/core/services/ocrcommon/block_translator.go @@ -4,10 +4,11 @@ import ( "context" "math/big" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) // BlockTranslator converts emitted block numbers (from block.number) into a diff --git a/core/services/ocrcommon/telemetry.go b/core/services/ocrcommon/telemetry.go index 2ef76800a4..2cb4fda910 100644 --- a/core/services/ocrcommon/telemetry.go +++ b/core/services/ocrcommon/telemetry.go @@ -15,6 +15,8 @@ import ( v1types "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v1" v2types "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v2" v3types "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v3" + v4types "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v4" + "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" @@ -41,6 +43,7 @@ type EnhancedTelemetryMercuryData struct { V1Observation *v1types.Observation V2Observation *v2types.Observation V3Observation *v3types.Observation + V4Observation *v4types.Observation TaskRunResults pipeline.TaskRunResults RepTimestamp ocrtypes.ReportTimestamp FeedVersion mercuryutils.FeedVersion @@ -298,6 +301,8 @@ func (e *EnhancedTelemetryService[T]) collectMercuryEnhancedTelemetry(d Enhanced ask := big.NewInt(0) // v2+v3 fields var mfts, lp, np int64 + // v4 fields + var marketStatus telem.MarketStatus switch { case d.V1Observation != nil: @@ -354,6 +359,29 @@ func (e *EnhancedTelemetryService[T]) collectMercuryEnhancedTelemetry(d Enhanced if obs.Ask.Err == nil && obs.Ask.Val != nil { ask = obs.Ask.Val } + case d.V4Observation != nil: + obs := *d.V4Observation + if obs.MaxFinalizedTimestamp.Err == nil { + mfts = obs.MaxFinalizedTimestamp.Val + } + if obs.LinkPrice.Err == nil && obs.LinkPrice.Val != nil { + lp = obs.LinkPrice.Val.Int64() + } + if obs.NativePrice.Err == nil && obs.NativePrice.Val != nil { + np = obs.NativePrice.Val.Int64() + } + if obs.BenchmarkPrice.Err == nil && obs.BenchmarkPrice.Val != nil { + bp = obs.BenchmarkPrice.Val + } + if obs.Bid.Err == nil && obs.Bid.Val != nil { + bid = obs.Bid.Val + } + if obs.Ask.Err == nil && obs.Ask.Val != nil { + ask = obs.Ask.Val + } + if obs.MarketStatus.Err == nil { + marketStatus = telem.MarketStatus(obs.MarketStatus.Val) + } } for _, trr := range d.TaskRunResults { @@ -401,6 +429,7 @@ func (e *EnhancedTelemetryService[T]) collectMercuryEnhancedTelemetry(d Enhanced ObservationBenchmarkPriceString: stringOrEmpty(bp), ObservationBidString: stringOrEmpty(bid), ObservationAskString: stringOrEmpty(ask), + ObservationMarketStatus: marketStatus, IsLinkFeed: d.IsLinkFeed, LinkPrice: lp, IsNativeFeed: d.IsNativeFeed, diff --git a/core/services/registrysyncer/local_registry.go b/core/services/registrysyncer/local_registry.go index 4e4a632bf8..d4bf4a49f5 100644 --- a/core/services/registrysyncer/local_registry.go +++ b/core/services/registrysyncer/local_registry.go @@ -16,7 +16,11 @@ type DonID uint32 type DON struct { capabilities.DON - CapabilityConfigurations map[string]capabilities.CapabilityConfiguration + CapabilityConfigurations map[string]CapabilityConfiguration +} + +type CapabilityConfiguration struct { + Config []byte } type Capability struct { @@ -26,22 +30,37 @@ type Capability struct { type LocalRegistry struct { lggr logger.Logger - peerWrapper p2ptypes.PeerWrapper + getPeerID func() (p2ptypes.PeerID, error) IDsToDONs map[DonID]DON IDsToNodes map[p2ptypes.PeerID]kcr.CapabilitiesRegistryNodeInfo IDsToCapabilities map[string]Capability } +func NewLocalRegistry( + lggr logger.Logger, + getPeerID func() (p2ptypes.PeerID, error), + IDsToDONs map[DonID]DON, + IDsToNodes map[p2ptypes.PeerID]kcr.CapabilitiesRegistryNodeInfo, + IDsToCapabilities map[string]Capability, +) LocalRegistry { + return LocalRegistry{ + lggr: lggr.Named("LocalRegistry"), + getPeerID: getPeerID, + IDsToDONs: IDsToDONs, + IDsToNodes: IDsToNodes, + IDsToCapabilities: IDsToCapabilities, + } +} + func (l *LocalRegistry) LocalNode(ctx context.Context) (capabilities.Node, error) { // Load the current nodes PeerWrapper, this gets us the current node's // PeerID, allowing us to contextualize registry information in terms of DON ownership // (eg. get my current DON configuration, etc). - if l.peerWrapper.GetPeer() == nil { + pid, err := l.getPeerID() + if err != nil { return capabilities.Node{}, errors.New("unable to get local node: peerWrapper hasn't started yet") } - pid := l.peerWrapper.GetPeer().ID() - var workflowDON capabilities.DON capabilityDONs := []capabilities.DON{} for _, d := range l.IDsToDONs { @@ -70,15 +89,15 @@ func (l *LocalRegistry) LocalNode(ctx context.Context) (capabilities.Node, error }, nil } -func (l *LocalRegistry) ConfigForCapability(ctx context.Context, capabilityID string, donID uint32) (capabilities.CapabilityConfiguration, error) { +func (l *LocalRegistry) ConfigForCapability(ctx context.Context, capabilityID string, donID uint32) (CapabilityConfiguration, error) { d, ok := l.IDsToDONs[DonID(donID)] if !ok { - return capabilities.CapabilityConfiguration{}, fmt.Errorf("could not find don %d", donID) + return CapabilityConfiguration{}, fmt.Errorf("could not find don %d", donID) } cc, ok := d.CapabilityConfigurations[capabilityID] if !ok { - return capabilities.CapabilityConfiguration{}, fmt.Errorf("could not find capability configuration for capability %s and donID %d", capabilityID, donID) + return CapabilityConfiguration{}, fmt.Errorf("could not find capability configuration for capability %s and donID %d", capabilityID, donID) } return cc, nil diff --git a/core/services/registrysyncer/mocks/orm.go b/core/services/registrysyncer/mocks/orm.go new file mode 100644 index 0000000000..d7777ecb6e --- /dev/null +++ b/core/services/registrysyncer/mocks/orm.go @@ -0,0 +1,142 @@ +// Code generated by mockery v2.43.2. DO NOT EDIT. + +package mocks + +import ( + context "context" + + registrysyncer "github.com/smartcontractkit/chainlink/v2/core/services/registrysyncer" + mock "github.com/stretchr/testify/mock" +) + +// ORM is an autogenerated mock type for the ORM type +type ORM struct { + mock.Mock +} + +type ORM_Expecter struct { + mock *mock.Mock +} + +func (_m *ORM) EXPECT() *ORM_Expecter { + return &ORM_Expecter{mock: &_m.Mock} +} + +// AddLocalRegistry provides a mock function with given fields: ctx, localRegistry +func (_m *ORM) AddLocalRegistry(ctx context.Context, localRegistry registrysyncer.LocalRegistry) error { + ret := _m.Called(ctx, localRegistry) + + if len(ret) == 0 { + panic("no return value specified for AddLocalRegistry") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, registrysyncer.LocalRegistry) error); ok { + r0 = rf(ctx, localRegistry) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ORM_AddLocalRegistry_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddLocalRegistry' +type ORM_AddLocalRegistry_Call struct { + *mock.Call +} + +// AddLocalRegistry is a helper method to define mock.On call +// - ctx context.Context +// - localRegistry registrysyncer.LocalRegistry +func (_e *ORM_Expecter) AddLocalRegistry(ctx interface{}, localRegistry interface{}) *ORM_AddLocalRegistry_Call { + return &ORM_AddLocalRegistry_Call{Call: _e.mock.On("AddLocalRegistry", ctx, localRegistry)} +} + +func (_c *ORM_AddLocalRegistry_Call) Run(run func(ctx context.Context, localRegistry registrysyncer.LocalRegistry)) *ORM_AddLocalRegistry_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(registrysyncer.LocalRegistry)) + }) + return _c +} + +func (_c *ORM_AddLocalRegistry_Call) Return(_a0 error) *ORM_AddLocalRegistry_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ORM_AddLocalRegistry_Call) RunAndReturn(run func(context.Context, registrysyncer.LocalRegistry) error) *ORM_AddLocalRegistry_Call { + _c.Call.Return(run) + return _c +} + +// LatestLocalRegistry provides a mock function with given fields: ctx +func (_m *ORM) LatestLocalRegistry(ctx context.Context) (*registrysyncer.LocalRegistry, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for LatestLocalRegistry") + } + + var r0 *registrysyncer.LocalRegistry + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (*registrysyncer.LocalRegistry, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) *registrysyncer.LocalRegistry); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*registrysyncer.LocalRegistry) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ORM_LatestLocalRegistry_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LatestLocalRegistry' +type ORM_LatestLocalRegistry_Call struct { + *mock.Call +} + +// LatestLocalRegistry is a helper method to define mock.On call +// - ctx context.Context +func (_e *ORM_Expecter) LatestLocalRegistry(ctx interface{}) *ORM_LatestLocalRegistry_Call { + return &ORM_LatestLocalRegistry_Call{Call: _e.mock.On("LatestLocalRegistry", ctx)} +} + +func (_c *ORM_LatestLocalRegistry_Call) Run(run func(ctx context.Context)) *ORM_LatestLocalRegistry_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *ORM_LatestLocalRegistry_Call) Return(_a0 *registrysyncer.LocalRegistry, _a1 error) *ORM_LatestLocalRegistry_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ORM_LatestLocalRegistry_Call) RunAndReturn(run func(context.Context) (*registrysyncer.LocalRegistry, error)) *ORM_LatestLocalRegistry_Call { + _c.Call.Return(run) + return _c +} + +// NewORM creates a new instance of ORM. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewORM(t interface { + mock.TestingT + Cleanup(func()) +}) *ORM { + mock := &ORM{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/services/registrysyncer/orm.go b/core/services/registrysyncer/orm.go new file mode 100644 index 0000000000..cb08eaafea --- /dev/null +++ b/core/services/registrysyncer/orm.go @@ -0,0 +1,167 @@ +package registrysyncer + +import ( + "context" + "crypto/sha256" + "encoding/json" + "fmt" + "math/big" + + "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" + + kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" + "github.com/smartcontractkit/chainlink/v2/core/logger" + p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" +) + +type capabilitiesRegistryNodeInfo struct { + NodeOperatorId uint32 `json:"nodeOperatorId"` + ConfigCount uint32 `json:"configCount"` + WorkflowDONId uint32 `json:"workflowDONId"` + Signer p2ptypes.PeerID `json:"signer"` + P2pId p2ptypes.PeerID `json:"p2pId"` + HashedCapabilityIds []p2ptypes.PeerID `json:"hashedCapabilityIds"` + CapabilitiesDONIds []string `json:"capabilitiesDONIds"` +} + +func (l *LocalRegistry) MarshalJSON() ([]byte, error) { + idsToNodes := make(map[p2ptypes.PeerID]capabilitiesRegistryNodeInfo) + for k, v := range l.IDsToNodes { + hashedCapabilityIds := make([]p2ptypes.PeerID, len(v.HashedCapabilityIds)) + for i, id := range v.HashedCapabilityIds { + hashedCapabilityIds[i] = p2ptypes.PeerID(id[:]) + } + capabilitiesDONIds := make([]string, len(v.CapabilitiesDONIds)) + for i, id := range v.CapabilitiesDONIds { + capabilitiesDONIds[i] = id.String() + } + idsToNodes[k] = capabilitiesRegistryNodeInfo{ + NodeOperatorId: v.NodeOperatorId, + ConfigCount: v.ConfigCount, + WorkflowDONId: v.WorkflowDONId, + Signer: p2ptypes.PeerID(v.Signer[:]), + P2pId: p2ptypes.PeerID(v.P2pId[:]), + HashedCapabilityIds: hashedCapabilityIds, + CapabilitiesDONIds: capabilitiesDONIds, + } + } + + b, err := json.Marshal(&struct { + IDsToDONs map[DonID]DON + IDsToNodes map[p2ptypes.PeerID]capabilitiesRegistryNodeInfo + IDsToCapabilities map[string]Capability + }{ + IDsToDONs: l.IDsToDONs, + IDsToNodes: idsToNodes, + IDsToCapabilities: l.IDsToCapabilities, + }) + if err != nil { + return []byte{}, err + } + return b, nil +} + +func (l *LocalRegistry) UnmarshalJSON(data []byte) error { + temp := struct { + IDsToDONs map[DonID]DON + IDsToNodes map[p2ptypes.PeerID]capabilitiesRegistryNodeInfo + IDsToCapabilities map[string]Capability + }{ + IDsToDONs: make(map[DonID]DON), + IDsToNodes: make(map[p2ptypes.PeerID]capabilitiesRegistryNodeInfo), + IDsToCapabilities: make(map[string]Capability), + } + + if err := json.Unmarshal(data, &temp); err != nil { + return fmt.Errorf("failed to unmarshal state: %w", err) + } + + l.IDsToDONs = temp.IDsToDONs + + l.IDsToNodes = make(map[p2ptypes.PeerID]kcr.CapabilitiesRegistryNodeInfo) + for peerID, v := range temp.IDsToNodes { + hashedCapabilityIds := make([][32]byte, len(v.HashedCapabilityIds)) + for i, id := range v.HashedCapabilityIds { + copy(hashedCapabilityIds[i][:], id[:]) + } + + capabilitiesDONIds := make([]*big.Int, len(v.CapabilitiesDONIds)) + for i, id := range v.CapabilitiesDONIds { + bigInt := new(big.Int) + bigInt.SetString(id, 10) + capabilitiesDONIds[i] = bigInt + } + l.IDsToNodes[peerID] = kcr.CapabilitiesRegistryNodeInfo{ + NodeOperatorId: v.NodeOperatorId, + ConfigCount: v.ConfigCount, + WorkflowDONId: v.WorkflowDONId, + Signer: v.Signer, + P2pId: v.P2pId, + HashedCapabilityIds: hashedCapabilityIds, + CapabilitiesDONIds: capabilitiesDONIds, + } + } + + l.IDsToCapabilities = temp.IDsToCapabilities + + return nil +} + +type ORM interface { + AddLocalRegistry(ctx context.Context, localRegistry LocalRegistry) error + LatestLocalRegistry(ctx context.Context) (*LocalRegistry, error) +} + +type orm struct { + ds sqlutil.DataSource + lggr logger.Logger +} + +var _ ORM = (*orm)(nil) + +func NewORM(ds sqlutil.DataSource, lggr logger.Logger) orm { + namedLogger := lggr.Named("RegistrySyncerORM") + return orm{ + ds: ds, + lggr: namedLogger, + } +} + +func (orm orm) AddLocalRegistry(ctx context.Context, localRegistry LocalRegistry) error { + return sqlutil.TransactDataSource(ctx, orm.ds, nil, func(tx sqlutil.DataSource) error { + localRegistryJSON, err := localRegistry.MarshalJSON() + if err != nil { + return err + } + hash := sha256.Sum256(localRegistryJSON) + _, err = tx.ExecContext( + ctx, + `INSERT INTO registry_syncer_states (data, data_hash) VALUES ($1, $2) ON CONFLICT (data_hash) DO NOTHING`, + localRegistryJSON, fmt.Sprintf("%x", hash[:]), + ) + if err != nil { + return err + } + _, err = tx.ExecContext(ctx, `DELETE FROM registry_syncer_states +WHERE data_hash NOT IN ( + SELECT data_hash FROM registry_syncer_states + ORDER BY id DESC + LIMIT 10 +);`) + return err + }) +} + +func (orm orm) LatestLocalRegistry(ctx context.Context) (*LocalRegistry, error) { + var localRegistry LocalRegistry + var localRegistryJSON string + err := orm.ds.GetContext(ctx, &localRegistryJSON, `SELECT data FROM registry_syncer_states ORDER BY id DESC LIMIT 1`) + if err != nil { + return nil, err + } + err = localRegistry.UnmarshalJSON([]byte(localRegistryJSON)) + if err != nil { + return nil, err + } + return &localRegistry, nil +} diff --git a/core/services/registrysyncer/orm_test.go b/core/services/registrysyncer/orm_test.go new file mode 100644 index 0000000000..03772ea22b --- /dev/null +++ b/core/services/registrysyncer/orm_test.go @@ -0,0 +1,145 @@ +package registrysyncer_test + +import ( + "encoding/hex" + "math/big" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/durationpb" + + ragetypes "github.com/smartcontractkit/libocr/ragep2p/types" + + "github.com/smartcontractkit/chainlink-common/pkg/capabilities" + capabilitiespb "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" + "github.com/smartcontractkit/chainlink-common/pkg/values" + + kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" + "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/p2p/types" + "github.com/smartcontractkit/chainlink/v2/core/services/registrysyncer" +) + +func TestRegistrySyncerORM_InsertAndRetrieval(t *testing.T) { + db := pgtest.NewSqlxDB(t) + ctx := testutils.Context(t) + lggr := logger.TestLogger(t) + orm := registrysyncer.NewORM(db, lggr) + + var states []registrysyncer.LocalRegistry + for i := 0; i < 11; i++ { + state := generateState(t) + err := orm.AddLocalRegistry(ctx, state) + require.NoError(t, err) + states = append(states, state) + } + + var count int + err := db.Get(&count, `SELECT count(*) FROM registry_syncer_states`) + require.NoError(t, err) + assert.Equal(t, 10, count) + + state, err := orm.LatestLocalRegistry(ctx) + require.NoError(t, err) + assert.Equal(t, states[10], *state) +} + +func generateState(t *testing.T) registrysyncer.LocalRegistry { + dID := uint32(1) + var pid ragetypes.PeerID + err := pid.UnmarshalText([]byte("12D3KooWBCF1XT5Wi8FzfgNCqRL76Swv8TRU3TiD4QiJm8NMNX7N")) + require.NoError(t, err) + nodes := [][32]byte{ + pid, + randomWord(), + randomWord(), + randomWord(), + } + capabilityID := randomWord() + capabilityID2 := randomWord() + capabilityIDStr := hex.EncodeToString(capabilityID[:]) + capabilityID2Str := hex.EncodeToString(capabilityID2[:]) + + config := &capabilitiespb.CapabilityConfig{ + DefaultConfig: values.Proto(values.EmptyMap()).GetMapValue(), + RemoteConfig: &capabilitiespb.CapabilityConfig_RemoteTriggerConfig{ + RemoteTriggerConfig: &capabilitiespb.RemoteTriggerConfig{ + RegistrationRefresh: durationpb.New(20 * time.Second), + RegistrationExpiry: durationpb.New(60 * time.Second), + // F + 1 + MinResponsesToAggregate: uint32(1) + 1, + MessageExpiry: durationpb.New(120 * time.Second), + }, + }, + } + configb, err := proto.Marshal(config) + require.NoError(t, err) + + return registrysyncer.LocalRegistry{ + IDsToDONs: map[registrysyncer.DonID]registrysyncer.DON{ + registrysyncer.DonID(dID): { + DON: capabilities.DON{ + ID: dID, + ConfigVersion: uint32(0), + F: uint8(1), + IsPublic: true, + AcceptsWorkflows: true, + Members: toPeerIDs(nodes), + }, + CapabilityConfigurations: map[string]registrysyncer.CapabilityConfiguration{ + capabilityIDStr: { + Config: configb, + }, + capabilityID2Str: { + Config: configb, + }, + }, + }, + }, + IDsToCapabilities: map[string]registrysyncer.Capability{ + capabilityIDStr: { + ID: capabilityIDStr, + CapabilityType: capabilities.CapabilityTypeAction, + }, + capabilityID2Str: { + ID: capabilityID2Str, + CapabilityType: capabilities.CapabilityTypeConsensus, + }, + }, + IDsToNodes: map[types.PeerID]kcr.CapabilitiesRegistryNodeInfo{ + nodes[0]: { + NodeOperatorId: 1, + Signer: randomWord(), + P2pId: nodes[0], + HashedCapabilityIds: [][32]byte{capabilityID, capabilityID2}, + CapabilitiesDONIds: []*big.Int{}, + }, + nodes[1]: { + NodeOperatorId: 1, + Signer: randomWord(), + P2pId: nodes[1], + HashedCapabilityIds: [][32]byte{capabilityID, capabilityID2}, + CapabilitiesDONIds: []*big.Int{}, + }, + nodes[2]: { + NodeOperatorId: 1, + Signer: randomWord(), + P2pId: nodes[2], + HashedCapabilityIds: [][32]byte{capabilityID, capabilityID2}, + CapabilitiesDONIds: []*big.Int{}, + }, + nodes[3]: { + NodeOperatorId: 1, + Signer: randomWord(), + P2pId: nodes[3], + HashedCapabilityIds: [][32]byte{capabilityID, capabilityID2}, + CapabilitiesDONIds: []*big.Int{}, + }, + }, + } +} diff --git a/core/services/registrysyncer/syncer.go b/core/services/registrysyncer/syncer.go index 6a44ff561d..ab50c448b5 100644 --- a/core/services/registrysyncer/syncer.go +++ b/core/services/registrysyncer/syncer.go @@ -4,17 +4,14 @@ import ( "context" "encoding/json" "fmt" + "math/big" "sync" "time" - "google.golang.org/protobuf/proto" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities" - capabilitiespb "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/types/query/primitives" - "github.com/smartcontractkit/chainlink-common/pkg/values" kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -31,15 +28,33 @@ type Syncer interface { AddLauncher(h ...Launcher) } +type ContractReaderFactory interface { + NewContractReader(context.Context, []byte) (types.ContractReader, error) +} + +type RegistrySyncer interface { + Sync(ctx context.Context, isInitialSync bool) error + AddLauncher(launchers ...Launcher) + Start(ctx context.Context) error + Close() error + Ready() error + HealthReport() map[string]error + Name() string +} + type registrySyncer struct { services.StateMachine stopCh services.StopChan launchers []Launcher reader types.ContractReader - initReader func(ctx context.Context, lggr logger.Logger, relayer contractReaderFactory, registryAddress string) (types.ContractReader, error) - relayer contractReaderFactory + initReader func(ctx context.Context, lggr logger.Logger, relayer ContractReaderFactory, registryAddress string) (types.ContractReader, error) + relayer ContractReaderFactory registryAddress string - peerWrapper p2ptypes.PeerWrapper + getPeerID func() (p2ptypes.PeerID, error) + + orm ORM + + updateChan chan *LocalRegistry wg sync.WaitGroup lggr logger.Logger @@ -55,29 +70,27 @@ var ( // New instantiates a new RegistrySyncer func New( lggr logger.Logger, - peerWrapper p2ptypes.PeerWrapper, - relayer contractReaderFactory, + getPeerID func() (p2ptypes.PeerID, error), + relayer ContractReaderFactory, registryAddress string, -) (*registrySyncer, error) { - stopCh := make(services.StopChan) + orm ORM, +) (RegistrySyncer, error) { return ®istrySyncer{ - stopCh: stopCh, + stopCh: make(services.StopChan), + updateChan: make(chan *LocalRegistry), lggr: lggr.Named("RegistrySyncer"), relayer: relayer, registryAddress: registryAddress, initReader: newReader, - peerWrapper: peerWrapper, + orm: orm, + getPeerID: getPeerID, }, nil } -type contractReaderFactory interface { - NewContractReader(context.Context, []byte) (types.ContractReader, error) -} - // NOTE: this can't be called while initializing the syncer and needs to be called in the sync loop. // This is because Bind() makes an onchain call to verify that the contract address exists, and if // called during initialization, this results in a "no live nodes" error. -func newReader(ctx context.Context, lggr logger.Logger, relayer contractReaderFactory, remoteRegistryAddress string) (types.ContractReader, error) { +func newReader(ctx context.Context, lggr logger.Logger, relayer ContractReaderFactory, remoteRegistryAddress string) (types.ContractReader, error) { contractReaderConfig := evmrelaytypes.ChainReaderConfig{ Contracts: map[string]evmrelaytypes.ChainContractReader{ "CapabilitiesRegistry": { @@ -124,6 +137,11 @@ func (s *registrySyncer) Start(ctx context.Context) error { defer s.wg.Done() s.syncLoop() }() + s.wg.Add(1) + go func() { + defer s.wg.Done() + s.updateStateLoop() + }() return nil }) } @@ -139,7 +157,7 @@ func (s *registrySyncer) syncLoop() { // sync immediately once spinning up syncLoop, as by default a ticker will // fire for the first time at T+N, where N is the interval. s.lggr.Debug("starting initial sync with remote registry") - err := s.sync(ctx) + err := s.Sync(ctx, true) if err != nil { s.lggr.Errorw("failed to sync with remote registry", "error", err) } @@ -150,7 +168,7 @@ func (s *registrySyncer) syncLoop() { return case <-ticker.C: s.lggr.Debug("starting regular sync with the remote registry") - err := s.sync(ctx) + err := s.Sync(ctx, false) if err != nil { s.lggr.Errorw("failed to sync with remote registry", "error", err) } @@ -158,29 +176,28 @@ func (s *registrySyncer) syncLoop() { } } -func unmarshalCapabilityConfig(data []byte) (capabilities.CapabilityConfiguration, error) { - cconf := &capabilitiespb.CapabilityConfig{} - err := proto.Unmarshal(data, cconf) - if err != nil { - return capabilities.CapabilityConfiguration{}, err - } +func (s *registrySyncer) updateStateLoop() { + ctx, cancel := s.stopCh.NewCtx() + defer cancel() - var rtc capabilities.RemoteTriggerConfig - if prtc := cconf.GetRemoteTriggerConfig(); prtc != nil { - rtc.RegistrationRefresh = prtc.RegistrationRefresh.AsDuration() - rtc.RegistrationExpiry = prtc.RegistrationExpiry.AsDuration() - rtc.MinResponsesToAggregate = prtc.MinResponsesToAggregate - rtc.MessageExpiry = prtc.MessageExpiry.AsDuration() + for { + select { + case <-s.stopCh: + return + case localRegistry, ok := <-s.updateChan: + if !ok { + // channel has been closed, terminating. + return + } + if err := s.orm.AddLocalRegistry(ctx, *localRegistry); err != nil { + s.lggr.Errorw("failed to save state to local registry", "error", err) + } + } } - - return capabilities.CapabilityConfiguration{ - DefaultConfig: values.FromMapValueProto(cconf.DefaultConfig), - RemoteTriggerConfig: rtc, - }, nil } func (s *registrySyncer) localRegistry(ctx context.Context) (*LocalRegistry, error) { - caps := []kcr.CapabilitiesRegistryCapabilityInfo{} + var caps []kcr.CapabilitiesRegistryCapabilityInfo err := s.reader.GetLatestValue(ctx, "CapabilitiesRegistry", "getCapabilities", primitives.Unconfirmed, nil, &caps) if err != nil { return nil, err @@ -198,7 +215,7 @@ func (s *registrySyncer) localRegistry(ctx context.Context) (*LocalRegistry, err hashedIDsToCapabilityIDs[c.HashedId] = cid } - dons := []kcr.CapabilitiesRegistryDONInfo{} + var dons []kcr.CapabilitiesRegistryDONInfo err = s.reader.GetLatestValue(ctx, "CapabilitiesRegistry", "getDONs", primitives.Unconfirmed, nil, &dons) if err != nil { return nil, err @@ -206,21 +223,16 @@ func (s *registrySyncer) localRegistry(ctx context.Context) (*LocalRegistry, err idsToDONs := map[DonID]DON{} for _, d := range dons { - cc := map[string]capabilities.CapabilityConfiguration{} + cc := map[string]CapabilityConfiguration{} for _, dc := range d.CapabilityConfigurations { cid, ok := hashedIDsToCapabilityIDs[dc.CapabilityId] if !ok { return nil, fmt.Errorf("invariant violation: could not find full ID for hashed ID %s", dc.CapabilityId) } - cconf, innerErr := unmarshalCapabilityConfig(dc.Config) - if innerErr != nil { - return nil, innerErr + cc[cid] = CapabilityConfiguration{ + Config: dc.Config, } - - cconf.RemoteTriggerConfig.ApplyDefaults() - - cc[cid] = cconf } idsToDONs[DonID(d.Id)] = DON{ @@ -229,7 +241,7 @@ func (s *registrySyncer) localRegistry(ctx context.Context) (*LocalRegistry, err } } - nodes := []kcr.CapabilitiesRegistryNodeInfo{} + var nodes []kcr.CapabilitiesRegistryNodeInfo err = s.reader.GetLatestValue(ctx, "CapabilitiesRegistry", "getNodes", primitives.Unconfirmed, nil, &nodes) if err != nil { return nil, err @@ -242,14 +254,14 @@ func (s *registrySyncer) localRegistry(ctx context.Context) (*LocalRegistry, err return &LocalRegistry{ lggr: s.lggr, - peerWrapper: s.peerWrapper, + getPeerID: s.getPeerID, IDsToDONs: idsToDONs, IDsToCapabilities: idsToCapabilities, IDsToNodes: idsToNodes, }, nil } -func (s *registrySyncer) sync(ctx context.Context) error { +func (s *registrySyncer) Sync(ctx context.Context, isInitialSync bool) error { s.mu.RLock() defer s.mu.RUnlock() @@ -267,13 +279,44 @@ func (s *registrySyncer) sync(ctx context.Context) error { s.reader = reader } - lr, err := s.localRegistry(ctx) - if err != nil { - return fmt.Errorf("failed to sync with remote registry: %w", err) + var lr *LocalRegistry + var err error + + if isInitialSync { + s.lggr.Debug("syncing with local registry") + lr, err = s.orm.LatestLocalRegistry(ctx) + if err != nil { + s.lggr.Warnw("failed to sync with local registry, using remote registry instead", "error", err) + } else { + lr.lggr = s.lggr + lr.getPeerID = s.getPeerID + } + } + + if lr == nil { + s.lggr.Debug("syncing with remote registry") + localRegistry, err := s.localRegistry(ctx) + if err != nil { + return fmt.Errorf("failed to sync with remote registry: %w", err) + } + lr = localRegistry + // Attempt to send local registry to the update channel without blocking + // This is to prevent the tests from hanging if they are not calling `Start()` on the syncer + select { + case <-s.stopCh: + s.lggr.Debug("sync cancelled, stopping") + case s.updateChan <- lr: + // Successfully sent state + s.lggr.Debug("remote registry update triggered successfully") + default: + // No one is ready to receive the state, handle accordingly + s.lggr.Debug("no listeners on update channel, remote registry update skipped") + } } for _, h := range s.launchers { - if err := h.Launch(ctx, lr); err != nil { + lrCopy := deepCopyLocalRegistry(lr) + if err := h.Launch(ctx, &lrCopy); err != nil { s.lggr.Errorf("error calling launcher: %s", err) } } @@ -281,6 +324,58 @@ func (s *registrySyncer) sync(ctx context.Context) error { return nil } +func deepCopyLocalRegistry(lr *LocalRegistry) LocalRegistry { + var lrCopy LocalRegistry + lrCopy.lggr = lr.lggr + lrCopy.getPeerID = lr.getPeerID + lrCopy.IDsToDONs = make(map[DonID]DON, len(lr.IDsToDONs)) + for id, don := range lr.IDsToDONs { + d := capabilities.DON{ + ID: don.ID, + ConfigVersion: don.ConfigVersion, + Members: make([]p2ptypes.PeerID, len(don.Members)), + F: don.F, + IsPublic: don.IsPublic, + AcceptsWorkflows: don.AcceptsWorkflows, + } + copy(d.Members, don.Members) + capCfgs := make(map[string]CapabilityConfiguration, len(don.CapabilityConfigurations)) + for capID, capCfg := range don.CapabilityConfigurations { + capCfgs[capID] = CapabilityConfiguration{ + Config: capCfg.Config[:], + } + } + lrCopy.IDsToDONs[id] = DON{ + DON: d, + CapabilityConfigurations: capCfgs, + } + } + + lrCopy.IDsToCapabilities = make(map[string]Capability, len(lr.IDsToCapabilities)) + for id, capability := range lr.IDsToCapabilities { + cp := capability + lrCopy.IDsToCapabilities[id] = cp + } + + lrCopy.IDsToNodes = make(map[p2ptypes.PeerID]kcr.CapabilitiesRegistryNodeInfo, len(lr.IDsToNodes)) + for id, node := range lr.IDsToNodes { + nodeInfo := kcr.CapabilitiesRegistryNodeInfo{ + NodeOperatorId: node.NodeOperatorId, + ConfigCount: node.ConfigCount, + WorkflowDONId: node.WorkflowDONId, + Signer: node.Signer, + P2pId: node.P2pId, + HashedCapabilityIds: make([][32]byte, len(node.HashedCapabilityIds)), + CapabilitiesDONIds: make([]*big.Int, len(node.CapabilitiesDONIds)), + } + copy(nodeInfo.HashedCapabilityIds, node.HashedCapabilityIds) + copy(nodeInfo.CapabilitiesDONIds, node.CapabilitiesDONIds) + lrCopy.IDsToNodes[id] = nodeInfo + } + + return lrCopy +} + func toCapabilityType(capabilityType uint8) capabilities.CapabilityType { switch capabilityType { case 0: @@ -292,8 +387,7 @@ func toCapabilityType(capabilityType uint8) capabilities.CapabilityType { case 3: return capabilities.CapabilityTypeTarget default: - // Not found - return capabilities.CapabilityType(-1) + return capabilities.CapabilityTypeUnknown } } @@ -322,6 +416,9 @@ func (s *registrySyncer) AddLauncher(launchers ...Launcher) { func (s *registrySyncer) Close() error { return s.StopOnce("RegistrySyncer", func() error { close(s.stopCh) + s.mu.Lock() + defer s.mu.Unlock() + close(s.updateChan) s.wg.Wait() return nil }) diff --git a/core/services/registrysyncer/syncer_test.go b/core/services/registrysyncer/syncer_test.go index b926183394..9e51b7498f 100644 --- a/core/services/registrysyncer/syncer_test.go +++ b/core/services/registrysyncer/syncer_test.go @@ -1,4 +1,4 @@ -package registrysyncer +package registrysyncer_test import ( "context" @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "math/big" + "sync" "testing" "time" @@ -15,6 +16,7 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/durationpb" @@ -25,6 +27,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/values" capabilitiespb "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" + evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" @@ -33,7 +36,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" - "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types/mocks" + "github.com/smartcontractkit/chainlink/v2/core/services/registrysyncer" + syncerMocks "github.com/smartcontractkit/chainlink/v2/core/services/registrysyncer/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" evmrelaytypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" ) @@ -127,16 +131,51 @@ func randomWord() [32]byte { } type launcher struct { - localRegistry *LocalRegistry + localRegistry *registrysyncer.LocalRegistry + mu sync.RWMutex } -func (l *launcher) Launch(ctx context.Context, localRegistry *LocalRegistry) error { +func (l *launcher) Launch(ctx context.Context, localRegistry *registrysyncer.LocalRegistry) error { + l.mu.Lock() + defer l.mu.Unlock() l.localRegistry = localRegistry return nil } +type orm struct { + ormMock *syncerMocks.ORM + latestLocalRegistryCh chan struct{} + addLocalRegistryCh chan struct{} +} + +func newORM(t *testing.T) *orm { + t.Helper() + + return &orm{ + ormMock: syncerMocks.NewORM(t), + latestLocalRegistryCh: make(chan struct{}, 1), + addLocalRegistryCh: make(chan struct{}, 1), + } +} + +func (o *orm) Cleanup() { + close(o.latestLocalRegistryCh) + close(o.addLocalRegistryCh) +} + +func (o *orm) AddLocalRegistry(ctx context.Context, localRegistry registrysyncer.LocalRegistry) error { + o.addLocalRegistryCh <- struct{}{} + err := o.ormMock.AddLocalRegistry(ctx, localRegistry) + return err +} + +func (o *orm) LatestLocalRegistry(ctx context.Context) (*registrysyncer.LocalRegistry, error) { + o.latestLocalRegistryCh <- struct{}{} + return o.ormMock.LatestLocalRegistry(ctx) +} + func toPeerIDs(ids [][32]byte) []p2ptypes.PeerID { - pids := []p2ptypes.PeerID{} + var pids []p2ptypes.PeerID for _, id := range ids { pids = append(pids, id) } @@ -210,6 +249,7 @@ func TestReader_Integration(t *testing.T) { RegistrationExpiry: durationpb.New(60 * time.Second), // F + 1 MinResponsesToAggregate: uint32(1) + 1, + MessageExpiry: durationpb.New(120 * time.Second), }, }, } @@ -236,49 +276,38 @@ func TestReader_Integration(t *testing.T) { require.NoError(t, err) - wrapper := mocks.NewPeerWrapper(t) + db := pgtest.NewSqlxDB(t) factory := newContractReaderFactory(t, sim) - syncer, err := New(logger.TestLogger(t), wrapper, factory, regAddress.Hex()) + syncerORM := registrysyncer.NewORM(db, logger.TestLogger(t)) + syncer, err := registrysyncer.New(logger.TestLogger(t), func() (p2ptypes.PeerID, error) { return p2ptypes.PeerID{}, nil }, factory, regAddress.Hex(), syncerORM) require.NoError(t, err) l := &launcher{} syncer.AddLauncher(l) - err = syncer.sync(ctx) + err = syncer.Sync(ctx, false) // not looking to load from the DB in this specific test. s := l.localRegistry require.NoError(t, err) assert.Len(t, s.IDsToCapabilities, 1) gotCap := s.IDsToCapabilities[cid] - assert.Equal(t, Capability{ + assert.Equal(t, registrysyncer.Capability{ CapabilityType: capabilities.CapabilityTypeTarget, ID: "write-chain@1.0.1", }, gotCap) assert.Len(t, s.IDsToDONs, 1) - rtc := capabilities.RemoteTriggerConfig{ - RegistrationRefresh: 20 * time.Second, - MinResponsesToAggregate: 2, - RegistrationExpiry: 60 * time.Second, - MessageExpiry: 120 * time.Second, - } - expectedDON := DON{ - DON: capabilities.DON{ - ID: 1, - ConfigVersion: 1, - IsPublic: true, - AcceptsWorkflows: true, - F: 1, - Members: toPeerIDs(nodeSet), - }, - CapabilityConfigurations: map[string]capabilities.CapabilityConfiguration{ - cid: { - DefaultConfig: values.EmptyMap(), - RemoteTriggerConfig: rtc, - }, - }, + expectedDON := capabilities.DON{ + ID: 1, + ConfigVersion: 1, + IsPublic: true, + AcceptsWorkflows: true, + F: 1, + Members: toPeerIDs(nodeSet), } - assert.Equal(t, expectedDON, s.IDsToDONs[1]) + gotDon := s.IDsToDONs[1] + assert.Equal(t, expectedDON, gotDon.DON) + assert.Equal(t, configb, gotDon.CapabilityConfigurations[cid].Config) nodesInfo := []kcr.CapabilitiesRegistryNodeInfo{ { @@ -321,6 +350,127 @@ func TestReader_Integration(t *testing.T) { }, s.IDsToNodes) } +func TestSyncer_DBIntegration(t *testing.T) { + ctx := testutils.Context(t) + reg, regAddress, owner, sim := startNewChainWithRegistry(t) + + _, err := reg.AddCapabilities(owner, []kcr.CapabilitiesRegistryCapability{writeChainCapability}) + require.NoError(t, err, "AddCapability failed for %s", writeChainCapability.LabelledName) + sim.Commit() + + cid, err := reg.GetHashedCapabilityId(&bind.CallOpts{}, writeChainCapability.LabelledName, writeChainCapability.Version) + require.NoError(t, err) + + _, err = reg.AddNodeOperators(owner, []kcr.CapabilitiesRegistryNodeOperator{ + { + Admin: owner.From, + Name: "TEST_NOP", + }, + }) + require.NoError(t, err) + + nodeSet := [][32]byte{ + randomWord(), + randomWord(), + randomWord(), + } + + signersSet := [][32]byte{ + randomWord(), + randomWord(), + randomWord(), + } + + nodes := []kcr.CapabilitiesRegistryNodeParams{ + { + // The first NodeOperatorId has id 1 since the id is auto-incrementing. + NodeOperatorId: uint32(1), + Signer: signersSet[0], + P2pId: nodeSet[0], + HashedCapabilityIds: [][32]byte{cid}, + }, + { + // The first NodeOperatorId has id 1 since the id is auto-incrementing. + NodeOperatorId: uint32(1), + Signer: signersSet[1], + P2pId: nodeSet[1], + HashedCapabilityIds: [][32]byte{cid}, + }, + { + // The first NodeOperatorId has id 1 since the id is auto-incrementing. + NodeOperatorId: uint32(1), + Signer: signersSet[2], + P2pId: nodeSet[2], + HashedCapabilityIds: [][32]byte{cid}, + }, + } + _, err = reg.AddNodes(owner, nodes) + require.NoError(t, err) + + config := &capabilitiespb.CapabilityConfig{ + DefaultConfig: values.Proto(values.EmptyMap()).GetMapValue(), + RemoteConfig: &capabilitiespb.CapabilityConfig_RemoteTriggerConfig{ + RemoteTriggerConfig: &capabilitiespb.RemoteTriggerConfig{ + RegistrationRefresh: durationpb.New(20 * time.Second), + RegistrationExpiry: durationpb.New(60 * time.Second), + // F + 1 + MinResponsesToAggregate: uint32(1) + 1, + }, + }, + } + configb, err := proto.Marshal(config) + require.NoError(t, err) + + cfgs := []kcr.CapabilitiesRegistryCapabilityConfiguration{ + { + CapabilityId: cid, + Config: configb, + }, + } + _, err = reg.AddDON( + owner, + nodeSet, + cfgs, + true, + true, + 1, + ) + sim.Commit() + + require.NoError(t, err) + + factory := newContractReaderFactory(t, sim) + syncerORM := newORM(t) + syncerORM.ormMock.On("LatestLocalRegistry", mock.Anything).Return(nil, fmt.Errorf("no state found")) + syncerORM.ormMock.On("AddLocalRegistry", mock.Anything, mock.Anything).Return(nil) + syncer, err := newTestSyncer(logger.TestLogger(t), func() (p2ptypes.PeerID, error) { return p2ptypes.PeerID{}, nil }, factory, regAddress.Hex(), syncerORM) + require.NoError(t, err) + require.NoError(t, syncer.Start(ctx)) + t.Cleanup(func() { + syncerORM.Cleanup() + require.NoError(t, syncer.Close()) + }) + + l := &launcher{} + syncer.AddLauncher(l) + + var latestLocalRegistryCalled, addLocalRegistryCalled bool + timeout := time.After(testutils.WaitTimeout(t)) + + for !latestLocalRegistryCalled || !addLocalRegistryCalled { + select { + case val := <-syncerORM.latestLocalRegistryCh: + assert.Equal(t, struct{}{}, val) + latestLocalRegistryCalled = true + case val := <-syncerORM.addLocalRegistryCh: + assert.Equal(t, struct{}{}, val) + addLocalRegistryCalled = true + case <-timeout: + t.Fatal("test timed out; channels did not received data") + } + } +} + func TestSyncer_LocalNode(t *testing.T) { ctx := tests.Context(t) lggr := logger.TestLogger(t) @@ -328,10 +478,6 @@ func TestSyncer_LocalNode(t *testing.T) { var pid p2ptypes.PeerID err := pid.UnmarshalText([]byte("12D3KooWBCF1XT5Wi8FzfgNCqRL76Swv8TRU3TiD4QiJm8NMNX7N")) require.NoError(t, err) - peer := mocks.NewPeer(t) - peer.On("ID").Return(pid) - wrapper := mocks.NewPeerWrapper(t) - wrapper.On("GetPeer").Return(peer) workflowDonNodes := []p2ptypes.PeerID{ pid, @@ -344,11 +490,11 @@ func TestSyncer_LocalNode(t *testing.T) { // The below state describes a Workflow DON (AcceptsWorkflows = true), // which exposes the streams-trigger and write_chain capabilities. // We expect receivers to be wired up and both capabilities to be added to the registry. - localRegistry := LocalRegistry{ - lggr: lggr, - peerWrapper: wrapper, - IDsToDONs: map[DonID]DON{ - DonID(dID): { + localRegistry := registrysyncer.NewLocalRegistry( + lggr, + func() (p2ptypes.PeerID, error) { return pid, nil }, + map[registrysyncer.DonID]registrysyncer.DON{ + registrysyncer.DonID(dID): { DON: capabilities.DON{ ID: dID, ConfigVersion: uint32(2), @@ -359,7 +505,7 @@ func TestSyncer_LocalNode(t *testing.T) { }, }, }, - IDsToNodes: map[p2ptypes.PeerID]kcr.CapabilitiesRegistryNodeInfo{ + map[p2ptypes.PeerID]kcr.CapabilitiesRegistryNodeInfo{ workflowDonNodes[0]: { NodeOperatorId: 1, Signer: randomWord(), @@ -381,7 +527,8 @@ func TestSyncer_LocalNode(t *testing.T) { P2pId: workflowDonNodes[3], }, }, - } + map[string]registrysyncer.Capability{}, + ) node, err := localRegistry.LocalNode(ctx) require.NoError(t, err) @@ -401,3 +548,17 @@ func TestSyncer_LocalNode(t *testing.T) { } assert.Equal(t, expectedNode, node) } + +func newTestSyncer( + lggr logger.Logger, + getPeerID func() (p2ptypes.PeerID, error), + relayer registrysyncer.ContractReaderFactory, + registryAddress string, + orm *orm, +) (registrysyncer.RegistrySyncer, error) { + rs, err := registrysyncer.New(lggr, getPeerID, relayer, registryAddress, orm) + if err != nil { + return nil, err + } + return rs, nil +} diff --git a/core/services/relay/dummy/config_provider.go b/core/services/relay/dummy/config_provider.go index 10662ee296..db36acba83 100644 --- a/core/services/relay/dummy/config_provider.go +++ b/core/services/relay/dummy/config_provider.go @@ -63,7 +63,7 @@ type configProvider struct { } func NewConfigProvider(lggr logger.Logger, cfg RelayConfig) (types.ConfigProvider, error) { - cp := &configProvider{lggr: lggr.Named("DummyConfigProvider")} + cp := &configProvider{lggr: lggr.Named("DummyConfigProvider").Named(cfg.ConfigTracker.ConfigDigest.String())} { contractConfig, err := cfg.ConfigTracker.ToContractConfig() diff --git a/core/services/relay/dummy/config_tracker.go b/core/services/relay/dummy/config_tracker.go index 0ae188361f..ecd08196e5 100644 --- a/core/services/relay/dummy/config_tracker.go +++ b/core/services/relay/dummy/config_tracker.go @@ -21,7 +21,7 @@ func NewContractConfigTracker(lggr logger.Logger, cfg ConfigTrackerCfg) (ocrtype if err != nil { return nil, err } - return &configTracker{lggr.Named("DummyConfigProvider"), contractConfig, cfg.ChangedInBlock, cfg.BlockHeight}, nil + return &configTracker{lggr.Named("DummyConfigTracker"), contractConfig, cfg.ChangedInBlock, cfg.BlockHeight}, nil } // Notify may optionally emit notification events when the contract's diff --git a/core/services/relay/dummy/llo_provider.go b/core/services/relay/dummy/llo_provider.go index 4aeb21bed8..887fa13430 100644 --- a/core/services/relay/dummy/llo_provider.go +++ b/core/services/relay/dummy/llo_provider.go @@ -73,10 +73,6 @@ func (p *lloProvider) OffchainConfigDigester() ocrtypes.OffchainConfigDigester { return p.cp.OffchainConfigDigester() } -func (p *lloProvider) OnchainConfigCodec() llo.OnchainConfigCodec { - return &llo.JSONOnchainConfigCodec{} -} - func (p *lloProvider) ContractTransmitter() llotypes.Transmitter { return p.transmitter } diff --git a/core/services/relay/evm/batch_caller.go b/core/services/relay/evm/batch_caller.go index 3ee4525827..ce5a2bd722 100644 --- a/core/services/relay/evm/batch_caller.go +++ b/core/services/relay/evm/batch_caller.go @@ -11,9 +11,10 @@ import ( "github.com/pkg/errors" "golang.org/x/sync/errgroup" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) var errEmptyOutput = errors.New("rpc call output is empty (make sure that the contract method exists and rpc is healthy)") @@ -148,7 +149,7 @@ func (c *defaultEvmBatchCaller) batchCall(ctx context.Context, blockNumber uint6 map[string]interface{}{ "from": common.Address{}, "to": call.ContractAddress, - "data": data, + "data": hexutil.Bytes(data), }, blockNumStr, }, diff --git a/core/services/relay/evm/batch_caller_test.go b/core/services/relay/evm/batch_caller_test.go index 995e47618c..048df90dab 100644 --- a/core/services/relay/evm/batch_caller_test.go +++ b/core/services/relay/evm/batch_caller_test.go @@ -1,12 +1,12 @@ package evm_test import ( - "encoding/hex" "fmt" "math/big" "testing" "github.com/cometbft/cometbft/libs/rand" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/rpc" "github.com/pkg/errors" "github.com/stretchr/testify/assert" @@ -152,12 +152,10 @@ func TestDefaultEvmBatchCaller_batchCallLimit(t *testing.T) { Run(func(args mock.Arguments) { evmCalls := args.Get(1).([]rpc.BatchElem) for i := range evmCalls { - arg := evmCalls[i].Args[0].(map[string]interface{})["data"].([]uint8) - bytes, err := hex.DecodeString(fmt.Sprintf("%x", arg)) - require.NoError(t, err) + arg := evmCalls[i].Args[0].(map[string]interface{})["data"].(hexutil.Bytes) str, isOk := evmCalls[i].Result.(*string) require.True(t, isOk) - *str = fmt.Sprintf("0x%064x", new(big.Int).SetBytes(bytes[24:]).Uint64()) + *str = fmt.Sprintf("0x%064x", new(big.Int).SetBytes(arg[24:]).Uint64()) } }).Return(nil) diff --git a/core/services/relay/evm/ccip.go b/core/services/relay/evm/ccip.go index 945763de85..5ef84170dc 100644 --- a/core/services/relay/evm/ccip.go +++ b/core/services/relay/evm/ccip.go @@ -7,10 +7,11 @@ import ( "time" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "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/ccip" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers" ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config" @@ -134,14 +135,7 @@ type IncompleteDestCommitStoreReader struct { cs cciptypes.CommitStoreReader } -func NewIncompleteDestCommitStoreReader( - lggr logger.Logger, - versionFinder ccip.VersionFinder, - address cciptypes.Address, - ec client.Client, - lp logpoller.LogPoller, - feeEstimatorConfig estimatorconfig.FeeEstimatorConfigProvider, -) (*IncompleteDestCommitStoreReader, error) { +func NewIncompleteDestCommitStoreReader(lggr logger.Logger, versionFinder ccip.VersionFinder, address cciptypes.Address, ec client.Client, lp logpoller.LogPoller, feeEstimatorConfig estimatorconfig.FeeEstimatorConfigProvider) (*IncompleteDestCommitStoreReader, error) { cs, err := ccip.NewCommitStoreReader(lggr, versionFinder, address, ec, lp, feeEstimatorConfig) if err != nil { return nil, err diff --git a/core/services/relay/evm/chain_reader.go b/core/services/relay/evm/chain_reader.go index d84c2f00a9..0f1f6e72dd 100644 --- a/core/services/relay/evm/chain_reader.go +++ b/core/services/relay/evm/chain_reader.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/smartcontractkit/chainlink-common/pkg/codec" + "github.com/smartcontractkit/chainlink-common/pkg/logger" commonservices "github.com/smartcontractkit/chainlink-common/pkg/services" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/types/query" @@ -21,7 +22,6 @@ import ( evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" ) @@ -49,7 +49,7 @@ var _ commontypes.ContractTypeProvider = &chainReader{} // Note that the ChainReaderService returned does not support anonymous events. func NewChainReaderService(ctx context.Context, lggr logger.Logger, lp logpoller.LogPoller, ht logpoller.HeadTracker, client evmclient.Client, config types.ChainReaderConfig) (ChainReaderService, error) { cr := &chainReader{ - lggr: lggr.Named("ChainReader"), + lggr: logger.Named(lggr, "ChainReader"), ht: ht, lp: lp, client: client, @@ -128,6 +128,10 @@ func (cr *chainReader) init(chainContractReaders map[string]types.ChainContractR return err } } + + if cr.bindings.contractBindings[contractName] == nil { + return fmt.Errorf("%w: no read bindings added for contract: %s", commontypes.ErrInvalidConfig, contractName) + } cr.bindings.contractBindings[contractName].pollingFilter = chainContractReader.PollingFilter.ToLPFilter(eventSigsForContractFilter) } return nil @@ -259,7 +263,7 @@ func (cr *chainReader) addEvent(contractName, eventName string, a abi.ABI, chain return err } - // Encoder def's codec won't be used to encode, only for its type as input for GetLatestValue + // Encoder defs codec won't be used for encoding, but for storing caller filtering params which won't be hashed. if err := cr.addEncoderDef(contractName, eventName, filterArgs, nil, chainReaderDefinition.InputModifications); err != nil { return err } @@ -327,9 +331,11 @@ func (cr *chainReader) addQueryingReadBindings(contractName string, genericTopic } } +// getEventInput returns codec entry for expected incoming event params and the modifier to be applied to the params. func (cr *chainReader) getEventInput(def types.ChainReaderDefinition, contractName, eventName string) ( types.CodecEntry, codec.Modifier, error) { inputInfo := cr.parsed.EncoderDefs[WrapItemType(contractName, eventName, true)] + // TODO can this be simplified? Isn't this same as inputInfo.Modifier()? BCI-3909 inMod, err := def.InputModifications.ToModifier(DecoderHooks...) if err != nil { return nil, nil, err @@ -378,6 +384,8 @@ func (cr *chainReader) addDecoderDef(contractName, itemType string, outputs abi. return output.Init() } +// setupEventInput returns abi args where indexed flag is set to false because we expect caller to filter with params that aren't hashed. +// codecEntry has expected onchain types set, for e.g. indexed topics of type string or uint8[32] array are expected as common.Hash onchain. func setupEventInput(event abi.Event, inputFields []string) ([]abi.Argument, types.CodecEntry, map[string]bool) { topicFieldDefs := map[string]bool{} for _, value := range inputFields { diff --git a/core/services/relay/evm/chain_writer.go b/core/services/relay/evm/chain_writer.go index 466811d115..1e07003b88 100644 --- a/core/services/relay/evm/chain_writer.go +++ b/core/services/relay/evm/chain_writer.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink-common/pkg/logger" commonservices "github.com/smartcontractkit/chainlink-common/pkg/services" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" @@ -18,7 +19,6 @@ import ( evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" evmtxmgr "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" ) @@ -122,11 +122,16 @@ func (w *chainWriter) SubmitTransaction(ctx context.Context, contract, method st } } + gasLimit := methodConfig.GasLimit + if meta != nil && meta.GasLimit != nil { + gasLimit = meta.GasLimit.Uint64() + } + req := evmtxmgr.TxRequest{ FromAddress: methodConfig.FromAddress, ToAddress: common.HexToAddress(toAddress), EncodedPayload: calldata, - FeeLimit: methodConfig.GasLimit, + FeeLimit: gasLimit, Meta: txMeta, IdempotencyKey: &transactionID, Strategy: w.sendStrategy, diff --git a/core/services/relay/evm/chain_writer_test.go b/core/services/relay/evm/chain_writer_test.go index e3fc8f8e22..b70a0dd0e3 100644 --- a/core/services/relay/evm/chain_writer_test.go +++ b/core/services/relay/evm/chain_writer_test.go @@ -67,6 +67,7 @@ func TestChainWriter(t *testing.T) { status commontypes.TransactionStatus }{ {uuid.NewString(), commontypes.Unknown}, + {uuid.NewString(), commontypes.Pending}, {uuid.NewString(), commontypes.Unconfirmed}, {uuid.NewString(), commontypes.Finalized}, {uuid.NewString(), commontypes.Failed}, diff --git a/core/services/relay/evm/commit_provider.go b/core/services/relay/evm/commit_provider.go index 780d1cee7b..eb254a0e29 100644 --- a/core/services/relay/evm/commit_provider.go +++ b/core/services/relay/evm/commit_provider.go @@ -12,11 +12,12 @@ import ( commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/estimatorconfig" ) @@ -284,7 +285,6 @@ func (P *SrcCommitProvider) NewOffRampReader(ctx context.Context, offRampAddr cc } func (P *DstCommitProvider) NewOffRampReader(ctx context.Context, offRampAddr cciptypes.Address) (offRampReader cciptypes.OffRampReader, err error) { - offRampReader, err = ccip.NewOffRampReader(P.lggr, P.versionFinder, offRampAddr, P.client, P.lp, P.gasEstimator, &P.maxGasPrice, true, P.feeEstimatorConfig) return } diff --git a/core/services/relay/evm/config_poller.go b/core/services/relay/evm/config_poller.go index 2280d60d7e..a00b04b078 100644 --- a/core/services/relay/evm/config_poller.go +++ b/core/services/relay/evm/config_poller.go @@ -15,11 +15,11 @@ import ( "github.com/smartcontractkit/libocr/gethwrappers2/ocrconfigurationstoreevmsimple" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" - "github.com/smartcontractkit/chainlink/v2/core/logger" evmRelayTypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" ) diff --git a/core/services/relay/evm/contract_transmitter.go b/core/services/relay/evm/contract_transmitter.go index d594dfb921..e2065bc60f 100644 --- a/core/services/relay/evm/contract_transmitter.go +++ b/core/services/relay/evm/contract_transmitter.go @@ -12,13 +12,15 @@ import ( "github.com/ethereum/go-ethereum/common" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" + "github.com/smartcontractkit/libocr/offchainreporting2plus/chains/evmutil" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services" ) @@ -102,7 +104,7 @@ func NewOCRContractTransmitter( transmittedEventSig: transmitted.ID, lp: lp, contractReader: caller, - lggr: lggr.Named("OCRContractTransmitter"), + lggr: logger.Named(lggr, "OCRContractTransmitter"), reportToEvmTxMeta: reportToEvmTxMetaNoop, excludeSigs: false, retention: 0, diff --git a/core/services/relay/evm/event_binding.go b/core/services/relay/evm/event_binding.go index acfb1aa630..7b62d862b3 100644 --- a/core/services/relay/evm/event_binding.go +++ b/core/services/relay/evm/event_binding.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" "github.com/google/uuid" "github.com/smartcontractkit/chainlink-common/pkg/codec" @@ -168,7 +169,7 @@ func (e *eventBinding) QueryKey(ctx context.Context, filter query.KeyFilter, lim } remapped.Expressions = append(defaultExpressions, remapped.Expressions...) - logs, err := e.lp.FilteredLogs(ctx, remapped, limitAndSort, e.contractName+"-"+e.eventName) + logs, err := e.lp.FilteredLogs(ctx, remapped, limitAndSort, e.contractName+"-"+e.address.String()+"-"+e.eventName) if err != nil { return nil, err } @@ -209,11 +210,13 @@ func (e *eventBinding) getLatestValueWithFilters( return err } + // convert caller chain agnostic params types to types representing onchain abi types, for e.g. bytes32. checkedParams, err := e.inputModifier.TransformToOnChain(offChain, "" /* unused */) if err != nil { return err } + // convert onchain params to native types similarly to generated abi wrappers, for e.g. fixed bytes32 abi type to [32]uint8. nativeParams, err := e.inputInfo.ToNative(reflect.ValueOf(checkedParams)) if err != nil { return err @@ -224,34 +227,45 @@ func (e *eventBinding) getLatestValueWithFilters( return err } - fai := filtersAndIndices[0] - remainingFilters := filtersAndIndices[1:] - - logs, err := e.lp.IndexedLogs(ctx, e.hash, e.address, 1, []common.Hash{fai}, confs) + // Create limiter and filter for the query. + limiter := query.NewLimitAndSort(query.CountLimit(1), query.NewSortBySequence(query.Desc)) + filter, err := query.Where( + "", + logpoller.NewAddressFilter(e.address), + logpoller.NewEventSigFilter(e.hash), + logpoller.NewConfirmationsFilter(confs), + createTopicFilters(filtersAndIndices), + ) if err != nil { return wrapInternalErr(err) } - // TODO Use filtered logs here BCF-3316 - // TODO: there should be a better way to ask log poller to filter these - // First, you should be able to ask for as many topics to match - // Second, you should be able to get the latest only - var logToUse *logpoller.Log - for _, log := range logs { - tmp := log - if compareLogs(&tmp, logToUse) > 0 && matchesRemainingFilters(&tmp, remainingFilters) { - // copy so that it's not pointing to the changing variable - logToUse = &tmp - } + // Gets the latest log that matches the filter and limiter. + logs, err := e.lp.FilteredLogs(ctx, filter, limiter, e.contractName+"-"+e.address.String()+"-"+e.eventName) + if err != nil { + return wrapInternalErr(err) } - if logToUse == nil { + if len(logs) == 0 { return fmt.Errorf("%w: no events found", commontypes.ErrNotFound) } - return e.decodeLog(ctx, logToUse, into) + return e.decodeLog(ctx, &logs[0], into) +} + +func createTopicFilters(filtersAndIndices []common.Hash) query.Expression { + var expressions []query.Expression + for topicID, fai := range filtersAndIndices { + // first topic index is 1-based, so we add 1. + expressions = append(expressions, logpoller.NewEventByTopicFilter( + uint64(topicID+1), []primitives.ValueComparator{{Value: fai.Hex(), Operator: primitives.Eq}}, + )) + } + return query.And(expressions...) } +// convertToOffChainType creates a struct based on contract abi with applied codec modifiers. +// Created type shouldn't have hashed types for indexed topics since incoming params wouldn't be hashed. func (e *eventBinding) convertToOffChainType(params any) (any, error) { offChain, err := e.codec.CreateType(WrapItemType(e.contractName, e.eventName, true), true) if err != nil { @@ -265,65 +279,35 @@ func (e *eventBinding) convertToOffChainType(params any) (any, error) { return offChain, nil } -func compareLogs(log, use *logpoller.Log) int64 { - if use == nil { - return 1 - } - - if log.BlockNumber != use.BlockNumber { - return log.BlockNumber - use.BlockNumber +// encodeParams accepts nativeParams and encodes them to match onchain topics. +func (e *eventBinding) encodeParams(nativeParams reflect.Value) ([]common.Hash, error) { + for nativeParams.Kind() == reflect.Pointer { + nativeParams = reflect.Indirect(nativeParams) } - return log.LogIndex - use.LogIndex -} - -func matchesRemainingFilters(log *logpoller.Log, filters []common.Hash) bool { - for i, rfai := range filters { - if !reflect.DeepEqual(rfai[:], log.Topics[i+2]) { - return false - } - } - - return true -} - -func (e *eventBinding) encodeParams(item reflect.Value) ([]common.Hash, error) { - for item.Kind() == reflect.Pointer { - item = reflect.Indirect(item) - } - - var topics []any - switch item.Kind() { + var params []any + switch nativeParams.Kind() { case reflect.Array, reflect.Slice: - native, err := representArray(item, e.inputInfo) + native, err := representArray(nativeParams, e.inputInfo) if err != nil { return nil, err } - topics = []any{native} + params = []any{native} case reflect.Struct, reflect.Map: var err error - if topics, err = unrollItem(item, e.inputInfo); err != nil { + if params, err = unrollItem(nativeParams, e.inputInfo); err != nil { return nil, err } default: - return nil, fmt.Errorf("%w: cannot encode kind %v", commontypes.ErrInvalidType, item.Kind()) + return nil, fmt.Errorf("%w: cannot encode kind %v", commontypes.ErrInvalidType, nativeParams.Kind()) } - // abi params allow you to Pack a pointers, but MakeTopics doesn't work with pointers. - if err := e.derefTopics(topics); err != nil { + // abi params allow you to Pack a pointers, but makeTopics doesn't work with pointers. + if err := e.derefTopics(params); err != nil { return nil, err } - hashes, err := abi.MakeTopics(topics) - if err != nil { - return nil, wrapInternalErr(err) - } - - if len(hashes) != 1 { - return nil, fmt.Errorf("%w: expected 1 filter set, got %d", commontypes.ErrInternal, len(hashes)) - } - - return hashes[0], nil + return e.makeTopics(params) } func (e *eventBinding) derefTopics(topics []any) error { @@ -340,11 +324,38 @@ func (e *eventBinding) derefTopics(topics []any) error { return nil } +// makeTopics encodes and hashes params filtering values to match onchain indexed topics. +func (e *eventBinding) makeTopics(params []any) ([]common.Hash, error) { + // make topic value for non-fixed bytes array manually because geth MakeTopics doesn't support it + for i, topic := range params { + if abiArg := e.inputInfo.Args()[i]; abiArg.Type.T == abi.ArrayTy && (abiArg.Type.Elem != nil && abiArg.Type.Elem.T == abi.UintTy) { + packed, err := abi.Arguments{abiArg}.Pack(topic) + if err != nil { + return nil, err + } + params[i] = crypto.Keccak256Hash(packed) + } + } + + hashes, err := abi.MakeTopics(params) + if err != nil { + return nil, wrapInternalErr(err) + } + + if len(hashes) != 1 { + return nil, fmt.Errorf("%w: expected 1 filter set, got %d", commontypes.ErrInternal, len(hashes)) + } + + return hashes[0], nil +} + func (e *eventBinding) decodeLog(ctx context.Context, log *logpoller.Log, into any) error { + // decode non indexed topics and apply output modifiers if err := e.codec.Decode(ctx, log.Data, into, WrapItemType(e.contractName, e.eventName, false)); err != nil { return err } + // decode indexed topics which is rarely useful since most indexed topic types get Keccak256 hashed and should be just used for log filtering. topics := make([]common.Hash, len(e.codecTopicInfo.Args())) if len(log.Topics) < len(topics)+1 { return fmt.Errorf("%w: not enough topics to decode", commontypes.ErrInvalidType) @@ -436,6 +447,7 @@ func (e *eventBinding) remapExpression(key string, expression query.Expression) // remap chain agnostic primitives to chain specific func (e *eventBinding) remapPrimitive(key string, expression query.Expression) (query.Expression, error) { switch primitive := expression.Primitive.(type) { + // TODO comparator primitive should undergo codec transformations and do hashed types handling similarly to how GetLatestValue handles it BCI-3910 case *primitives.Comparator: if val, ok := e.eventDataWords[primitive.Name]; ok { return logpoller.NewEventByWordFilter(e.hash, val, primitive.ValueComparators), nil diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index ae5244f5f0..19016426e5 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "math/big" + "net/http" "strings" "sync" @@ -32,8 +33,11 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median/evmreportcodec" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + chainselectors "github.com/smartcontractkit/chain-selectors" + ocr3capability "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3" "github.com/smartcontractkit/chainlink-common/pkg/capabilities/triggers" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" @@ -43,7 +47,6 @@ import ( txm "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/llo" "github.com/smartcontractkit/chainlink/v2/core/services/llo/bm" @@ -57,6 +60,7 @@ import ( reportcodecv1 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v1/reportcodec" reportcodecv2 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v2/reportcodec" reportcodecv3 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v3/reportcodec" + reportcodecv4 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v4/reportcodec" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" ) @@ -139,10 +143,9 @@ func (u UnimplementedContractTransmitter) LatestConfigDigestAndEpoch(ctx context type Relayer struct { ds sqlutil.DataSource chain legacyevm.Chain - lggr logger.Logger + lggr logger.SugaredLogger ks CSAETHKeystore mercuryPool wsrpc.Pool - chainReader commontypes.ContractReader codec commontypes.Codec capabilitiesRegistry coretypes.CapabilitiesRegistry @@ -152,8 +155,7 @@ type Relayer struct { triggerCapability *triggers.MercuryTriggerService // LLO/data streams - cdcFactory llo.ChannelDefinitionCacheFactory - lloORM llo.ORM + cdcFactory func() (llo.ChannelDefinitionCacheFactory, error) } type CSAETHKeystore interface { @@ -167,6 +169,7 @@ type RelayerOpts struct { MercuryPool wsrpc.Pool TransmitterConfig mercury.TransmitterConfig CapabilitiesRegistry coretypes.CapabilitiesRegistry + HTTPClient *http.Client } func (c RelayerOpts) Validate() error { @@ -191,19 +194,24 @@ func NewRelayer(lggr logger.Logger, chain legacyevm.Chain, opts RelayerOpts) (*R if err != nil { return nil, fmt.Errorf("cannot create evm relayer: %w", err) } - lggr = lggr.Named("Relayer") + sugared := logger.Sugared(lggr).Named("Relayer") mercuryORM := mercury.NewORM(opts.DS) - lloORM := llo.NewORM(opts.DS, chain.ID()) - cdcFactory := llo.NewChannelDefinitionCacheFactory(lggr, lloORM, chain.LogPoller()) + cdcFactory := sync.OnceValues(func() (llo.ChannelDefinitionCacheFactory, error) { + chainSelector, err := chainselectors.SelectorFromChainId(chain.ID().Uint64()) + if err != nil { + return nil, fmt.Errorf("failed to get chain selector for chain id %s: %w", chain.ID(), err) + } + lloORM := llo.NewORM(opts.DS, chainSelector) + return llo.NewChannelDefinitionCacheFactory(sugared, lloORM, chain.LogPoller(), opts.HTTPClient), nil + }) relayer := &Relayer{ ds: opts.DS, chain: chain, - lggr: lggr, + lggr: sugared, ks: opts.CSAETHKeystore, mercuryPool: opts.MercuryPool, cdcFactory: cdcFactory, - lloORM: lloORM, mercuryORM: mercuryORM, transmitterCfg: opts.TransmitterConfig, capabilitiesRegistry: opts.CapabilitiesRegistry, @@ -212,7 +220,8 @@ func NewRelayer(lggr logger.Logger, chain legacyevm.Chain, opts RelayerOpts) (*R // Initialize write target capability if configuration is defined if chain.Config().EVM().Workflow().ForwarderAddress() != nil { ctx := context.Background() - capability, err := NewWriteTarget(ctx, relayer, chain, lggr) + capability, err := NewWriteTarget(ctx, relayer, chain, chain.Config().EVM().Workflow().DefaultGasLimit(), + lggr) if err != nil { return nil, fmt.Errorf("failed to initialize write target: %w", err) } @@ -272,10 +281,14 @@ func (r *Relayer) NewOCR3CapabilityProvider(rargs commontypes.RelayArgs, pargs c func (r *Relayer) NewPluginProvider(rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (commontypes.PluginProvider, error) { // TODO https://smartcontract-it.atlassian.net/browse/BCF-2887 ctx := context.Background() + lggr := logger.Sugared(r.lggr).Named("PluginProvider").Named(rargs.ExternalJobID.String()) + relayOpts := types.NewRelayOpts(rargs) + relayConfig, err := relayOpts.RelayConfig() + if err != nil { + return nil, fmt.Errorf("failed to get relay config: %w", err) + } - lggr := r.lggr.Named("PluginProvider").Named(rargs.ExternalJobID.String()) - - configWatcher, err := newStandardConfigProvider(ctx, r.lggr, r.chain, types.NewRelayOpts(rargs)) + configWatcher, err := newStandardConfigProvider(ctx, r.lggr, r.chain, relayOpts) if err != nil { return nil, err } @@ -285,8 +298,17 @@ func (r *Relayer) NewPluginProvider(rargs commontypes.RelayArgs, pargs commontyp return nil, err } + var chainReaderService ChainReaderService + if relayConfig.ChainReader != nil { + if chainReaderService, err = NewChainReaderService(ctx, lggr, r.chain.LogPoller(), r.chain.HeadTracker(), r.chain.Client(), *relayConfig.ChainReader); err != nil { + return nil, err + } + } else { + lggr.Info("ChainReader missing from RelayConfig") + } + return NewPluginProvider( - r.chainReader, + chainReaderService, r.codec, transmitter, configWatcher, @@ -297,7 +319,7 @@ func (r *Relayer) NewPluginProvider(rargs commontypes.RelayArgs, pargs commontyp func (r *Relayer) NewMercuryProvider(rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (commontypes.MercuryProvider, error) { // TODO https://smartcontract-it.atlassian.net/browse/BCF-2887 ctx := context.Background() - lggr := r.lggr.Named("MercuryProvider").Named(rargs.ExternalJobID.String()) + lggr := logger.Sugared(r.lggr).Named("MercuryProvider").Named(rargs.ExternalJobID.String()) relayOpts := types.NewRelayOpts(rargs) relayConfig, err := relayOpts.RelayConfig() if err != nil { @@ -361,6 +383,7 @@ func (r *Relayer) NewMercuryProvider(rargs commontypes.RelayArgs, pargs commonty reportCodecV1 := reportcodecv1.NewReportCodec(*relayConfig.FeedID, lggr.Named("ReportCodecV1")) reportCodecV2 := reportcodecv2.NewReportCodec(*relayConfig.FeedID, lggr.Named("ReportCodecV2")) reportCodecV3 := reportcodecv3.NewReportCodec(*relayConfig.FeedID, lggr.Named("ReportCodecV3")) + reportCodecV4 := reportcodecv4.NewReportCodec(*relayConfig.FeedID, lggr.Named("ReportCodecV4")) var transmitterCodec mercury.TransmitterReportDecoder switch feedID.Version() { @@ -370,12 +393,14 @@ func (r *Relayer) NewMercuryProvider(rargs commontypes.RelayArgs, pargs commonty transmitterCodec = reportCodecV2 case 3: transmitterCodec = reportCodecV3 + case 4: + transmitterCodec = reportCodecV4 default: return nil, fmt.Errorf("invalid feed version %d", feedID.Version()) } transmitter := mercury.NewTransmitter(lggr, r.transmitterCfg, clients, privKey.PublicKey, rargs.JobID, *relayConfig.FeedID, r.mercuryORM, transmitterCodec, r.triggerCapability) - return NewMercuryProvider(cp, r.chainReader, r.codec, NewMercuryChainReader(r.chain.HeadTracker()), transmitter, reportCodecV1, reportCodecV2, reportCodecV3, lggr), nil + return NewMercuryProvider(cp, r.codec, NewMercuryChainReader(r.chain.HeadTracker()), transmitter, reportCodecV1, reportCodecV2, reportCodecV3, reportCodecV4, lggr), nil } func chainToUUID(chainID *big.Int) uuid.UUID { @@ -613,7 +638,11 @@ func (r *Relayer) NewLLOProvider(rargs commontypes.RelayArgs, pargs commontypes. transmitter = llo.NewTransmitter(r.lggr, client, privKey.PublicKey) } - cdc, err := r.cdcFactory.NewCache(lloCfg) + cdcFactory, err := r.cdcFactory() + if err != nil { + return nil, err + } + cdc, err := cdcFactory.NewCache(lloCfg) if err != nil { return nil, err } @@ -692,16 +721,15 @@ func FilterNamesFromRelayArgs(args commontypes.RelayArgs) (filterNames []string, } type configWatcher struct { - services.StateMachine - lggr logger.Logger + services.Service + eng *services.Engine + contractAddress common.Address offchainDigester ocrtypes.OffchainConfigDigester configPoller types.ConfigPoller chain legacyevm.Chain runReplay bool fromBlock uint64 - stopCh services.StopChan - wg sync.WaitGroup } func newConfigWatcher(lggr logger.Logger, @@ -712,54 +740,41 @@ func newConfigWatcher(lggr logger.Logger, fromBlock uint64, runReplay bool, ) *configWatcher { - return &configWatcher{ - lggr: lggr.Named("ConfigWatcher").Named(contractAddress.String()), + cw := &configWatcher{ contractAddress: contractAddress, offchainDigester: offchainDigester, configPoller: configPoller, chain: chain, runReplay: runReplay, fromBlock: fromBlock, - stopCh: make(chan struct{}), } + cw.Service, cw.eng = services.Config{ + Name: fmt.Sprintf("ConfigWatcher.%s", contractAddress), + NewSubServices: nil, + Start: cw.start, + Close: cw.close, + }.NewServiceEngine(lggr) + return cw } -func (c *configWatcher) Name() string { - return c.lggr.Name() -} - -func (c *configWatcher) Start(ctx context.Context) error { - return c.StartOnce(fmt.Sprintf("configWatcher %x", c.contractAddress), func() error { - if c.runReplay && c.fromBlock != 0 { - // Only replay if it's a brand runReplay job. - c.wg.Add(1) - go func() { - defer c.wg.Done() - ctx, cancel := c.stopCh.NewCtx() - defer cancel() - c.lggr.Infow("starting replay for config", "fromBlock", c.fromBlock) - if err := c.configPoller.Replay(ctx, int64(c.fromBlock)); err != nil { - c.lggr.Errorw("error replaying for config", "err", err) - } else { - c.lggr.Infow("completed replaying for config", "fromBlock", c.fromBlock) - } - }() - } - c.configPoller.Start() - return nil - }) -} - -func (c *configWatcher) Close() error { - return c.StopOnce(fmt.Sprintf("configWatcher %x", c.contractAddress), func() error { - close(c.stopCh) - c.wg.Wait() - return c.configPoller.Close() - }) +func (c *configWatcher) start(ctx context.Context) error { + if c.runReplay && c.fromBlock != 0 { + // Only replay if it's a brand runReplay job. + c.eng.Go(func(ctx context.Context) { + c.eng.Infow("starting replay for config", "fromBlock", c.fromBlock) + if err := c.configPoller.Replay(ctx, int64(c.fromBlock)); err != nil { + c.eng.Errorw("error replaying for config", "err", err) + } else { + c.eng.Infow("completed replaying for config", "fromBlock", c.fromBlock) + } + }) + } + c.configPoller.Start() + return nil } -func (c *configWatcher) HealthReport() map[string]error { - return map[string]error{c.Name(): c.Healthy()} +func (c *configWatcher) close() error { + return c.configPoller.Close() } func (c *configWatcher) OffchainConfigDigester() ocrtypes.OffchainConfigDigester { @@ -913,7 +928,7 @@ func (r *Relayer) NewMedianProvider(rargs commontypes.RelayArgs, pargs commontyp // TODO https://smartcontract-it.atlassian.net/browse/BCF-2887 ctx := context.Background() - lggr := r.lggr.Named("MedianProvider").Named(rargs.ExternalJobID.String()) + lggr := logger.Sugared(r.lggr).Named("MedianProvider").Named(rargs.ExternalJobID.String()) relayOpts := types.NewRelayOpts(rargs) relayConfig, err := relayOpts.RelayConfig() if err != nil { @@ -982,7 +997,7 @@ func (r *Relayer) NewMedianProvider(rargs commontypes.RelayArgs, pargs commontyp } func (r *Relayer) NewAutomationProvider(rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (commontypes.AutomationProvider, error) { - lggr := r.lggr.Named("AutomationProvider").Named(rargs.ExternalJobID.String()) + lggr := logger.Sugared(r.lggr).Named("AutomationProvider").Named(rargs.ExternalJobID.String()) ocr2keeperRelayer := NewOCR2KeeperRelayer(r.ds, r.chain, lggr.Named("OCR2KeeperRelayer"), r.ks.Eth()) return ocr2keeperRelayer.NewOCR2KeeperProvider(rargs, pargs) diff --git a/core/services/relay/evm/evmtesting/chain_reader_interface_tester.go b/core/services/relay/evm/evmtesting/chain_reader_interface_tester.go index 4474f054db..7812ab202b 100644 --- a/core/services/relay/evm/evmtesting/chain_reader_interface_tester.go +++ b/core/services/relay/evm/evmtesting/chain_reader_interface_tester.go @@ -32,9 +32,10 @@ import ( ) const ( - triggerWithDynamicTopic = "TriggeredEventWithDynamicTopic" - triggerWithAllTopics = "TriggeredWithFourTopics" - finalityDepth = 4 + triggerWithDynamicTopic = "TriggeredEventWithDynamicTopic" + triggerWithAllTopics = "TriggeredWithFourTopics" + triggerWithAllTopicsWithHashed = "TriggeredWithFourTopicsWithHashed" + finalityDepth = 4 ) type EVMChainReaderInterfaceTesterHelper[T TestingT[T]] interface { @@ -96,7 +97,7 @@ func (it *EVMChainReaderInterfaceTester[T]) Setup(t T) { AnyContractName: { ContractABI: chain_reader_tester.ChainReaderTesterMetaData.ABI, ContractPollingFilter: types.ContractPollingFilter{ - GenericEventNames: []string{EventName, EventWithFilterName}, + GenericEventNames: []string{EventName, EventWithFilterName, triggerWithAllTopicsWithHashed}, }, Configs: map[string]*types.ChainReaderDefinition{ MethodTakingLatestParamsReturningTestStruct: &methodTakingLatestParamsReturningTestStructConfig, @@ -145,6 +146,13 @@ func (it *EVMChainReaderInterfaceTester[T]) Setup(t T) { // These float values can map to different finality concepts across chains. ConfidenceConfirmations: map[string]int{"0.0": int(evmtypes.Unconfirmed), "1.0": int(evmtypes.Finalized)}, }, + triggerWithAllTopicsWithHashed: { + ChainSpecificName: triggerWithAllTopicsWithHashed, + ReadType: types.Event, + EventDefinitions: &types.EventDefinitions{ + InputFields: []string{"Field1", "Field2", "Field3"}, + }, + }, MethodReturningSeenStruct: { ChainSpecificName: "returnSeen", InputModifications: codec.ModifiersConfig{ diff --git a/core/services/relay/evm/evmtesting/run_tests.go b/core/services/relay/evm/evmtesting/run_tests.go index f958c055ca..caa24e8ae2 100644 --- a/core/services/relay/evm/evmtesting/run_tests.go +++ b/core/services/relay/evm/evmtesting/run_tests.go @@ -12,10 +12,9 @@ import ( clcommontypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/types/query/primitives" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" . "github.com/smartcontractkit/chainlink-common/pkg/types/interfacetests" //nolint common practice to import test mods with . - - "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" ) func RunChainReaderEvmTests[T TestingT[T]](t T, it *EVMChainReaderInterfaceTester[T]) { @@ -74,6 +73,31 @@ func RunChainReaderEvmTests[T TestingT[T]](t T, it *EVMChainReaderInterfaceTeste assert.Equal(t, int32(3), latest.Field3) }) + t.Run("Filtering can be done on indexed topics that get hashed", func(t T) { + it.Setup(t) + it.dirtyContracts = true + triggerFourTopicsWithHashed(t, it, "1", [32]uint8{2}, [32]byte{5}) + triggerFourTopicsWithHashed(t, it, "2", [32]uint8{2}, [32]byte{3}) + triggerFourTopicsWithHashed(t, it, "1", [32]uint8{3}, [32]byte{3}) + + ctx := it.Helper.Context(t) + cr := it.GetChainReader(t) + require.NoError(t, cr.Bind(ctx, it.GetBindings(t))) + var latest struct { + Field3 [32]byte + } + params := struct { + Field1 string + Field2 [32]uint8 + Field3 [32]byte + }{Field1: "1", Field2: [32]uint8{2}, Field3: [32]byte{5}} + + time.Sleep(it.MaxWaitTimeForEvents()) + require.NoError(t, cr.GetLatestValue(ctx, AnyContractName, triggerWithAllTopicsWithHashed, primitives.Unconfirmed, params, &latest)) + // only checking Field3 topic makes sense since it isn't hashed, to check other fields we'd have to replicate solidity encoding and hashing + assert.Equal(t, [32]uint8{5}, latest.Field3) + }) + t.Run("Bind returns error on missing contract at address", func(t T) { it.Setup(t) @@ -95,3 +119,12 @@ func triggerFourTopics[T TestingT[T]](t T, it *EVMChainReaderInterfaceTester[T], it.IncNonce() it.AwaitTx(t, tx) } + +func triggerFourTopicsWithHashed[T TestingT[T]](t T, it *EVMChainReaderInterfaceTester[T], i1 string, i2 [32]uint8, i3 [32]byte) { + tx, err := it.contractTesters[it.address].ChainReaderTesterTransactor.TriggerWithFourTopicsWithHashed(it.GetAuthWithGasSet(t), i1, i2, i3) + require.NoError(t, err) + require.NoError(t, err) + it.Helper.Commit() + it.IncNonce() + it.AwaitTx(t, tx) +} diff --git a/core/services/relay/evm/exec_provider.go b/core/services/relay/evm/exec_provider.go index e50ae41351..d837133e61 100644 --- a/core/services/relay/evm/exec_provider.go +++ b/core/services/relay/evm/exec_provider.go @@ -16,12 +16,13 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccip" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/estimatorconfig" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/tokendata/usdc" @@ -363,7 +364,6 @@ func (d *DstExecProvider) NewCommitStoreReader(ctx context.Context, addr cciptyp } func (d *DstExecProvider) NewOffRampReader(ctx context.Context, offRampAddress cciptypes.Address) (offRampReader cciptypes.OffRampReader, err error) { - offRampReader, err = ccip.NewOffRampReader(d.lggr, d.versionFinder, offRampAddress, d.client, d.lp, d.gasEstimator, &d.maxGasPrice, true, d.feeEstimatorConfig) return } diff --git a/core/services/relay/evm/functions.go b/core/services/relay/evm/functions.go index a04a991e37..1f1554eb11 100644 --- a/core/services/relay/evm/functions.go +++ b/core/services/relay/evm/functions.go @@ -8,17 +8,15 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" - "go.uber.org/multierr" - ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txm "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/functions/config" functionsRelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/functions" @@ -26,12 +24,29 @@ import ( ) type functionsProvider struct { - services.StateMachine + services.Service + eng *services.Engine + configWatcher *configWatcher contractTransmitter ContractTransmitter logPollerWrapper evmRelayTypes.LogPollerWrapper } +func newFunctionsProvider(lggr logger.Logger, cw *configWatcher, ct ContractTransmitter, lpw evmRelayTypes.LogPollerWrapper) *functionsProvider { + p := &functionsProvider{ + configWatcher: cw, + contractTransmitter: ct, + logPollerWrapper: lpw, + } + p.Service, p.eng = services.Config{ + Name: "FunctionsProvider", + NewSubServices: func(lggr logger.Logger) []services.Service { + return []services.Service{p.configWatcher, p.logPollerWrapper} + }, + }.NewServiceEngine(lggr) + return p +} + var _ evmRelayTypes.FunctionsProvider = (*functionsProvider)(nil) func (p *functionsProvider) ContractTransmitter() ocrtypes.ContractTransmitter { @@ -47,23 +62,6 @@ func (p *functionsProvider) FunctionsEvents() commontypes.FunctionsEvents { return nil } -func (p *functionsProvider) Start(ctx context.Context) error { - return p.StartOnce("FunctionsProvider", func() error { - if err := p.configWatcher.Start(ctx); err != nil { - return err - } - return p.logPollerWrapper.Start(ctx) - }) -} - -func (p *functionsProvider) Close() error { - return p.StopOnce("FunctionsProvider", func() (err error) { - err = multierr.Combine(err, p.logPollerWrapper.Close()) - err = multierr.Combine(err, p.configWatcher.Close()) - return - }) -} - // Forward all calls to the underlying configWatcher func (p *functionsProvider) OffchainConfigDigester() ocrtypes.OffchainConfigDigester { return p.configWatcher.OffchainConfigDigester() @@ -73,14 +71,6 @@ func (p *functionsProvider) ContractConfigTracker() ocrtypes.ContractConfigTrack return p.configWatcher.ContractConfigTracker() } -func (p *functionsProvider) HealthReport() map[string]error { - return p.configWatcher.HealthReport() -} - -func (p *functionsProvider) Name() string { - return p.configWatcher.Name() -} - func (p *functionsProvider) ChainReader() commontypes.ContractReader { return nil } @@ -127,11 +117,7 @@ func NewFunctionsProvider(ctx context.Context, chain legacyevm.Chain, rargs comm } else { lggr.Warn("no sending keys configured for functions plugin, not starting contract transmitter") } - return &functionsProvider{ - configWatcher: configWatcher, - contractTransmitter: contractTransmitter, - logPollerWrapper: logPollerWrapper, - }, nil + return newFunctionsProvider(lggr, configWatcher, contractTransmitter, logPollerWrapper), nil } func newFunctionsConfigProvider(ctx context.Context, pluginType functionsRelay.FunctionsPluginType, chain legacyevm.Chain, args commontypes.RelayArgs, fromBlock uint64, logPollerWrapper evmRelayTypes.LogPollerWrapper, lggr logger.Logger) (*configWatcher, error) { diff --git a/core/services/relay/evm/functions/config_poller.go b/core/services/relay/evm/functions/config_poller.go index 71616f2e84..2cb21738b9 100644 --- a/core/services/relay/evm/functions/config_poller.go +++ b/core/services/relay/evm/functions/config_poller.go @@ -9,12 +9,13 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" - "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" + "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" ) diff --git a/core/services/relay/evm/functions/contract_transmitter.go b/core/services/relay/evm/functions/contract_transmitter.go index 23143ed3ef..f588a02390 100644 --- a/core/services/relay/evm/functions/contract_transmitter.go +++ b/core/services/relay/evm/functions/contract_transmitter.go @@ -13,14 +13,16 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" + "github.com/smartcontractkit/libocr/offchainreporting2plus/chains/evmutil" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/functions/encoding" evmRelayTypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" @@ -105,7 +107,7 @@ func NewFunctionsContractTransmitter( transmittedEventSig: transmitted.ID, lp: lp, contractReader: caller, - lggr: lggr.Named("OCRFunctionsContractTransmitter"), + lggr: logger.Named(lggr, "OCRFunctionsContractTransmitter"), contractVersion: contractVersion, reportCodec: codec, txm: txm, diff --git a/core/services/relay/evm/functions/logpoller_wrapper.go b/core/services/relay/evm/functions/logpoller_wrapper.go index 559b1ec33f..260c366eb8 100644 --- a/core/services/relay/evm/functions/logpoller_wrapper.go +++ b/core/services/relay/evm/functions/logpoller_wrapper.go @@ -10,19 +10,20 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_coordinator" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_router" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/functions/config" evmRelayTypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" ) type logPollerWrapper struct { - services.StateMachine + services.Service + eng *services.Engine routerContract *functions_router.FunctionsRouter pluginConfig config.PluginConfig @@ -38,9 +39,6 @@ type logPollerWrapper struct { detectedRequests detectedEvents detectedResponses detectedEvents mu sync.Mutex - closeWait sync.WaitGroup - stopCh services.StopChan - lggr logger.Logger } type detectedEvent struct { @@ -94,7 +92,7 @@ func NewLogPollerWrapper(routerContractAddress common.Address, pluginConfig conf return nil, errors.Errorf("invalid config: number of required confirmation blocks >= pastBlocksToPoll") } - return &logPollerWrapper{ + w := &logPollerWrapper{ routerContract: routerContract, pluginConfig: pluginConfig, requestBlockOffset: requestBlockOffset, @@ -106,40 +104,25 @@ func NewLogPollerWrapper(routerContractAddress common.Address, pluginConfig conf logPoller: logPoller, client: client, subscribers: make(map[string]evmRelayTypes.RouteUpdateSubscriber), - stopCh: make(services.StopChan), - lggr: lggr.Named("LogPollerWrapper"), - }, nil -} - -func (l *logPollerWrapper) Start(context.Context) error { - return l.StartOnce("LogPollerWrapper", func() error { - l.lggr.Infow("starting LogPollerWrapper", "routerContract", l.routerContract.Address().Hex(), "contractVersion", l.pluginConfig.ContractVersion) - l.mu.Lock() - defer l.mu.Unlock() - if l.pluginConfig.ContractVersion != 1 { - return errors.New("only contract version 1 is supported") - } - l.closeWait.Add(1) - go l.checkForRouteUpdates() - return nil - }) -} - -func (l *logPollerWrapper) Close() error { - return l.StopOnce("LogPollerWrapper", func() (err error) { - l.lggr.Info("closing LogPollerWrapper") - close(l.stopCh) - l.closeWait.Wait() - return nil - }) + } + w.Service, w.eng = services.Config{ + Name: "LoggPollerWrapper", + Start: w.start, + }.NewServiceEngine(lggr) + return w, nil } -func (l *logPollerWrapper) HealthReport() map[string]error { - return map[string]error{l.Name(): l.Ready()} +func (l *logPollerWrapper) start(context.Context) error { + l.eng.Infow("starting LogPollerWrapper", "routerContract", l.routerContract.Address().Hex(), "contractVersion", l.pluginConfig.ContractVersion) + l.mu.Lock() + defer l.mu.Unlock() + if l.pluginConfig.ContractVersion != 1 { + return errors.New("only contract version 1 is supported") + } + l.eng.Go(l.checkForRouteUpdates) + return nil } -func (l *logPollerWrapper) Name() string { return l.lggr.Name() } - // methods of LogPollerWrapper func (l *logPollerWrapper) LatestEvents(ctx context.Context) ([]evmRelayTypes.OracleRequest, []evmRelayTypes.OracleResponse, error) { l.mu.Lock() @@ -166,7 +149,7 @@ func (l *logPollerWrapper) LatestEvents(ctx context.Context) ([]evmRelayTypes.Or resultsReq := []evmRelayTypes.OracleRequest{} resultsResp := []evmRelayTypes.OracleResponse{} if len(coordinators) == 0 { - l.lggr.Debug("LatestEvents: no non-zero coordinators to check") + l.eng.Debug("LatestEvents: no non-zero coordinators to check") return resultsReq, resultsResp, errors.New("no non-zero coordinators to check") } @@ -174,32 +157,32 @@ func (l *logPollerWrapper) LatestEvents(ctx context.Context) ([]evmRelayTypes.Or requestEndBlock := latestBlockNum - l.requestBlockOffset requestLogs, err := l.logPoller.Logs(ctx, startBlockNum, requestEndBlock, functions_coordinator.FunctionsCoordinatorOracleRequest{}.Topic(), coordinator) if err != nil { - l.lggr.Errorw("LatestEvents: fetching request logs from LogPoller failed", "startBlock", startBlockNum, "endBlock", requestEndBlock) + l.eng.Errorw("LatestEvents: fetching request logs from LogPoller failed", "startBlock", startBlockNum, "endBlock", requestEndBlock) return nil, nil, err } - l.lggr.Debugw("LatestEvents: fetched request logs", "nRequestLogs", len(requestLogs), "latestBlock", latest, "startBlock", startBlockNum, "endBlock", requestEndBlock) + l.eng.Debugw("LatestEvents: fetched request logs", "nRequestLogs", len(requestLogs), "latestBlock", latest, "startBlock", startBlockNum, "endBlock", requestEndBlock) requestLogs = l.filterPreviouslyDetectedEvents(requestLogs, &l.detectedRequests, "requests") responseEndBlock := latestBlockNum - l.responseBlockOffset responseLogs, err := l.logPoller.Logs(ctx, startBlockNum, responseEndBlock, functions_coordinator.FunctionsCoordinatorOracleResponse{}.Topic(), coordinator) if err != nil { - l.lggr.Errorw("LatestEvents: fetching response logs from LogPoller failed", "startBlock", startBlockNum, "endBlock", responseEndBlock) + l.eng.Errorw("LatestEvents: fetching response logs from LogPoller failed", "startBlock", startBlockNum, "endBlock", responseEndBlock) return nil, nil, err } - l.lggr.Debugw("LatestEvents: fetched request logs", "nResponseLogs", len(responseLogs), "latestBlock", latest, "startBlock", startBlockNum, "endBlock", responseEndBlock) + l.eng.Debugw("LatestEvents: fetched request logs", "nResponseLogs", len(responseLogs), "latestBlock", latest, "startBlock", startBlockNum, "endBlock", responseEndBlock) responseLogs = l.filterPreviouslyDetectedEvents(responseLogs, &l.detectedResponses, "responses") parsingContract, err := functions_coordinator.NewFunctionsCoordinator(coordinator, l.client) if err != nil { - l.lggr.Error("LatestEvents: creating a contract instance for parsing failed") + l.eng.Error("LatestEvents: creating a contract instance for parsing failed") return nil, nil, err } - l.lggr.Debugw("LatestEvents: parsing logs", "nRequestLogs", len(requestLogs), "nResponseLogs", len(responseLogs), "coordinatorAddress", coordinator.Hex()) + l.eng.Debugw("LatestEvents: parsing logs", "nRequestLogs", len(requestLogs), "nResponseLogs", len(responseLogs), "coordinatorAddress", coordinator.Hex()) for _, log := range requestLogs { gethLog := log.ToGethLog() oracleRequest, err := parsingContract.ParseOracleRequest(gethLog) if err != nil { - l.lggr.Errorw("LatestEvents: failed to parse a request log, skipping", "err", err) + l.eng.Errorw("LatestEvents: failed to parse a request log, skipping", "err", err) continue } @@ -212,7 +195,7 @@ func (l *logPollerWrapper) LatestEvents(ctx context.Context) ([]evmRelayTypes.Or bytes32Type, errType7 := abi.NewType("bytes32", "bytes32", nil) if errType1 != nil || errType2 != nil || errType3 != nil || errType4 != nil || errType5 != nil || errType6 != nil || errType7 != nil { - l.lggr.Errorw("LatestEvents: failed to initialize types", "errType1", errType1, + l.eng.Errorw("LatestEvents: failed to initialize types", "errType1", errType1, "errType2", errType2, "errType3", errType3, "errType4", errType4, "errType5", errType5, "errType6", errType6, "errType7", errType7, ) continue @@ -244,7 +227,7 @@ func (l *logPollerWrapper) LatestEvents(ctx context.Context) ([]evmRelayTypes.Or oracleRequest.Commitment.TimeoutTimestamp, ) if err != nil { - l.lggr.Errorw("LatestEvents: failed to pack commitment bytes, skipping", "err", err) + l.eng.Errorw("LatestEvents: failed to pack commitment bytes, skipping", "err", err) } resultsReq = append(resultsReq, evmRelayTypes.OracleRequest{ @@ -266,7 +249,7 @@ func (l *logPollerWrapper) LatestEvents(ctx context.Context) ([]evmRelayTypes.Or gethLog := log.ToGethLog() oracleResponse, err := parsingContract.ParseOracleResponse(gethLog) if err != nil { - l.lggr.Errorw("LatestEvents: failed to parse a response log, skipping") + l.eng.Errorw("LatestEvents: failed to parse a response log, skipping") continue } resultsResp = append(resultsResp, evmRelayTypes.OracleResponse{ @@ -275,13 +258,13 @@ func (l *logPollerWrapper) LatestEvents(ctx context.Context) ([]evmRelayTypes.Or } } - l.lggr.Debugw("LatestEvents: done", "nRequestLogs", len(resultsReq), "nResponseLogs", len(resultsResp), "startBlock", startBlockNum, "endBlock", latestBlockNum) + l.eng.Debugw("LatestEvents: done", "nRequestLogs", len(resultsReq), "nResponseLogs", len(resultsResp), "startBlock", startBlockNum, "endBlock", latestBlockNum) return resultsReq, resultsResp, nil } func (l *logPollerWrapper) filterPreviouslyDetectedEvents(logs []logpoller.Log, detectedEvents *detectedEvents, filterType string) []logpoller.Log { if len(logs) > maxLogsToProcess { - l.lggr.Errorw("filterPreviouslyDetectedEvents: too many logs to process, only processing latest maxLogsToProcess logs", "filterType", filterType, "nLogs", len(logs), "maxLogsToProcess", maxLogsToProcess) + l.eng.Errorw("filterPreviouslyDetectedEvents: too many logs to process, only processing latest maxLogsToProcess logs", "filterType", filterType, "nLogs", len(logs), "maxLogsToProcess", maxLogsToProcess) logs = logs[len(logs)-maxLogsToProcess:] } l.mu.Lock() @@ -290,7 +273,7 @@ func (l *logPollerWrapper) filterPreviouslyDetectedEvents(logs []logpoller.Log, for _, log := range logs { var requestId [32]byte if len(log.Topics) < 2 || len(log.Topics[1]) != 32 { - l.lggr.Errorw("filterPreviouslyDetectedEvents: invalid log, skipping", "filterType", filterType, "log", log) + l.eng.Errorw("filterPreviouslyDetectedEvents: invalid log, skipping", "filterType", filterType, "log", log) continue } copy(requestId[:], log.Topics[1]) // requestId is the second topic (1st topic is the event signature) @@ -310,7 +293,7 @@ func (l *logPollerWrapper) filterPreviouslyDetectedEvents(logs []logpoller.Log, expiredRequests++ } detectedEvents.detectedEventsOrdered = detectedEvents.detectedEventsOrdered[expiredRequests:] - l.lggr.Debugw("filterPreviouslyDetectedEvents: done", "filterType", filterType, "nLogs", len(logs), "nFilteredLogs", len(filteredLogs), "nExpiredRequests", expiredRequests, "previouslyDetectedCacheSize", len(detectedEvents.detectedEventsOrdered)) + l.eng.Debugw("filterPreviouslyDetectedEvents: done", "filterType", filterType, "nLogs", len(logs), "nFilteredLogs", len(filteredLogs), "nExpiredRequests", expiredRequests, "previouslyDetectedCacheSize", len(detectedEvents.detectedEventsOrdered)) return filteredLogs } @@ -319,7 +302,7 @@ func (l *logPollerWrapper) SubscribeToUpdates(ctx context.Context, subscriberNam if l.pluginConfig.ContractVersion == 0 { // in V0, immediately set contract address to Oracle contract and never update again if err := subscriber.UpdateRoutes(ctx, l.routerContract.Address(), l.routerContract.Address()); err != nil { - l.lggr.Errorw("LogPollerWrapper: Failed to update routes", "subscriberName", subscriberName, "err", err) + l.eng.Errorw("LogPollerWrapper: Failed to update routes", "subscriberName", subscriberName, "err", err) } } else if l.pluginConfig.ContractVersion == 1 { l.mu.Lock() @@ -328,37 +311,36 @@ func (l *logPollerWrapper) SubscribeToUpdates(ctx context.Context, subscriberNam } } -func (l *logPollerWrapper) checkForRouteUpdates() { - defer l.closeWait.Done() +func (l *logPollerWrapper) checkForRouteUpdates(ctx context.Context) { freqSec := l.pluginConfig.ContractUpdateCheckFrequencySec if freqSec == 0 { - l.lggr.Errorw("LogPollerWrapper: ContractUpdateCheckFrequencySec is zero - route update checks disabled") + l.eng.Errorw("LogPollerWrapper: ContractUpdateCheckFrequencySec is zero - route update checks disabled") return } - updateOnce := func() { + updateOnce := func(ctx context.Context) { // NOTE: timeout == frequency here, could be changed to a separate config value timeout := time.Duration(l.pluginConfig.ContractUpdateCheckFrequencySec) * time.Second - ctx, cancel := l.stopCh.CtxCancel(context.WithTimeout(context.Background(), timeout)) + ctx, cancel := context.WithTimeout(ctx, timeout) defer cancel() active, proposed, err := l.getCurrentCoordinators(ctx) if err != nil { - l.lggr.Errorw("LogPollerWrapper: error calling getCurrentCoordinators", "err", err) + l.eng.Errorw("LogPollerWrapper: error calling getCurrentCoordinators", "err", err) return } l.handleRouteUpdate(ctx, active, proposed) } - updateOnce() // update once right away + updateOnce(ctx) // update once right away ticker := time.NewTicker(time.Duration(freqSec) * time.Second) defer ticker.Stop() for { select { - case <-l.stopCh: + case <-ctx.Done(): return case <-ticker.C: - updateOnce() + updateOnce(ctx) } } } @@ -394,22 +376,22 @@ func (l *logPollerWrapper) handleRouteUpdate(ctx context.Context, activeCoordina defer l.mu.Unlock() if activeCoordinator == (common.Address{}) { - l.lggr.Error("LogPollerWrapper: cannot update activeCoordinator to zero address") + l.eng.Error("LogPollerWrapper: cannot update activeCoordinator to zero address") return } if activeCoordinator == l.activeCoordinator && proposedCoordinator == l.proposedCoordinator { - l.lggr.Debug("LogPollerWrapper: no changes to routes") + l.eng.Debug("LogPollerWrapper: no changes to routes") return } errActive := l.registerFilters(ctx, activeCoordinator) errProposed := l.registerFilters(ctx, proposedCoordinator) if errActive != nil || errProposed != nil { - l.lggr.Errorw("LogPollerWrapper: Failed to register filters", "errorActive", errActive, "errorProposed", errProposed) + l.eng.Errorw("LogPollerWrapper: Failed to register filters", "errorActive", errActive, "errorProposed", errProposed) return } - l.lggr.Debugw("LogPollerWrapper: new routes", "activeCoordinator", activeCoordinator.Hex(), "proposedCoordinator", proposedCoordinator.Hex()) + l.eng.Debugw("LogPollerWrapper: new routes", "activeCoordinator", activeCoordinator.Hex(), "proposedCoordinator", proposedCoordinator.Hex()) l.activeCoordinator = activeCoordinator l.proposedCoordinator = proposedCoordinator @@ -417,7 +399,7 @@ func (l *logPollerWrapper) handleRouteUpdate(ctx context.Context, activeCoordina for _, subscriber := range l.subscribers { err := subscriber.UpdateRoutes(ctx, activeCoordinator, proposedCoordinator) if err != nil { - l.lggr.Errorw("LogPollerWrapper: Failed to update routes", "err", err) + l.eng.Errorw("LogPollerWrapper: Failed to update routes", "err", err) } } @@ -430,9 +412,9 @@ func (l *logPollerWrapper) handleRouteUpdate(ctx context.Context, activeCoordina continue } if err := l.logPoller.UnregisterFilter(ctx, filter.Name); err != nil { - l.lggr.Errorw("LogPollerWrapper: Failed to unregister filter", "filterName", filter.Name, "err", err) + l.eng.Errorw("LogPollerWrapper: Failed to unregister filter", "filterName", filter.Name, "err", err) } - l.lggr.Debugw("LogPollerWrapper: Successfully unregistered filter", "filterName", filter.Name) + l.eng.Debugw("LogPollerWrapper: Successfully unregistered filter", "filterName", filter.Name) } } diff --git a/core/services/relay/evm/llo_config_provider.go b/core/services/relay/evm/llo_config_provider.go index 6efd0ccada..71b6a39be2 100644 --- a/core/services/relay/evm/llo_config_provider.go +++ b/core/services/relay/evm/llo_config_provider.go @@ -6,8 +6,9 @@ import ( "github.com/ethereum/go-ethereum/common" pkgerrors "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "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" ) diff --git a/core/services/relay/evm/llo_provider.go b/core/services/relay/evm/llo_provider.go index b685565e6e..55b80bd58e 100644 --- a/core/services/relay/evm/llo_provider.go +++ b/core/services/relay/evm/llo_provider.go @@ -6,12 +6,12 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "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" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/llo" ) @@ -35,7 +35,7 @@ func NewLLOProvider( return &lloProvider{ cp, transmitter, - lggr.Named("LLOProvider"), + logger.Named(lggr, "LLOProvider"), channelDefinitionCache, services.MultiStart{}, } @@ -74,10 +74,6 @@ func (p *lloProvider) OffchainConfigDigester() ocrtypes.OffchainConfigDigester { return p.cp.OffchainConfigDigester() } -func (p *lloProvider) OnchainConfigCodec() llo.OnchainConfigCodec { - return &llo.JSONOnchainConfigCodec{} -} - func (p *lloProvider) ContractTransmitter() llotypes.Transmitter { return p.transmitter } diff --git a/core/services/relay/evm/median.go b/core/services/relay/evm/median.go index 2407cff714..60a63994bd 100644 --- a/core/services/relay/evm/median.go +++ b/core/services/relay/evm/median.go @@ -8,16 +8,17 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" + "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" "github.com/smartcontractkit/libocr/offchainreporting2plus/types" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" offchain_aggregator_wrapper "github.com/smartcontractkit/chainlink/v2/core/internal/gethwrappers2/generated/offchainaggregator" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) var _ median.MedianContract = &medianContract{} @@ -31,7 +32,7 @@ type medianContract struct { } func newMedianContract(configTracker types.ContractConfigTracker, contractAddress common.Address, chain legacyevm.Chain, specID int32, ds sqlutil.DataSource, lggr logger.Logger) (*medianContract, error) { - lggr = lggr.Named("MedianContract") + lggr = logger.Named(lggr, "MedianContract") contract, err := offchain_aggregator_wrapper.NewOffchainAggregator(contractAddress, chain.Client()) if err != nil { return nil, errors.Wrap(err, "could not instantiate NewOffchainAggregator") diff --git a/core/services/relay/evm/median_test.go b/core/services/relay/evm/median_test.go index 9c474006aa..a1578737b6 100644 --- a/core/services/relay/evm/median_test.go +++ b/core/services/relay/evm/median_test.go @@ -7,23 +7,23 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/logger" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/logger" evmtypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" ) func TestNewMedianProvider(t *testing.T) { - lggr := logger.TestLogger(t) + lggr := logger.Test(t) chain := mocks.NewChain(t) chainID := testutils.NewRandomEVMChainID() chain.On("ID").Return(chainID) contractID := testutils.NewAddress() - relayer := Relayer{lggr: lggr, chain: chain} + relayer := Relayer{lggr: logger.Sugared(lggr), chain: chain} pargs := commontypes.PluginArgs{} diff --git a/core/services/relay/evm/mercury/config_poller.go b/core/services/relay/evm/mercury/config_poller.go index 2da541a8e4..e93ad339ce 100644 --- a/core/services/relay/evm/mercury/config_poller.go +++ b/core/services/relay/evm/mercury/config_poller.go @@ -11,9 +11,10 @@ import ( "github.com/pkg/errors" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/verifier" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" ) diff --git a/core/services/relay/evm/mercury/persistence_manager.go b/core/services/relay/evm/mercury/persistence_manager.go index d7f3d8eaa0..dfe75e7c3c 100644 --- a/core/services/relay/evm/mercury/persistence_manager.go +++ b/core/services/relay/evm/mercury/persistence_manager.go @@ -7,9 +7,10 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" - "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/pb" ) @@ -39,7 +40,7 @@ type PersistenceManager struct { func NewPersistenceManager(lggr logger.Logger, serverURL string, orm ORM, jobID int32, maxTransmitQueueSize int, flushDeletesFrequency, pruneFrequency time.Duration) *PersistenceManager { return &PersistenceManager{ - lggr: lggr.Named("MercuryPersistenceManager").With("serverURL", serverURL), + lggr: logger.Sugared(lggr).Named("MercuryPersistenceManager").With("serverURL", serverURL), orm: orm, serverURL: serverURL, stopCh: make(services.StopChan), diff --git a/core/services/relay/evm/mercury/queue.go b/core/services/relay/evm/mercury/queue.go index 8b39be72a6..a450d21af6 100644 --- a/core/services/relay/evm/mercury/queue.go +++ b/core/services/relay/evm/mercury/queue.go @@ -13,9 +13,9 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/pb" ) @@ -42,7 +42,7 @@ type transmitQueue struct { services.StateMachine cond sync.Cond - lggr logger.Logger + lggr logger.SugaredLogger asyncDeleter asyncDeleter mu *sync.RWMutex @@ -76,7 +76,7 @@ func NewTransmitQueue(lggr logger.Logger, serverURL, feedID string, maxlen int, return &transmitQueue{ services.StateMachine{}, sync.Cond{L: mu}, - lggr.Named("TransmitQueue"), + logger.Sugared(lggr).Named("TransmitQueue"), asyncDeleter, mu, nil, // pq needs to be initialized by calling tq.Init before use diff --git a/core/services/relay/evm/mercury/transmitter.go b/core/services/relay/evm/mercury/transmitter.go index f1434bf20f..b914e67b45 100644 --- a/core/services/relay/evm/mercury/transmitter.go +++ b/core/services/relay/evm/mercury/transmitter.go @@ -27,10 +27,10 @@ import ( capStreams "github.com/smartcontractkit/chainlink-common/pkg/capabilities/datastreams" "github.com/smartcontractkit/chainlink-common/pkg/capabilities/triggers" commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/types/mercury" - "github.com/smartcontractkit/chainlink/v2/core/logger" mercuryutils "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/pb" @@ -110,7 +110,7 @@ type TransmitterConfig interface { type mercuryTransmitter struct { services.StateMachine - lggr logger.Logger + lggr logger.SugaredLogger cfg TransmitterConfig orm ORM @@ -147,7 +147,7 @@ func getPayloadTypes() abi.Arguments { } type server struct { - lggr logger.Logger + lggr logger.SugaredLogger transmitTimeout time.Duration @@ -285,7 +285,7 @@ func (s *server) runQueueLoop(stopCh services.StopChan, wg *sync.WaitGroup, feed func newServer(lggr logger.Logger, cfg TransmitterConfig, client wsrpc.Client, pm *PersistenceManager, serverURL, feedIDHex string) *server { return &server{ - lggr, + logger.Sugared(lggr), cfg.TransmitTimeout().Duration(), client, pm, @@ -302,16 +302,17 @@ func newServer(lggr logger.Logger, cfg TransmitterConfig, client wsrpc.Client, p } func NewTransmitter(lggr logger.Logger, cfg TransmitterConfig, clients map[string]wsrpc.Client, fromAccount ed25519.PublicKey, jobID int32, feedID [32]byte, orm ORM, codec TransmitterReportDecoder, triggerCapability *triggers.MercuryTriggerService) *mercuryTransmitter { + sugared := logger.Sugared(lggr) feedIDHex := fmt.Sprintf("0x%x", feedID[:]) servers := make(map[string]*server, len(clients)) for serverURL, client := range clients { - cLggr := lggr.Named(serverURL).With("serverURL", serverURL) + cLggr := sugared.Named(serverURL).With("serverURL", serverURL) pm := NewPersistenceManager(cLggr, serverURL, orm, jobID, int(cfg.TransmitQueueMaxSize()), flushDeletesFrequency, pruneFrequency) servers[serverURL] = newServer(cLggr, cfg, client, pm, serverURL, feedIDHex) } return &mercuryTransmitter{ services.StateMachine{}, - lggr.Named("MercuryTransmitter").With("feedID", feedIDHex), + sugared.Named("MercuryTransmitter").With("feedID", feedIDHex), cfg, orm, servers, diff --git a/core/services/relay/evm/mercury/utils/feeds.go b/core/services/relay/evm/mercury/utils/feeds.go index 6f8978bbf0..36d6bc60f5 100644 --- a/core/services/relay/evm/mercury/utils/feeds.go +++ b/core/services/relay/evm/mercury/utils/feeds.go @@ -83,6 +83,7 @@ const ( REPORT_V1 REPORT_V2 REPORT_V3 + REPORT_V4 _ ) @@ -110,3 +111,4 @@ func (f FeedID) Version() FeedVersion { func (f FeedID) IsV1() bool { return f.Version() == REPORT_V1 } func (f FeedID) IsV2() bool { return f.Version() == REPORT_V2 } func (f FeedID) IsV3() bool { return f.Version() == REPORT_V3 } +func (f FeedID) IsV4() bool { return f.Version() == REPORT_V4 } diff --git a/core/services/relay/evm/mercury/v1/reportcodec/report_codec.go b/core/services/relay/evm/mercury/v1/reportcodec/report_codec.go index f4c6af32b8..fb332dcc8f 100644 --- a/core/services/relay/evm/mercury/v1/reportcodec/report_codec.go +++ b/core/services/relay/evm/mercury/v1/reportcodec/report_codec.go @@ -11,8 +11,9 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" v1 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v1" - "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" reporttypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v1/types" ) diff --git a/core/services/relay/evm/mercury/v2/reportcodec/report_codec.go b/core/services/relay/evm/mercury/v2/reportcodec/report_codec.go index 33c5fa9a32..ebbdfac66c 100644 --- a/core/services/relay/evm/mercury/v2/reportcodec/report_codec.go +++ b/core/services/relay/evm/mercury/v2/reportcodec/report_codec.go @@ -9,9 +9,9 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" v2 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v2" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" reporttypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v2/types" ) diff --git a/core/services/relay/evm/mercury/v3/reportcodec/report_codec.go b/core/services/relay/evm/mercury/v3/reportcodec/report_codec.go index 601431838d..1bf750fbf9 100644 --- a/core/services/relay/evm/mercury/v3/reportcodec/report_codec.go +++ b/core/services/relay/evm/mercury/v3/reportcodec/report_codec.go @@ -9,9 +9,9 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" v3 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v3" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" reporttypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v3/types" ) diff --git a/core/services/relay/evm/mercury/v4/data_source.go b/core/services/relay/evm/mercury/v4/data_source.go new file mode 100644 index 0000000000..f9c2c2d5de --- /dev/null +++ b/core/services/relay/evm/mercury/v4/data_source.go @@ -0,0 +1,290 @@ +package v4 + +import ( + "context" + "errors" + "fmt" + "math/big" + "sync" + + pkgerrors "github.com/pkg/errors" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + "github.com/smartcontractkit/chainlink-common/pkg/types/mercury" + v4types "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v4" + v4 "github.com/smartcontractkit/chainlink-data-streams/mercury/v4" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/job" + "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" + "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/types" + mercurytypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/types" + mercuryutils "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v4/reportcodec" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +type Runner interface { + ExecuteRun(ctx context.Context, spec pipeline.Spec, vars pipeline.Vars) (run *pipeline.Run, trrs pipeline.TaskRunResults, err error) +} + +type LatestReportFetcher interface { + LatestPrice(ctx context.Context, feedID [32]byte) (*big.Int, error) + LatestTimestamp(context.Context) (int64, error) +} + +type datasource struct { + pipelineRunner Runner + jb job.Job + spec pipeline.Spec + feedID mercuryutils.FeedID + lggr logger.Logger + saver ocrcommon.Saver + orm types.DataSourceORM + codec reportcodec.ReportCodec + + fetcher LatestReportFetcher + linkFeedID mercuryutils.FeedID + nativeFeedID mercuryutils.FeedID + + mu sync.RWMutex + + chEnhancedTelem chan<- ocrcommon.EnhancedTelemetryMercuryData +} + +var _ v4.DataSource = &datasource{} + +func NewDataSource(orm types.DataSourceORM, pr pipeline.Runner, jb job.Job, spec pipeline.Spec, feedID mercuryutils.FeedID, lggr logger.Logger, s ocrcommon.Saver, enhancedTelemChan chan ocrcommon.EnhancedTelemetryMercuryData, fetcher LatestReportFetcher, linkFeedID, nativeFeedID mercuryutils.FeedID) *datasource { + return &datasource{pr, jb, spec, feedID, lggr, s, orm, reportcodec.ReportCodec{}, fetcher, linkFeedID, nativeFeedID, sync.RWMutex{}, enhancedTelemChan} +} + +func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestamp, fetchMaxFinalizedTimestamp bool) (obs v4types.Observation, pipelineExecutionErr error) { + var wg sync.WaitGroup + ctx, cancel := context.WithCancel(ctx) + + if fetchMaxFinalizedTimestamp { + wg.Add(1) + go func() { + defer wg.Done() + latest, dbErr := ds.orm.LatestReport(ctx, ds.feedID) + if dbErr != nil { + obs.MaxFinalizedTimestamp.Err = dbErr + return + } + if latest != nil { + maxFinalizedBlockNumber, decodeErr := ds.codec.ObservationTimestampFromReport(latest) + obs.MaxFinalizedTimestamp.Val, obs.MaxFinalizedTimestamp.Err = int64(maxFinalizedBlockNumber), decodeErr + return + } + obs.MaxFinalizedTimestamp.Val, obs.MaxFinalizedTimestamp.Err = ds.fetcher.LatestTimestamp(ctx) + }() + } + + var trrs pipeline.TaskRunResults + wg.Add(1) + go func() { + defer wg.Done() + var run *pipeline.Run + run, trrs, pipelineExecutionErr = ds.executeRun(ctx) + if pipelineExecutionErr != nil { + cancel() + pipelineExecutionErr = fmt.Errorf("Observe failed while executing run: %w", pipelineExecutionErr) + return + } + + ds.saver.Save(run) + + var parsed parseOutput + parsed, pipelineExecutionErr = ds.parse(trrs) + if pipelineExecutionErr != nil { + cancel() + // This is not expected under normal circumstances + ds.lggr.Errorw("Observe failed while parsing run results", "err", pipelineExecutionErr) + pipelineExecutionErr = fmt.Errorf("Observe failed while parsing run results: %w", pipelineExecutionErr) + return + } + obs.BenchmarkPrice = parsed.benchmarkPrice + obs.Bid = parsed.bid + obs.Ask = parsed.ask + obs.MarketStatus = parsed.marketStatus + }() + + var isLink, isNative bool + if ds.feedID == ds.linkFeedID { + isLink = true + } else { + wg.Add(1) + go func() { + defer wg.Done() + obs.LinkPrice.Val, obs.LinkPrice.Err = ds.fetcher.LatestPrice(ctx, ds.linkFeedID) + if obs.LinkPrice.Val == nil && obs.LinkPrice.Err == nil { + mercurytypes.PriceFeedMissingCount.WithLabelValues(ds.linkFeedID.String()).Inc() + ds.lggr.Warnw(fmt.Sprintf("Mercury server was missing LINK feed, using sentinel value of %s", v4.MissingPrice), "linkFeedID", ds.linkFeedID) + obs.LinkPrice.Val = v4.MissingPrice + } else if obs.LinkPrice.Err != nil { + mercurytypes.PriceFeedErrorCount.WithLabelValues(ds.linkFeedID.String()).Inc() + ds.lggr.Errorw("Mercury server returned error querying LINK price feed", "err", obs.LinkPrice.Err, "linkFeedID", ds.linkFeedID) + } + }() + } + + if ds.feedID == ds.nativeFeedID { + isNative = true + } else { + wg.Add(1) + go func() { + defer wg.Done() + obs.NativePrice.Val, obs.NativePrice.Err = ds.fetcher.LatestPrice(ctx, ds.nativeFeedID) + if obs.NativePrice.Val == nil && obs.NativePrice.Err == nil { + mercurytypes.PriceFeedMissingCount.WithLabelValues(ds.nativeFeedID.String()).Inc() + ds.lggr.Warnw(fmt.Sprintf("Mercury server was missing native feed, using sentinel value of %s", v4.MissingPrice), "nativeFeedID", ds.nativeFeedID) + obs.NativePrice.Val = v4.MissingPrice + } else if obs.NativePrice.Err != nil { + mercurytypes.PriceFeedErrorCount.WithLabelValues(ds.nativeFeedID.String()).Inc() + ds.lggr.Errorw("Mercury server returned error querying native price feed", "err", obs.NativePrice.Err, "nativeFeedID", ds.nativeFeedID) + } + }() + } + + wg.Wait() + cancel() + + if pipelineExecutionErr != nil { + return + } + + if isLink || isNative { + // run has now completed so it is safe to use benchmark price + if isLink { + // This IS the LINK feed, use our observed price + obs.LinkPrice.Val, obs.LinkPrice.Err = obs.BenchmarkPrice.Val, obs.BenchmarkPrice.Err + } + if isNative { + // This IS the native feed, use our observed price + obs.NativePrice.Val, obs.NativePrice.Err = obs.BenchmarkPrice.Val, obs.BenchmarkPrice.Err + } + } + + ocrcommon.MaybeEnqueueEnhancedTelem(ds.jb, ds.chEnhancedTelem, ocrcommon.EnhancedTelemetryMercuryData{ + V4Observation: &obs, + TaskRunResults: trrs, + RepTimestamp: repts, + FeedVersion: mercuryutils.REPORT_V4, + FetchMaxFinalizedTimestamp: fetchMaxFinalizedTimestamp, + IsLinkFeed: isLink, + IsNativeFeed: isNative, + }) + + return obs, nil +} + +func toBigInt(val interface{}) (*big.Int, error) { + dec, err := utils.ToDecimal(val) + if err != nil { + return nil, err + } + return dec.BigInt(), nil +} + +type parseOutput struct { + benchmarkPrice mercury.ObsResult[*big.Int] + bid mercury.ObsResult[*big.Int] + ask mercury.ObsResult[*big.Int] + marketStatus mercury.ObsResult[uint32] +} + +func (ds *datasource) parse(trrs pipeline.TaskRunResults) (o parseOutput, merr error) { + var finaltrrs []pipeline.TaskRunResult + for _, trr := range trrs { + // only return terminal trrs from executeRun + if trr.IsTerminal() { + finaltrrs = append(finaltrrs, trr) + } + } + + // pipeline.TaskRunResults comes ordered asc by index, this is guaranteed + // by the pipeline executor + if len(finaltrrs) != 4 { + return o, fmt.Errorf("invalid number of results, expected: 4, got: %d", len(finaltrrs)) + } + + merr = errors.Join( + setBenchmarkPrice(&o, finaltrrs[0].Result), + setBid(&o, finaltrrs[1].Result), + setAsk(&o, finaltrrs[2].Result), + setMarketStatus(&o, finaltrrs[3].Result), + ) + + return o, merr +} + +func setBenchmarkPrice(o *parseOutput, res pipeline.Result) error { + if res.Error != nil { + o.benchmarkPrice.Err = res.Error + return res.Error + } + val, err := toBigInt(res.Value) + if err != nil { + return fmt.Errorf("failed to parse BenchmarkPrice: %w", err) + } + o.benchmarkPrice.Val = val + return nil +} + +func setBid(o *parseOutput, res pipeline.Result) error { + if res.Error != nil { + o.bid.Err = res.Error + return res.Error + } + val, err := toBigInt(res.Value) + if err != nil { + return fmt.Errorf("failed to parse Bid: %w", err) + } + o.bid.Val = val + return nil +} + +func setAsk(o *parseOutput, res pipeline.Result) error { + if res.Error != nil { + o.ask.Err = res.Error + return res.Error + } + val, err := toBigInt(res.Value) + if err != nil { + return fmt.Errorf("failed to parse Ask: %w", err) + } + o.ask.Val = val + return nil +} + +func setMarketStatus(o *parseOutput, res pipeline.Result) error { + if res.Error != nil { + o.marketStatus.Err = res.Error + return res.Error + } + val, err := toBigInt(res.Value) + if err != nil { + return fmt.Errorf("failed to parse MarketStatus: %w", err) + } + o.marketStatus.Val = uint32(val.Int64()) + return nil +} + +// The context passed in here has a timeout of (ObservationTimeout + ObservationGracePeriod). +// Upon context cancellation, its expected that we return any usable values within ObservationGracePeriod. +func (ds *datasource) executeRun(ctx context.Context) (*pipeline.Run, pipeline.TaskRunResults, error) { + vars := pipeline.NewVarsFrom(map[string]interface{}{ + "jb": map[string]interface{}{ + "databaseID": ds.jb.ID, + "externalJobID": ds.jb.ExternalJobID, + "name": ds.jb.Name.ValueOrZero(), + }, + }) + + run, trrs, err := ds.pipelineRunner.ExecuteRun(ctx, ds.spec, vars) + if err != nil { + return nil, nil, pkgerrors.Wrapf(err, "error executing run for spec ID %v", ds.spec.ID) + } + + return run, trrs, err +} diff --git a/core/services/relay/evm/mercury/v4/data_source_test.go b/core/services/relay/evm/mercury/v4/data_source_test.go new file mode 100644 index 0000000000..bce9c3c608 --- /dev/null +++ b/core/services/relay/evm/mercury/v4/data_source_test.go @@ -0,0 +1,349 @@ +package v4 + +import ( + "context" + "math/big" + "testing" + + "github.com/pkg/errors" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/stretchr/testify/assert" + + mercurytypes "github.com/smartcontractkit/chainlink-common/pkg/types/mercury" + relaymercuryv4 "github.com/smartcontractkit/chainlink-data-streams/mercury/v4" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" + mercurymocks "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/mocks" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" + reportcodecv4 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v4/reportcodec" +) + +var _ mercurytypes.ServerFetcher = &mockFetcher{} + +type mockFetcher struct { + ts int64 + tsErr error + linkPrice *big.Int + linkPriceErr error + nativePrice *big.Int + nativePriceErr error +} + +var feedId utils.FeedID = [32]byte{1} +var linkFeedId utils.FeedID = [32]byte{2} +var nativeFeedId utils.FeedID = [32]byte{3} + +func (m *mockFetcher) FetchInitialMaxFinalizedBlockNumber(context.Context) (*int64, error) { + return nil, nil +} + +func (m *mockFetcher) LatestPrice(ctx context.Context, fId [32]byte) (*big.Int, error) { + if fId == linkFeedId { + return m.linkPrice, m.linkPriceErr + } else if fId == nativeFeedId { + return m.nativePrice, m.nativePriceErr + } + return nil, nil +} + +func (m *mockFetcher) LatestTimestamp(context.Context) (int64, error) { + return m.ts, m.tsErr +} + +type mockORM struct { + report []byte + err error +} + +func (m *mockORM) LatestReport(ctx context.Context, feedID [32]byte) (report []byte, err error) { + return m.report, m.err +} + +type mockSaver struct { + r *pipeline.Run +} + +func (ms *mockSaver) Save(r *pipeline.Run) { + ms.r = r +} + +func Test_Datasource(t *testing.T) { + orm := &mockORM{} + ds := &datasource{orm: orm, lggr: logger.TestLogger(t)} + ctx := testutils.Context(t) + repts := ocrtypes.ReportTimestamp{} + + fetcher := &mockFetcher{} + ds.fetcher = fetcher + + saver := &mockSaver{} + ds.saver = saver + + goodTrrs := []pipeline.TaskRunResult{ + { + // bp + Result: pipeline.Result{Value: "122.345"}, + Task: &mercurymocks.MockTask{}, + }, + { + // bid + Result: pipeline.Result{Value: "121.993"}, + Task: &mercurymocks.MockTask{}, + }, + { + // ask + Result: pipeline.Result{Value: "123.111"}, + Task: &mercurymocks.MockTask{}, + }, + { + // marketStatus + Result: pipeline.Result{Value: "1"}, + Task: &mercurymocks.MockTask{}, + }, + } + + ds.pipelineRunner = &mercurymocks.MockRunner{ + Trrs: goodTrrs, + } + + spec := pipeline.Spec{} + ds.spec = spec + + t.Run("when fetchMaxFinalizedTimestamp=true", func(t *testing.T) { + t.Run("with latest report in database", func(t *testing.T) { + orm.report = buildSamplev4Report() + orm.err = nil + + obs, err := ds.Observe(ctx, repts, true) + assert.NoError(t, err) + + assert.NoError(t, obs.MaxFinalizedTimestamp.Err) + assert.Equal(t, int64(124), obs.MaxFinalizedTimestamp.Val) + }) + t.Run("if querying latest report fails", func(t *testing.T) { + orm.report = nil + orm.err = errors.New("something exploded") + + obs, err := ds.Observe(ctx, repts, true) + assert.NoError(t, err) + + assert.EqualError(t, obs.MaxFinalizedTimestamp.Err, "something exploded") + assert.Zero(t, obs.MaxFinalizedTimestamp.Val) + }) + t.Run("if codec fails to decode", func(t *testing.T) { + orm.report = []byte{1, 2, 3} + orm.err = nil + + obs, err := ds.Observe(ctx, repts, true) + assert.NoError(t, err) + + assert.EqualError(t, obs.MaxFinalizedTimestamp.Err, "failed to decode report: abi: cannot marshal in to go type: length insufficient 3 require 32") + assert.Zero(t, obs.MaxFinalizedTimestamp.Val) + }) + + orm.report = nil + orm.err = nil + + t.Run("if LatestTimestamp returns error", func(t *testing.T) { + fetcher.tsErr = errors.New("some error") + + obs, err := ds.Observe(ctx, repts, true) + assert.NoError(t, err) + + assert.EqualError(t, obs.MaxFinalizedTimestamp.Err, "some error") + assert.Zero(t, obs.MaxFinalizedTimestamp.Val) + }) + + t.Run("if LatestTimestamp succeeds", func(t *testing.T) { + fetcher.tsErr = nil + fetcher.ts = 123 + + obs, err := ds.Observe(ctx, repts, true) + assert.NoError(t, err) + + assert.Equal(t, int64(123), obs.MaxFinalizedTimestamp.Val) + assert.NoError(t, obs.MaxFinalizedTimestamp.Err) + }) + + t.Run("if LatestTimestamp succeeds but ts=0 (new feed)", func(t *testing.T) { + fetcher.tsErr = nil + fetcher.ts = 0 + + obs, err := ds.Observe(ctx, repts, true) + assert.NoError(t, err) + + assert.NoError(t, obs.MaxFinalizedTimestamp.Err) + assert.Zero(t, obs.MaxFinalizedTimestamp.Val) + }) + + t.Run("when run execution succeeded", func(t *testing.T) { + t.Run("when feedId=linkFeedID=nativeFeedId", func(t *testing.T) { + t.Cleanup(func() { + ds.feedID, ds.linkFeedID, ds.nativeFeedID = feedId, linkFeedId, nativeFeedId + }) + + ds.feedID, ds.linkFeedID, ds.nativeFeedID = feedId, feedId, feedId + + fetcher.ts = 123123 + fetcher.tsErr = nil + + obs, err := ds.Observe(ctx, repts, true) + assert.NoError(t, err) + + assert.Equal(t, big.NewInt(122), obs.BenchmarkPrice.Val) + assert.NoError(t, obs.BenchmarkPrice.Err) + assert.Equal(t, big.NewInt(121), obs.Bid.Val) + assert.NoError(t, obs.Bid.Err) + assert.Equal(t, big.NewInt(123), obs.Ask.Val) + assert.NoError(t, obs.Ask.Err) + assert.Equal(t, int64(123123), obs.MaxFinalizedTimestamp.Val) + assert.NoError(t, obs.MaxFinalizedTimestamp.Err) + assert.Equal(t, big.NewInt(122), obs.LinkPrice.Val) + assert.NoError(t, obs.LinkPrice.Err) + assert.Equal(t, big.NewInt(122), obs.NativePrice.Val) + assert.NoError(t, obs.NativePrice.Err) + assert.Equal(t, uint32(1), obs.MarketStatus.Val) + assert.NoError(t, obs.MarketStatus.Err) + }) + }) + }) + + t.Run("when fetchMaxFinalizedTimestamp=false", func(t *testing.T) { + t.Run("when run execution fails, returns error", func(t *testing.T) { + t.Cleanup(func() { + ds.pipelineRunner = &mercurymocks.MockRunner{ + Trrs: goodTrrs, + Err: nil, + } + }) + + ds.pipelineRunner = &mercurymocks.MockRunner{ + Trrs: goodTrrs, + Err: errors.New("run execution failed"), + } + + _, err := ds.Observe(ctx, repts, false) + assert.EqualError(t, err, "Observe failed while executing run: error executing run for spec ID 0: run execution failed") + }) + + t.Run("when parsing run results fails, return error", func(t *testing.T) { + t.Cleanup(func() { + runner := &mercurymocks.MockRunner{ + Trrs: goodTrrs, + Err: nil, + } + ds.pipelineRunner = runner + }) + + badTrrs := []pipeline.TaskRunResult{ + { + // benchmark price + Result: pipeline.Result{Value: "122.345"}, + Task: &mercurymocks.MockTask{}, + }, + { + // bid + Result: pipeline.Result{Value: "121.993"}, + Task: &mercurymocks.MockTask{}, + }, + { + // ask + Result: pipeline.Result{Error: errors.New("some error with ask")}, + Task: &mercurymocks.MockTask{}, + }, + { + // marketStatus + Result: pipeline.Result{Value: "1"}, + Task: &mercurymocks.MockTask{}, + }, + } + + ds.pipelineRunner = &mercurymocks.MockRunner{ + Trrs: badTrrs, + Err: nil, + } + + _, err := ds.Observe(ctx, repts, false) + assert.EqualError(t, err, "Observe failed while parsing run results: some error with ask") + }) + + t.Run("when run execution succeeded", func(t *testing.T) { + t.Run("when feedId=linkFeedID=nativeFeedId", func(t *testing.T) { + t.Cleanup(func() { + ds.feedID, ds.linkFeedID, ds.nativeFeedID = feedId, linkFeedId, nativeFeedId + }) + + var feedId utils.FeedID = [32]byte{1} + ds.feedID, ds.linkFeedID, ds.nativeFeedID = feedId, feedId, feedId + + obs, err := ds.Observe(ctx, repts, false) + assert.NoError(t, err) + + assert.Equal(t, big.NewInt(122), obs.BenchmarkPrice.Val) + assert.NoError(t, obs.BenchmarkPrice.Err) + assert.Equal(t, big.NewInt(121), obs.Bid.Val) + assert.NoError(t, obs.Bid.Err) + assert.Equal(t, big.NewInt(123), obs.Ask.Val) + assert.NoError(t, obs.Ask.Err) + assert.Equal(t, int64(0), obs.MaxFinalizedTimestamp.Val) + assert.NoError(t, obs.MaxFinalizedTimestamp.Err) + assert.Equal(t, big.NewInt(122), obs.LinkPrice.Val) + assert.NoError(t, obs.LinkPrice.Err) + assert.Equal(t, big.NewInt(122), obs.NativePrice.Val) + assert.NoError(t, obs.NativePrice.Err) + assert.Equal(t, uint32(1), obs.MarketStatus.Val) + assert.NoError(t, obs.MarketStatus.Err) + }) + + t.Run("when fails to fetch linkPrice or nativePrice", func(t *testing.T) { + t.Cleanup(func() { + fetcher.linkPriceErr = nil + fetcher.nativePriceErr = nil + }) + + fetcher.linkPriceErr = errors.New("some error fetching link price") + fetcher.nativePriceErr = errors.New("some error fetching native price") + + obs, err := ds.Observe(ctx, repts, false) + assert.NoError(t, err) + + assert.Nil(t, obs.LinkPrice.Val) + assert.EqualError(t, obs.LinkPrice.Err, "some error fetching link price") + assert.Nil(t, obs.NativePrice.Val) + assert.EqualError(t, obs.NativePrice.Err, "some error fetching native price") + }) + + t.Run("when succeeds to fetch linkPrice or nativePrice but got nil (new feed)", func(t *testing.T) { + obs, err := ds.Observe(ctx, repts, false) + assert.NoError(t, err) + + assert.Equal(t, obs.LinkPrice.Val, relaymercuryv4.MissingPrice) + assert.Nil(t, obs.LinkPrice.Err) + assert.Equal(t, obs.NativePrice.Val, relaymercuryv4.MissingPrice) + assert.Nil(t, obs.NativePrice.Err) + }) + }) + }) +} + +var sampleFeedID = [32]uint8{28, 145, 107, 74, 167, 229, 124, 167, 182, 138, 225, 191, 69, 101, 63, 86, 182, 86, 253, 58, 163, 53, 239, 127, 174, 105, 107, 102, 63, 27, 132, 114} + +func buildSamplev4Report() []byte { + feedID := sampleFeedID + timestamp := uint32(124) + bp := big.NewInt(242) + bid := big.NewInt(243) + ask := big.NewInt(244) + validFromTimestamp := uint32(123) + expiresAt := uint32(456) + linkFee := big.NewInt(3334455) + nativeFee := big.NewInt(556677) + marketStatus := uint32(1) + + b, err := reportcodecv4.ReportTypes.Pack(feedID, validFromTimestamp, timestamp, nativeFee, linkFee, expiresAt, bp, bid, ask, marketStatus) + if err != nil { + panic(err) + } + return b +} diff --git a/core/services/relay/evm/mercury/v4/reportcodec/report_codec.go b/core/services/relay/evm/mercury/v4/reportcodec/report_codec.go new file mode 100644 index 0000000000..9c4cb0e509 --- /dev/null +++ b/core/services/relay/evm/mercury/v4/reportcodec/report_codec.go @@ -0,0 +1,83 @@ +package reportcodec + +import ( + "errors" + "fmt" + "math/big" + + pkgerrors "github.com/pkg/errors" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + v4 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v4" + + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" + reporttypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v4/types" +) + +var ReportTypes = reporttypes.GetSchema() +var maxReportLength = 32 * len(ReportTypes) // each arg is 256 bit EVM word +var zero = big.NewInt(0) + +var _ v4.ReportCodec = &ReportCodec{} + +type ReportCodec struct { + logger logger.Logger + feedID utils.FeedID +} + +func NewReportCodec(feedID [32]byte, lggr logger.Logger) *ReportCodec { + return &ReportCodec{lggr, feedID} +} + +func (r *ReportCodec) BuildReport(rf v4.ReportFields) (ocrtypes.Report, error) { + var merr error + if rf.BenchmarkPrice == nil { + merr = errors.Join(merr, errors.New("benchmarkPrice may not be nil")) + } + if rf.Bid == nil { + merr = errors.Join(merr, errors.New("bid may not be nil")) + } + if rf.Ask == nil { + merr = errors.Join(merr, errors.New("ask may not be nil")) + } + if rf.LinkFee == nil { + merr = errors.Join(merr, errors.New("linkFee may not be nil")) + } else if rf.LinkFee.Cmp(zero) < 0 { + merr = errors.Join(merr, fmt.Errorf("linkFee may not be negative (got: %s)", rf.LinkFee)) + } + if rf.NativeFee == nil { + merr = errors.Join(merr, errors.New("nativeFee may not be nil")) + } else if rf.NativeFee.Cmp(zero) < 0 { + merr = errors.Join(merr, fmt.Errorf("nativeFee may not be negative (got: %s)", rf.NativeFee)) + } + if merr != nil { + return nil, merr + } + reportBytes, err := ReportTypes.Pack(r.feedID, rf.ValidFromTimestamp, rf.Timestamp, rf.NativeFee, rf.LinkFee, rf.ExpiresAt, rf.BenchmarkPrice, rf.Bid, rf.Ask, rf.MarketStatus) + return ocrtypes.Report(reportBytes), pkgerrors.Wrap(err, "failed to pack report blob") +} + +func (r *ReportCodec) MaxReportLength(n int) (int, error) { + return maxReportLength, nil +} + +func (r *ReportCodec) ObservationTimestampFromReport(report ocrtypes.Report) (uint32, error) { + decoded, err := r.Decode(report) + if err != nil { + return 0, err + } + return decoded.ObservationsTimestamp, nil +} + +func (r *ReportCodec) Decode(report ocrtypes.Report) (*reporttypes.Report, error) { + return reporttypes.Decode(report) +} + +func (r *ReportCodec) BenchmarkPriceFromReport(report ocrtypes.Report) (*big.Int, error) { + decoded, err := r.Decode(report) + if err != nil { + return nil, err + } + return decoded.BenchmarkPrice, nil +} diff --git a/core/services/relay/evm/mercury/v4/reportcodec/report_codec_test.go b/core/services/relay/evm/mercury/v4/reportcodec/report_codec_test.go new file mode 100644 index 0000000000..b62f42ef57 --- /dev/null +++ b/core/services/relay/evm/mercury/v4/reportcodec/report_codec_test.go @@ -0,0 +1,163 @@ +package reportcodec + +import ( + "math/big" + "testing" + + "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + v4 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v4" +) + +func newValidReportFields() v4.ReportFields { + return v4.ReportFields{ + Timestamp: 242, + BenchmarkPrice: big.NewInt(243), + Bid: big.NewInt(244), + Ask: big.NewInt(245), + ValidFromTimestamp: 123, + ExpiresAt: 20, + LinkFee: big.NewInt(456), + NativeFee: big.NewInt(457), + MarketStatus: 1, + } +} + +func Test_ReportCodec_BuildReport(t *testing.T) { + r := ReportCodec{} + + t.Run("BuildReport errors on zero values", func(t *testing.T) { + _, err := r.BuildReport(v4.ReportFields{}) + require.Error(t, err) + assert.Contains(t, err.Error(), "benchmarkPrice may not be nil") + assert.Contains(t, err.Error(), "linkFee may not be nil") + assert.Contains(t, err.Error(), "nativeFee may not be nil") + }) + + t.Run("BuildReport constructs a report from observations", func(t *testing.T) { + rf := newValidReportFields() + // only need to test happy path since validations are done in relaymercury + + report, err := r.BuildReport(rf) + require.NoError(t, err) + + reportElems := make(map[string]interface{}) + err = ReportTypes.UnpackIntoMap(reportElems, report) + require.NoError(t, err) + + assert.Equal(t, int(reportElems["observationsTimestamp"].(uint32)), 242) + assert.Equal(t, reportElems["benchmarkPrice"].(*big.Int).Int64(), int64(243)) + assert.Equal(t, reportElems["bid"].(*big.Int).Int64(), int64(244)) + assert.Equal(t, reportElems["ask"].(*big.Int).Int64(), int64(245)) + assert.Equal(t, reportElems["validFromTimestamp"].(uint32), uint32(123)) + assert.Equal(t, reportElems["expiresAt"].(uint32), uint32(20)) + assert.Equal(t, reportElems["linkFee"].(*big.Int).Int64(), int64(456)) + assert.Equal(t, reportElems["nativeFee"].(*big.Int).Int64(), int64(457)) + assert.Equal(t, reportElems["marketStatus"].(uint32), uint32(1)) + + assert.Equal(t, types.Report{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, 0x0, 0x0, 0x0, 0x7b, 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, 0xf2, 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, 0xc9, 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, 0xc8, 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, 0x14, 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, 0xf3, 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, 0xf4, 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, 0xf5, 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}, report) + max, err := r.MaxReportLength(4) + require.NoError(t, err) + assert.LessOrEqual(t, len(report), max) + + t.Run("Decode decodes the report", func(t *testing.T) { + decoded, err := r.Decode(report) + require.NoError(t, err) + + require.NotNil(t, decoded) + + assert.Equal(t, uint32(242), decoded.ObservationsTimestamp) + assert.Equal(t, big.NewInt(243), decoded.BenchmarkPrice) + assert.Equal(t, big.NewInt(244), decoded.Bid) + assert.Equal(t, big.NewInt(245), decoded.Ask) + assert.Equal(t, uint32(123), decoded.ValidFromTimestamp) + assert.Equal(t, uint32(20), decoded.ExpiresAt) + assert.Equal(t, big.NewInt(456), decoded.LinkFee) + assert.Equal(t, big.NewInt(457), decoded.NativeFee) + assert.Equal(t, uint32(1), decoded.MarketStatus) + }) + }) + + t.Run("errors on negative fee", func(t *testing.T) { + rf := newValidReportFields() + rf.LinkFee = big.NewInt(-1) + rf.NativeFee = big.NewInt(-1) + _, err := r.BuildReport(rf) + require.Error(t, err) + + assert.Contains(t, err.Error(), "linkFee may not be negative (got: -1)") + assert.Contains(t, err.Error(), "nativeFee may not be negative (got: -1)") + }) + + t.Run("Decode errors on invalid report", func(t *testing.T) { + _, err := r.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 = r.Decode(longBad) + assert.EqualError(t, err, "failed to decode report: abi: improperly encoded uint32 value") + }) +} + +func buildSampleReport(ts int64) []byte { + feedID := [32]byte{'f', 'o', 'o'} + timestamp := uint32(ts) + bp := big.NewInt(242) + bid := big.NewInt(243) + ask := big.NewInt(244) + validFromTimestamp := uint32(123) + expiresAt := uint32(456) + linkFee := big.NewInt(3334455) + nativeFee := big.NewInt(556677) + marketStatus := uint32(1) + + b, err := ReportTypes.Pack(feedID, validFromTimestamp, timestamp, nativeFee, linkFee, expiresAt, bp, bid, ask, marketStatus) + if err != nil { + panic(err) + } + return b +} + +func Test_ReportCodec_ObservationTimestampFromReport(t *testing.T) { + r := ReportCodec{} + + t.Run("ObservationTimestampFromReport extracts observation timestamp from a valid report", func(t *testing.T) { + report := buildSampleReport(123) + + ts, err := r.ObservationTimestampFromReport(report) + require.NoError(t, err) + + assert.Equal(t, ts, uint32(123)) + }) + t.Run("ObservationTimestampFromReport returns error when report is invalid", func(t *testing.T) { + report := []byte{1, 2, 3} + + _, err := r.ObservationTimestampFromReport(report) + require.Error(t, err) + + assert.EqualError(t, err, "failed to decode report: abi: cannot marshal in to go type: length insufficient 3 require 32") + }) +} + +func Test_ReportCodec_BenchmarkPriceFromReport(t *testing.T) { + r := ReportCodec{} + + t.Run("BenchmarkPriceFromReport extracts the benchmark price from valid report", func(t *testing.T) { + report := buildSampleReport(123) + + bp, err := r.BenchmarkPriceFromReport(report) + require.NoError(t, err) + + assert.Equal(t, big.NewInt(242), bp) + }) + t.Run("BenchmarkPriceFromReport errors on invalid report", func(t *testing.T) { + _, err := r.BenchmarkPriceFromReport([]byte{1, 2, 3}) + require.Error(t, err) + assert.EqualError(t, err, "failed to decode report: abi: cannot marshal in to go type: length insufficient 3 require 32") + }) +} diff --git a/core/services/relay/evm/mercury/v4/types/types.go b/core/services/relay/evm/mercury/v4/types/types.go new file mode 100644 index 0000000000..3abdd262a6 --- /dev/null +++ b/core/services/relay/evm/mercury/v4/types/types.go @@ -0,0 +1,58 @@ +package reporttypes + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi" +) + +var 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: "feedId", Type: mustNewType("bytes32")}, + {Name: "validFromTimestamp", Type: mustNewType("uint32")}, + {Name: "observationsTimestamp", Type: mustNewType("uint32")}, + {Name: "nativeFee", Type: mustNewType("uint192")}, + {Name: "linkFee", Type: mustNewType("uint192")}, + {Name: "expiresAt", Type: mustNewType("uint32")}, + {Name: "benchmarkPrice", Type: mustNewType("int192")}, + {Name: "bid", Type: mustNewType("int192")}, + {Name: "ask", Type: mustNewType("int192")}, + {Name: "marketStatus", Type: mustNewType("uint32")}, + }) +} + +type Report struct { + FeedId [32]byte + ObservationsTimestamp uint32 + BenchmarkPrice *big.Int + Bid *big.Int + Ask *big.Int + ValidFromTimestamp uint32 + ExpiresAt uint32 + LinkFee *big.Int + NativeFee *big.Int + MarketStatus uint32 +} + +// Decode is made available to external users (i.e. mercury server) +func Decode(report []byte) (*Report, error) { + values, err := schema.Unpack(report) + if err != nil { + return nil, fmt.Errorf("failed to decode report: %w", err) + } + decoded := new(Report) + if err = schema.Copy(decoded, values); err != nil { + return nil, fmt.Errorf("failed to copy report values to struct: %w", err) + } + return decoded, nil +} diff --git a/core/services/relay/evm/mercury/verifier/verifier.go b/core/services/relay/evm/mercury/verifier/verifier.go new file mode 100644 index 0000000000..02bb17d387 --- /dev/null +++ b/core/services/relay/evm/mercury/verifier/verifier.go @@ -0,0 +1,111 @@ +package verifier + +import ( + "errors" + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/smartcontractkit/libocr/offchainreporting2plus/types" +) + +var ( + ErrVerificationFailed = errors.New("verification failed") + + ErrFailedUnmarshalPubkey = fmt.Errorf("%w: failed to unmarshal pubkey", ErrVerificationFailed) + ErrVerifyInvalidSignatureCount = fmt.Errorf("%w: invalid signature count", ErrVerificationFailed) + ErrVerifyMismatchedSignatureCount = fmt.Errorf("%w: mismatched signature count", ErrVerificationFailed) + ErrVerifyInvalidSignature = fmt.Errorf("%w: invalid signature", ErrVerificationFailed) + ErrVerifySomeSignerUnauthorized = fmt.Errorf("%w: node unauthorized", ErrVerificationFailed) + ErrVerifyNonUniqueSignature = fmt.Errorf("%w: signer has already signed", ErrVerificationFailed) +) + +type SignedReport struct { + RawRs [][32]byte + RawSs [][32]byte + RawVs [32]byte + ReportContext [3][32]byte + Report []byte +} + +type Verifier interface { + // Verify checks the report against its configuration, and then verifies signatures. + // It replicates the Verifier contract's "verify" function for server side + // report verification. + // See also: contracts/src/v0.8/llo-feeds/Verifier.sol + Verify(report SignedReport, f uint8, authorizedSigners []common.Address) (signers []common.Address, err error) +} + +var _ Verifier = (*verifier)(nil) + +type verifier struct{} + +func NewVerifier() Verifier { + return &verifier{} +} + +func (v *verifier) Verify(sr SignedReport, f uint8, authorizedSigners []common.Address) (signers []common.Address, err error) { + if len(sr.RawRs) != int(f+1) { + return signers, fmt.Errorf("%w: expected the number of signatures (len(rs)) to equal the number of signatures required (f), but f=%d and len(rs)=%d", ErrVerifyInvalidSignatureCount, f+1, len(sr.RawRs)) + } + if len(sr.RawRs) != len(sr.RawSs) { + return signers, fmt.Errorf("%w: got %d rs and %d ss, expected equal", ErrVerifyMismatchedSignatureCount, len(sr.RawRs), len(sr.RawSs)) + } + + sigData := ReportToSigData(sr.ReportContext, sr.Report) + + signerMap := make(map[common.Address]bool) + for _, signer := range authorizedSigners { + signerMap[signer] = false + } + + // Loop over every signature and collect errors. This wastes some CPU cycles, but we need to know everyone who + // signed the report. Some risk mitigated by checking that the number of signatures matches the expected (F) earlier + var verifyErrors error + reportSigners := make([]common.Address, len(sr.RawRs)) // For logging + metrics, string for convenience + for i := 0; i < len(sr.RawRs); i++ { + sig := append(sr.RawRs[i][:], sr.RawSs[i][:]...) + sig = append(sig, sr.RawVs[i]) // In the contract, you'll see vs+27. We don't do that here since geth adds +27 internally + + sigPubKey, err := crypto.Ecrecover(sigData, sig) + if err != nil { + verifyErrors = errors.Join(verifyErrors, fmt.Errorf("failed to recover signature: %w", err)) + continue + } + + verified := crypto.VerifySignature(sigPubKey, sigData, sig[:64]) + if !verified { + verifyErrors = errors.Join(verifyErrors, ErrVerifyInvalidSignature, fmt.Errorf("signature verification failed for pubKey: %x, sig: %x", sigPubKey, sig)) + continue + } + + unmarshalledPub, err := crypto.UnmarshalPubkey(sigPubKey) + if err != nil { + verifyErrors = errors.Join(verifyErrors, ErrFailedUnmarshalPubkey, fmt.Errorf("public key=%x error=%w", sigPubKey, err)) + continue + } + + address := crypto.PubkeyToAddress(*unmarshalledPub) + reportSigners[i] = address + encountered, authorized := signerMap[address] + if !authorized { + verifyErrors = errors.Join(verifyErrors, ErrVerifySomeSignerUnauthorized, fmt.Errorf("signer %s not in list of authorized nodes", address.String())) + continue + } + if encountered { + verifyErrors = errors.Join(verifyErrors, ErrVerifyNonUniqueSignature, fmt.Errorf("signer %s has already signed this report", address.String())) + continue + } + signerMap[address] = true + signers = append(signers, address) + } + return signers, verifyErrors +} + +func ReportToSigData(reportCtx [3][32]byte, sr types.Report) []byte { + sigData := crypto.Keccak256(sr) + sigData = append(sigData, reportCtx[0][:]...) + sigData = append(sigData, reportCtx[1][:]...) + sigData = append(sigData, reportCtx[2][:]...) + return crypto.Keccak256(sigData) +} diff --git a/core/services/relay/evm/mercury/verifier/verifier_test.go b/core/services/relay/evm/mercury/verifier/verifier_test.go new file mode 100644 index 0000000000..4cc9dcc9bf --- /dev/null +++ b/core/services/relay/evm/mercury/verifier/verifier_test.go @@ -0,0 +1,80 @@ +package verifier + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/test-go/testify/assert" + "github.com/test-go/testify/require" + + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury" +) + +func Test_Verifier(t *testing.T) { + t.Parallel() + + signedReportBinary := hexutil.MustDecode(`0x0006e1dde86b8a12add45546a14ea7e5efd10b67a373c6f4c41ecfa17d0005350000000000000000000000000000000000000000000000000000000000000201000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002800001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000034c9214519c942ad0aa84a3dd31870e6efe8b3fcab4e176c5226879b26c77000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000669150aa0000000000000000000000000000000000001504e1e6c380271bb8b129ac8f7c0000000000000000000000000000000000001504e1e6c380271bb8b129ac8f7c00000000000000000000000000000000000000000000000000000000669150ab0000000000000000000000000000000000000000000000000000002482116240000000000000000000000000000000000000000000000000000000247625a04000000000000000000000000000000000000000000000000000000024880743400000000000000000000000000000000000000000000000000000000000000002710ac21df88ab70c8822b68be53d7bed65c82ffc9204c1d7ccf3c6c4048b3ca2cafb26e7bbd8f13fe626c946baa5ffcb444319c4229b945ea65d0c99c21978a100000000000000000000000000000000000000000000000000000000000000022c07843f17aa3ecd55f52e99e889906f825f49e4ddfa9c74ca487dd4ff101cc636108a5323be838e658dffa1be67bd91e99f68c4bf86936b76c5d8193b707597`) + m := make(map[string]interface{}) + err := mercury.PayloadTypes.UnpackIntoMap(m, signedReportBinary) + require.NoError(t, err) + + signedReport := SignedReport{ + RawRs: m["rawRs"].([][32]byte), + RawSs: m["rawSs"].([][32]byte), + RawVs: m["rawVs"].([32]byte), + ReportContext: m["reportContext"].([3][32]byte), + Report: m["report"].([]byte), + } + + f := uint8(1) + + v := NewVerifier() + + t.Run("Verify errors with unauthorized signers", func(t *testing.T) { + _, err := v.Verify(signedReport, f, []common.Address{}) + require.Error(t, err) + assert.EqualError(t, err, "verification failed: node unauthorized\nsigner 0x3fc9FaA15d71EeD614e5322bd9554Fb35cC381d2 not in list of authorized nodes\nverification failed: node unauthorized\nsigner 0xBa6534da0E49c71cD9d0292203F1524876f33E23 not in list of authorized nodes") + }) + + t.Run("Verify succeeds with authorized signers", func(t *testing.T) { + signers, err := v.Verify(signedReport, f, []common.Address{ + common.HexToAddress("0xde25e5b4005f611e356ce203900da4e37d72d58f"), + common.HexToAddress("0x256431d41cf0d944f5877bc6c93846a9829dfc03"), + common.HexToAddress("0x3fc9faa15d71eed614e5322bd9554fb35cc381d2"), + common.HexToAddress("0xba6534da0e49c71cd9d0292203f1524876f33e23"), + }) + require.NoError(t, err) + assert.Equal(t, []common.Address{ + common.HexToAddress("0x3fc9faa15d71eed614e5322bd9554fb35cc381d2"), + common.HexToAddress("0xBa6534da0E49c71cD9d0292203F1524876f33E23"), + }, signers) + }) + + t.Run("Verify fails if report has been tampered with", func(t *testing.T) { + badReport := signedReport + badReport.Report = []byte{0x0011} + _, err := v.Verify(badReport, f, []common.Address{ + common.HexToAddress("0xde25e5b4005f611e356ce203900da4e37d72d58f"), + common.HexToAddress("0x256431d41cf0d944f5877bc6c93846a9829dfc03"), + common.HexToAddress("0x3fc9faa15d71eed614e5322bd9554fb35cc381d2"), + common.HexToAddress("0xba6534da0e49c71cd9d0292203f1524876f33e23"), + }) + + require.Error(t, err) + }) + + t.Run("Verify fails if rawVs has been changed", func(t *testing.T) { + badReport := signedReport + badReport.RawVs = [32]byte{0x0011} + _, err := v.Verify(badReport, f, []common.Address{ + common.HexToAddress("0xde25e5b4005f611e356ce203900da4e37d72d58f"), + common.HexToAddress("0x256431d41cf0d944f5877bc6c93846a9829dfc03"), + common.HexToAddress("0x3fc9faa15d71eed614e5322bd9554fb35cc381d2"), + common.HexToAddress("0xba6534da0e49c71cd9d0292203f1524876f33e23"), + }) + + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to recover signature: invalid signature recovery id") + }) +} 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 ab74d426bb..d7d3212764 100644 --- a/core/services/relay/evm/mercury/wsrpc/pb/mercury.pb.go +++ b/core/services/relay/evm/mercury/wsrpc/pb/mercury.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.34.2 -// protoc v4.25.1 +// protoc v5.28.2 // source: mercury.proto package pb diff --git a/core/services/relay/evm/mercury/wsrpc/pb/mercury_wsrpc.pb.go b/core/services/relay/evm/mercury/wsrpc/pb/mercury_wsrpc.pb.go index 4d05db4380..e82d23469c 100644 --- a/core/services/relay/evm/mercury/wsrpc/pb/mercury_wsrpc.pb.go +++ b/core/services/relay/evm/mercury/wsrpc/pb/mercury_wsrpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-wsrpc. DO NOT EDIT. // versions: // - protoc-gen-go-wsrpc v0.0.1 -// - protoc v4.25.1 +// - protoc v5.28.2 package pb diff --git a/core/services/relay/evm/mercury_config_provider.go b/core/services/relay/evm/mercury_config_provider.go index bd0749e5ae..349650c0fc 100644 --- a/core/services/relay/evm/mercury_config_provider.go +++ b/core/services/relay/evm/mercury_config_provider.go @@ -7,10 +7,10 @@ import ( "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink-common/pkg/logger" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" ) @@ -31,7 +31,7 @@ func newMercuryConfigProvider(ctx context.Context, lggr logger.Logger, chain leg } cp, err := mercury.NewConfigPoller( ctx, - lggr.Named(relayConfig.FeedID.String()), + logger.Named(lggr, relayConfig.FeedID.String()), chain.LogPoller(), aggregatorAddress, *relayConfig.FeedID, diff --git a/core/services/relay/evm/mercury_provider.go b/core/services/relay/evm/mercury_provider.go index 48882b701c..807ace6ce1 100644 --- a/core/services/relay/evm/mercury_provider.go +++ b/core/services/relay/evm/mercury_provider.go @@ -6,17 +6,18 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" - mercurytypes "github.com/smartcontractkit/chainlink-common/pkg/types/mercury" v1 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v1" v2 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v2" v3 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v3" + v4 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v4" + "github.com/smartcontractkit/chainlink-data-streams/mercury" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" evmmercury "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury" ) @@ -24,12 +25,12 @@ var _ commontypes.MercuryProvider = (*mercuryProvider)(nil) type mercuryProvider struct { cp commontypes.ConfigProvider - chainReader commontypes.ContractReader codec commontypes.Codec transmitter evmmercury.Transmitter reportCodecV1 v1.ReportCodec reportCodecV2 v2.ReportCodec reportCodecV3 v3.ReportCodec + reportCodecV4 v4.ReportCodec mercuryChainReader mercurytypes.ChainReader logger logger.Logger ms services.MultiStart @@ -37,23 +38,23 @@ type mercuryProvider struct { func NewMercuryProvider( cp commontypes.ConfigProvider, - chainReader commontypes.ContractReader, codec commontypes.Codec, mercuryChainReader mercurytypes.ChainReader, transmitter evmmercury.Transmitter, reportCodecV1 v1.ReportCodec, reportCodecV2 v2.ReportCodec, reportCodecV3 v3.ReportCodec, + reportCodecV4 v4.ReportCodec, lggr logger.Logger, ) *mercuryProvider { return &mercuryProvider{ cp, - chainReader, codec, transmitter, reportCodecV1, reportCodecV2, reportCodecV3, + reportCodecV4, mercuryChainReader, lggr, services.MultiStart{}, @@ -115,6 +116,10 @@ func (p *mercuryProvider) ReportCodecV3() v3.ReportCodec { return p.reportCodecV3 } +func (p *mercuryProvider) ReportCodecV4() v4.ReportCodec { + return p.reportCodecV4 +} + func (p *mercuryProvider) ContractTransmitter() ocrtypes.ContractTransmitter { return p.transmitter } @@ -124,7 +129,7 @@ func (p *mercuryProvider) MercuryServerFetcher() mercurytypes.ServerFetcher { } func (p *mercuryProvider) ChainReader() commontypes.ContractReader { - return p.chainReader + return nil } var _ mercurytypes.ChainReader = (*mercuryChainReader)(nil) diff --git a/core/services/relay/evm/method_binding.go b/core/services/relay/evm/method_binding.go index 448f1b9fbf..9ff57fd934 100644 --- a/core/services/relay/evm/method_binding.go +++ b/core/services/relay/evm/method_binding.go @@ -8,14 +8,14 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink-common/pkg/logger" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/types/query" "github.com/smartcontractkit/chainlink-common/pkg/types/query/primitives" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" - evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ) type NoContractExistsError struct { diff --git a/core/services/relay/evm/ocr2keeper.go b/core/services/relay/evm/ocr2keeper.go index b2d19c1170..2300f54b6e 100644 --- a/core/services/relay/evm/ocr2keeper.go +++ b/core/services/relay/evm/ocr2keeper.go @@ -13,6 +13,7 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/chainlink-automation/pkg/v3/plugin" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/types/automation" @@ -20,7 +21,6 @@ import ( evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" ac "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_automation_v21_plus_common" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" evm "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" @@ -218,7 +218,7 @@ func newOCR2KeeperConfigProvider(ctx context.Context, lggr logger.Logger, chain configPoller, err := NewConfigPoller( ctx, - lggr.With("contractID", rargs.ContractID), + logger.With(lggr, "contractID", rargs.ContractID), CPConfig{ chain.Client(), chain.LogPoller(), diff --git a/core/services/relay/evm/plugin_provider.go b/core/services/relay/evm/plugin_provider.go index ffcea48db2..780b134074 100644 --- a/core/services/relay/evm/plugin_provider.go +++ b/core/services/relay/evm/plugin_provider.go @@ -5,17 +5,16 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/types" - - "github.com/smartcontractkit/chainlink/v2/core/logger" ) type pluginProvider struct { services.Service - chainReader types.ContractReader + chainReader ChainReaderService codec types.Codec - contractTransmitter ocrtypes.ContractTransmitter + contractTransmitter ContractTransmitter configWatcher *configWatcher lggr logger.Logger ms services.MultiStart @@ -24,9 +23,9 @@ type pluginProvider struct { var _ types.PluginProvider = (*pluginProvider)(nil) func NewPluginProvider( - chainReader types.ContractReader, + chainReader ChainReaderService, codec types.Codec, - contractTransmitter ocrtypes.ContractTransmitter, + contractTransmitter ContractTransmitter, configWatcher *configWatcher, lggr logger.Logger, ) *pluginProvider { @@ -47,6 +46,10 @@ func (p *pluginProvider) Ready() error { return nil } func (p *pluginProvider) HealthReport() map[string]error { hp := map[string]error{p.Name(): p.Ready()} services.CopyHealth(hp, p.configWatcher.HealthReport()) + services.CopyHealth(hp, p.contractTransmitter.HealthReport()) + if p.chainReader != nil { + services.CopyHealth(hp, p.chainReader.HealthReport()) + } return hp } @@ -71,9 +74,14 @@ func (p *pluginProvider) Codec() types.Codec { } func (p *pluginProvider) Start(ctx context.Context) error { - return p.configWatcher.Start(ctx) + srvcs := []services.StartClose{p.configWatcher, p.contractTransmitter} + if p.chainReader != nil { + srvcs = append(srvcs, p.chainReader) + } + + return p.ms.Start(ctx, srvcs...) } func (p *pluginProvider) Close() error { - return p.configWatcher.Close() + return p.ms.Close() } diff --git a/core/services/relay/evm/request_round_db.go b/core/services/relay/evm/request_round_db.go index 1aa3dfd747..07a2d1cbc3 100644 --- a/core/services/relay/evm/request_round_db.go +++ b/core/services/relay/evm/request_round_db.go @@ -8,8 +8,8 @@ import ( "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) // RequestRoundDB stores requested rounds for querying by the median plugin. diff --git a/core/services/relay/evm/request_round_tracker.go b/core/services/relay/evm/request_round_tracker.go index 7cf1377569..b9200fff75 100644 --- a/core/services/relay/evm/request_round_tracker.go +++ b/core/services/relay/evm/request_round_tracker.go @@ -12,13 +12,13 @@ import ( "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" offchain_aggregator_wrapper "github.com/smartcontractkit/chainlink/v2/core/internal/gethwrappers2/generated/offchainaggregator" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" ) diff --git a/core/services/relay/evm/standard_config_provider.go b/core/services/relay/evm/standard_config_provider.go index 59f91c52f4..91ca25413f 100644 --- a/core/services/relay/evm/standard_config_provider.go +++ b/core/services/relay/evm/standard_config_provider.go @@ -10,8 +10,9 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2plus/chains/evmutil" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" ) diff --git a/core/services/relay/evm/types/codec_entry_test.go b/core/services/relay/evm/types/codec_entry_test.go index 06b08fcecf..64e0998716 100644 --- a/core/services/relay/evm/types/codec_entry_test.go +++ b/core/services/relay/evm/types/codec_entry_test.go @@ -273,17 +273,27 @@ func TestCodecEntry(t *testing.T) { assertHaveSameStructureAndNames(t, iNative.Type(), entry.CheckedType()) }) - t.Run("Indexed non basic types change to hash", func(t *testing.T) { - anyType, err := abi.NewType("string", "", []abi.ArgumentMarshaling{}) + t.Run("Indexed string and bytes array change to hash", func(t *testing.T) { + stringType, err := abi.NewType("string", "", []abi.ArgumentMarshaling{}) require.NoError(t, err) - entry := NewCodecEntry(abi.Arguments{{Name: "Name", Type: anyType, Indexed: true}}, nil, nil) - require.NoError(t, entry.Init()) - nativeField, ok := entry.CheckedType().FieldByName("Name") - require.True(t, ok) - assert.Equal(t, reflect.TypeOf(&common.Hash{}), nativeField.Type) - native, err := entry.ToNative(reflect.New(entry.CheckedType())) + arrayType, err := abi.NewType("uint8[32]", "", []abi.ArgumentMarshaling{}) require.NoError(t, err) - assertHaveSameStructureAndNames(t, native.Type().Elem(), entry.CheckedType()) + + abiArgs := abi.Arguments{ + {Name: "String", Type: stringType, Indexed: true}, + {Name: "Array", Type: arrayType, Indexed: true}, + } + + for i := 0; i < len(abiArgs); i++ { + entry := NewCodecEntry(abi.Arguments{abiArgs[i]}, nil, nil) + require.NoError(t, entry.Init()) + nativeField, ok := entry.CheckedType().FieldByName(abiArgs[i].Name) + require.True(t, ok) + assert.Equal(t, reflect.TypeOf(&common.Hash{}), nativeField.Type) + native, err := entry.ToNative(reflect.New(entry.CheckedType())) + require.NoError(t, err) + assertHaveSameStructureAndNames(t, native.Type().Elem(), entry.CheckedType()) + } }) t.Run("Too many indexed items returns an error", func(t *testing.T) { diff --git a/core/services/relay/evm/write_target.go b/core/services/relay/evm/write_target.go index fb1c694a2e..69ee23ee85 100644 --- a/core/services/relay/evm/write_target.go +++ b/core/services/relay/evm/write_target.go @@ -7,14 +7,15 @@ import ( chainselectors "github.com/smartcontractkit/chain-selectors" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/v2/core/capabilities/targets" "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" relayevmtypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" ) -func NewWriteTarget(ctx context.Context, relayer *Relayer, chain legacyevm.Chain, lggr logger.Logger) (*targets.WriteTarget, error) { +func NewWriteTarget(ctx context.Context, relayer *Relayer, chain legacyevm.Chain, defaultGasLimit uint64, lggr logger.Logger) (*targets.WriteTarget, error) { // generate ID based on chain selector id := fmt.Sprintf("write_%v@1.0.0", chain.ID()) chainName, err := chainselectors.NameFromChainId(chain.ID().Uint64()) @@ -31,8 +32,8 @@ func NewWriteTarget(ctx context.Context, relayer *Relayer, chain legacyevm.Chain "forwarder": { ContractABI: forwarder.KeystoneForwarderABI, Configs: map[string]*relayevmtypes.ChainReaderDefinition{ - "getTransmitter": { - ChainSpecificName: "getTransmitter", + "getTransmissionInfo": { + ChainSpecificName: "getTransmissionInfo", }, }, }, @@ -55,7 +56,7 @@ func NewWriteTarget(ctx context.Context, relayer *Relayer, chain legacyevm.Chain ChainSpecificName: "report", Checker: "simulate", FromAddress: config.FromAddress().Address(), - GasLimit: 200_000, + GasLimit: defaultGasLimit, }, }, }, @@ -73,5 +74,5 @@ func NewWriteTarget(ctx context.Context, relayer *Relayer, chain legacyevm.Chain return nil, err } - return targets.NewWriteTarget(lggr, id, cr, cw, config.ForwarderAddress().String()), nil + return targets.NewWriteTarget(logger.Named(lggr, "WriteTarget"), id, cr, cw, config.ForwarderAddress().String(), defaultGasLimit), nil } diff --git a/core/services/relay/evm/write_target_test.go b/core/services/relay/evm/write_target_test.go index f3dcae220e..67e45228cf 100644 --- a/core/services/relay/evm/write_target_test.go +++ b/core/services/relay/evm/write_target_test.go @@ -1,7 +1,10 @@ package evm_test import ( + "bytes" + "encoding/hex" "errors" + "fmt" "math/big" "testing" @@ -9,6 +12,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/values" "github.com/smartcontractkit/chainlink/v2/common/headtracker/mocks" + "github.com/smartcontractkit/chainlink/v2/core/capabilities/targets" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" @@ -36,16 +40,72 @@ import ( var forwardABI = types.MustGetABI(forwarder.KeystoneForwarderMetaData.ABI) +func newMockedEncodeTransmissionInfo() ([]byte, error) { + info := targets.TransmissionInfo{ + GasLimit: big.NewInt(0), + InvalidReceiver: false, + State: 0, + Success: false, + TransmissionId: [32]byte{}, + Transmitter: common.HexToAddress("0x0"), + } + + var buffer bytes.Buffer + gasLimitBytes := info.GasLimit.Bytes() + if len(gasLimitBytes) > 80 { + return nil, fmt.Errorf("GasLimit too large") + } + paddedGasLimit := make([]byte, 80-len(gasLimitBytes)) + buffer.Write(paddedGasLimit) + buffer.Write(gasLimitBytes) + + // Encode InvalidReceiver (as uint8) + if info.InvalidReceiver { + buffer.WriteByte(1) + } else { + buffer.WriteByte(0) + } + + // Padding for InvalidReceiver to fit into 32 bytes + padInvalidReceiver := make([]byte, 31) + buffer.Write(padInvalidReceiver) + + // Encode State (as uint8) + buffer.WriteByte(info.State) + + // Padding for State to fit into 32 bytes + padState := make([]byte, 31) + buffer.Write(padState) + + // Encode Success (as uint8) + if info.Success { + buffer.WriteByte(1) + } else { + buffer.WriteByte(0) + } + + // Padding for Success to fit into 32 bytes + padSuccess := make([]byte, 31) + buffer.Write(padSuccess) + + // Encode TransmissionId (as bytes32) + buffer.Write(info.TransmissionId[:]) + + // Encode Transmitter (as address) + buffer.Write(info.Transmitter.Bytes()) + + return buffer.Bytes(), nil +} + func TestEvmWrite(t *testing.T) { chain := evmmocks.NewChain(t) txManager := txmmocks.NewMockEvmTxManager(t) evmClient := evmclimocks.NewClient(t) - // This probably isn't the best way to do this, but couldn't find a simpler way to mock the CallContract response - var mockCall []byte - for i := 0; i < 32; i++ { - mockCall = append(mockCall, byte(0)) - } + // This is a very error-prone way to mock an on-chain response to a GetLatestValue("getTransmissionInfo") call + // It's a bit of a hack, but it's the best way to do it without a lot of refactoring + mockCall, err := newMockedEncodeTransmissionInfo() + require.NoError(t, err) evmClient.On("CallContract", mock.Anything, mock.Anything, mock.Anything).Return(mockCall, nil).Maybe() evmClient.On("CodeAt", mock.Anything, mock.Anything, mock.Anything).Return([]byte("test"), nil) @@ -87,74 +147,66 @@ func TestEvmWrite(t *testing.T) { }) require.NoError(t, err) - txManager.On("CreateTransaction", mock.Anything, mock.Anything).Return(txmgr.Tx{}, nil).Run(func(args mock.Arguments) { - req := args.Get(1).(txmgr.TxRequest) - payload := make(map[string]any) - method := forwardABI.Methods["report"] - err = method.Inputs.UnpackIntoMap(payload, req.EncodedPayload[4:]) - require.NoError(t, err) - require.Equal(t, []byte{0x1, 0x2, 0x3}, payload["rawReport"]) - require.Equal(t, [][]byte{}, payload["signatures"]) - }).Once() + reportMetadata := targets.ReportV1Metadata{ + Version: 1, + WorkflowExecutionID: [32]byte{}, + Timestamp: 0, + DonID: 0, + DonConfigVersion: 0, + WorkflowCID: [32]byte{}, + WorkflowName: [10]byte{}, + WorkflowOwner: [20]byte{}, + ReportID: [2]byte{}, + } - t.Run("succeeds with valid report", func(t *testing.T) { - ctx := testutils.Context(t) - capability, err := evm.NewWriteTarget(ctx, relayer, chain, lggr) - require.NoError(t, err) + reportMetadataBytes, err := reportMetadata.Encode() + require.NoError(t, err) - config, err := values.NewMap(map[string]any{ - "Address": evmCfg.EVM().Workflow().ForwarderAddress().String(), - }) - require.NoError(t, err) + signatures := [][]byte{} - inputs, err := values.NewMap(map[string]any{ - "signed_report": map[string]any{ - "report": []byte{1, 2, 3}, - "signatures": [][]byte{}, - "context": []byte{4, 5}, - "id": []byte{9, 9}, - }, - }) - require.NoError(t, err) - - req := capabilities.CapabilityRequest{ - Metadata: capabilities.RequestMetadata{ - WorkflowID: "test-id", - }, - Config: config, - Inputs: inputs, - } + validInputs, err := values.NewMap(map[string]any{ + "signed_report": map[string]any{ + "report": reportMetadataBytes, + "signatures": signatures, + "context": []byte{4, 5}, + "id": []byte{9, 9}, + }, + }) + require.NoError(t, err) - ch, err := capability.Execute(ctx, req) - require.NoError(t, err) + validMetadata := capabilities.RequestMetadata{ + WorkflowID: hex.EncodeToString(reportMetadata.WorkflowCID[:]), + WorkflowOwner: hex.EncodeToString(reportMetadata.WorkflowOwner[:]), + WorkflowName: hex.EncodeToString(reportMetadata.WorkflowName[:]), + WorkflowExecutionID: hex.EncodeToString(reportMetadata.WorkflowExecutionID[:]), + } - response := <-ch - require.Nil(t, response.Err) + validConfig, err := values.NewMap(map[string]any{ + "Address": evmCfg.EVM().Workflow().ForwarderAddress().String(), }) + require.NoError(t, err) - t.Run("succeeds with empty report", func(t *testing.T) { - ctx := testutils.Context(t) - capability, err := evm.NewWriteTarget(ctx, relayer, chain, logger.TestLogger(t)) + txManager.On("CreateTransaction", mock.Anything, mock.Anything).Return(txmgr.Tx{}, nil).Run(func(args mock.Arguments) { + req := args.Get(1).(txmgr.TxRequest) + payload := make(map[string]any) + method := forwardABI.Methods["report"] + err = method.Inputs.UnpackIntoMap(payload, req.EncodedPayload[4:]) require.NoError(t, err) + require.Equal(t, reportMetadataBytes, payload["rawReport"]) + require.Equal(t, signatures, payload["signatures"]) + }).Once() - config, err := values.NewMap(map[string]any{ - "Address": evmCfg.EVM().Workflow().ForwarderAddress().String(), - }) - require.NoError(t, err) + defaultGasLimit := uint64(400_000) - inputs, err := values.NewMap(map[string]any{ - "signed_report": map[string]any{ - "report": nil, - }, - }) + t.Run("succeeds with valid report", func(t *testing.T) { + ctx := testutils.Context(t) + capability, err := evm.NewWriteTarget(ctx, relayer, chain, defaultGasLimit, lggr) require.NoError(t, err) req := capabilities.CapabilityRequest{ - Metadata: capabilities.RequestMetadata{ - WorkflowID: "test-id", - }, - Config: config, - Inputs: inputs, + Metadata: validMetadata, + Config: validConfig, + Inputs: validInputs, } ch, err := capability.Execute(ctx, req) @@ -166,7 +218,7 @@ func TestEvmWrite(t *testing.T) { t.Run("fails with invalid config", func(t *testing.T) { ctx := testutils.Context(t) - capability, err := evm.NewWriteTarget(ctx, relayer, chain, logger.TestLogger(t)) + capability, err := evm.NewWriteTarget(ctx, relayer, chain, defaultGasLimit, logger.TestLogger(t)) require.NoError(t, err) invalidConfig, err := values.NewMap(map[string]any{ @@ -174,19 +226,10 @@ func TestEvmWrite(t *testing.T) { }) require.NoError(t, err) - inputs, err := values.NewMap(map[string]any{ - "signed_report": map[string]any{ - "report": nil, - }, - }) - require.NoError(t, err) - req := capabilities.CapabilityRequest{ - Metadata: capabilities.RequestMetadata{ - WorkflowID: "test-id", - }, - Config: invalidConfig, - Inputs: inputs, + Metadata: validMetadata, + Config: invalidConfig, + Inputs: validInputs, } _, err = capability.Execute(ctx, req) @@ -195,30 +238,13 @@ func TestEvmWrite(t *testing.T) { t.Run("fails when TXM CreateTransaction returns error", func(t *testing.T) { ctx := testutils.Context(t) - capability, err := evm.NewWriteTarget(ctx, relayer, chain, logger.TestLogger(t)) - require.NoError(t, err) - - config, err := values.NewMap(map[string]any{ - "Address": evmCfg.EVM().Workflow().ForwarderAddress().String(), - }) - require.NoError(t, err) - - inputs, err := values.NewMap(map[string]any{ - "signed_report": map[string]any{ - "report": []byte{1, 2, 3}, - "signatures": [][]byte{}, - "context": []byte{4, 5}, - "id": []byte{9, 9}, - }, - }) + capability, err := evm.NewWriteTarget(ctx, relayer, chain, defaultGasLimit, logger.TestLogger(t)) require.NoError(t, err) req := capabilities.CapabilityRequest{ - Metadata: capabilities.RequestMetadata{ - WorkflowID: "test-id", - }, - Config: config, - Inputs: inputs, + Metadata: validMetadata, + Config: validConfig, + Inputs: validInputs, } txManager.On("CreateTransaction", mock.Anything, mock.Anything).Return(txmgr.Tx{}, errors.New("TXM error")) diff --git a/core/services/streams/stream.go b/core/services/streams/stream.go index 8825cd3b34..b65c6dc12f 100644 --- a/core/services/streams/stream.go +++ b/core/services/streams/stream.go @@ -3,12 +3,10 @@ package streams import ( "context" "fmt" - "math/big" "sync" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type Runner interface { @@ -94,35 +92,3 @@ func (s *stream) executeRun(ctx context.Context) (*pipeline.Run, pipeline.TaskRu return run, trrs, err } - -// ExtractBigInt returns a result of a pipeline run that returns one single -// decimal result, as a *big.Int. -// This acts as a reference/example method, other methods can be implemented to -// extract any desired type that matches a particular pipeline run output. -// Returns error on parse errors: if results are wrong type -func ExtractBigInt(trrs pipeline.TaskRunResults) (*big.Int, error) { - // pipeline.TaskRunResults comes ordered asc by index, this is guaranteed - // by the pipeline executor - finaltrrs := trrs.Terminals() - - if len(finaltrrs) != 1 { - return nil, fmt.Errorf("invalid number of results, expected: 1, got: %d", len(finaltrrs)) - } - res := finaltrrs[0].Result - if res.Error != nil { - return nil, res.Error - } - val, err := toBigInt(res.Value) - if err != nil { - return nil, fmt.Errorf("failed to parse BenchmarkPrice: %w", err) - } - return val, nil -} - -func toBigInt(val interface{}) (*big.Int, error) { - dec, err := utils.ToDecimal(val) - if err != nil { - return nil, err - } - return dec.BigInt(), nil -} diff --git a/core/services/streams/stream_test.go b/core/services/streams/stream_test.go index 61e5187880..7817413812 100644 --- a/core/services/streams/stream_test.go +++ b/core/services/streams/stream_test.go @@ -2,7 +2,6 @@ package streams import ( "context" - "math/big" "testing" "time" @@ -99,35 +98,3 @@ succeed; }) }) } - -func Test_ExtractBigInt(t *testing.T) { - t.Run("wrong number of inputs", func(t *testing.T) { - trrs := []pipeline.TaskRunResult{} - - _, err := ExtractBigInt(trrs) - assert.EqualError(t, err, "invalid number of results, expected: 1, got: 0") - }) - t.Run("wrong type", func(t *testing.T) { - trrs := []pipeline.TaskRunResult{ - { - Result: pipeline.Result{Value: []byte{1, 2, 3}}, - Task: &MockTask{}, - }, - } - - _, err := ExtractBigInt(trrs) - assert.EqualError(t, err, "failed to parse BenchmarkPrice: type []uint8 cannot be converted to decimal.Decimal ([1 2 3])") - }) - t.Run("correct inputs", func(t *testing.T) { - trrs := []pipeline.TaskRunResult{ - { - Result: pipeline.Result{Value: "122.345"}, - Task: &MockTask{}, - }, - } - - val, err := ExtractBigInt(trrs) - require.NoError(t, err) - assert.Equal(t, big.NewInt(122), val) - }) -} diff --git a/core/services/synchronization/helpers_test.go b/core/services/synchronization/helpers_test.go index 7bb2dde763..aea9bf77f4 100644 --- a/core/services/synchronization/helpers_test.go +++ b/core/services/synchronization/helpers_test.go @@ -12,15 +12,15 @@ import ( // NewTestTelemetryIngressClient calls NewTelemetryIngressClient and injects telemClient. func NewTestTelemetryIngressClient(t *testing.T, url *url.URL, serverPubKeyHex string, ks keystore.CSA, logging bool, telemClient telemPb.TelemClient) TelemetryService { - tc := NewTelemetryIngressClient(url, serverPubKeyHex, ks, logging, logger.TestLogger(t), 100, "test", "test") + tc := NewTelemetryIngressClient(url, serverPubKeyHex, ks, logging, logger.TestLogger(t), 100) tc.(*telemetryIngressClient).telemClient = telemClient return tc } // NewTestTelemetryIngressBatchClient calls NewTelemetryIngressBatchClient and injects telemClient. func NewTestTelemetryIngressBatchClient(t *testing.T, url *url.URL, serverPubKeyHex string, ks keystore.CSA, logging bool, telemClient telemPb.TelemClient, sendInterval time.Duration, uniconn bool) TelemetryService { - tc := NewTelemetryIngressBatchClient(url, serverPubKeyHex, ks, logging, logger.TestLogger(t), 100, 50, sendInterval, time.Second, uniconn, "test", "test") - tc.(*telemetryIngressBatchClient).close = func() error { return nil } + tc := NewTelemetryIngressBatchClient(url, serverPubKeyHex, ks, logging, logger.TestLogger(t), 100, 50, sendInterval, time.Second, uniconn) + tc.(*telemetryIngressBatchClient).closeFn = func() error { return nil } tc.(*telemetryIngressBatchClient).telemClient = telemClient return tc } diff --git a/core/services/synchronization/telem/telem.pb.go b/core/services/synchronization/telem/telem.pb.go index 4c86bf3453..ec1ac5ee52 100644 --- a/core/services/synchronization/telem/telem.pb.go +++ b/core/services/synchronization/telem/telem.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.34.1 +// protoc-gen-go v1.34.2 // protoc v4.25.1 // source: core/services/synchronization/telem/telem.proto @@ -265,7 +265,7 @@ func file_core_services_synchronization_telem_telem_proto_rawDescGZIP() []byte { } var file_core_services_synchronization_telem_telem_proto_msgTypes = make([]protoimpl.MessageInfo, 3) -var file_core_services_synchronization_telem_telem_proto_goTypes = []interface{}{ +var file_core_services_synchronization_telem_telem_proto_goTypes = []any{ (*TelemRequest)(nil), // 0: telem.TelemRequest (*TelemBatchRequest)(nil), // 1: telem.TelemBatchRequest (*TelemResponse)(nil), // 2: telem.TelemResponse @@ -288,7 +288,7 @@ func file_core_services_synchronization_telem_telem_proto_init() { return } if !protoimpl.UnsafeEnabled { - file_core_services_synchronization_telem_telem_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_core_services_synchronization_telem_telem_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*TelemRequest); i { case 0: return &v.state @@ -300,7 +300,7 @@ func file_core_services_synchronization_telem_telem_proto_init() { return nil } } - file_core_services_synchronization_telem_telem_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + file_core_services_synchronization_telem_telem_proto_msgTypes[1].Exporter = func(v any, i int) any { switch v := v.(*TelemBatchRequest); i { case 0: return &v.state @@ -312,7 +312,7 @@ func file_core_services_synchronization_telem_telem_proto_init() { return nil } } - file_core_services_synchronization_telem_telem_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + file_core_services_synchronization_telem_telem_proto_msgTypes[2].Exporter = func(v any, i int) any { switch v := v.(*TelemResponse); i { case 0: return &v.state diff --git a/core/services/synchronization/telem/telem_automation_custom.pb.go b/core/services/synchronization/telem/telem_automation_custom.pb.go index dd135ed702..118e76d680 100644 --- a/core/services/synchronization/telem/telem_automation_custom.pb.go +++ b/core/services/synchronization/telem/telem_automation_custom.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.34.1 +// protoc-gen-go v1.34.2 // protoc v4.25.1 // source: core/services/synchronization/telem/telem_automation_custom.proto @@ -290,7 +290,7 @@ func file_core_services_synchronization_telem_telem_automation_custom_proto_rawD } var file_core_services_synchronization_telem_telem_automation_custom_proto_msgTypes = make([]protoimpl.MessageInfo, 3) -var file_core_services_synchronization_telem_telem_automation_custom_proto_goTypes = []interface{}{ +var file_core_services_synchronization_telem_telem_automation_custom_proto_goTypes = []any{ (*BlockNumber)(nil), // 0: telem.BlockNumber (*NodeVersion)(nil), // 1: telem.NodeVersion (*AutomationTelemWrapper)(nil), // 2: telem.AutomationTelemWrapper @@ -311,7 +311,7 @@ func file_core_services_synchronization_telem_telem_automation_custom_proto_init return } if !protoimpl.UnsafeEnabled { - file_core_services_synchronization_telem_telem_automation_custom_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_core_services_synchronization_telem_telem_automation_custom_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*BlockNumber); i { case 0: return &v.state @@ -323,7 +323,7 @@ func file_core_services_synchronization_telem_telem_automation_custom_proto_init return nil } } - file_core_services_synchronization_telem_telem_automation_custom_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + file_core_services_synchronization_telem_telem_automation_custom_proto_msgTypes[1].Exporter = func(v any, i int) any { switch v := v.(*NodeVersion); i { case 0: return &v.state @@ -335,7 +335,7 @@ func file_core_services_synchronization_telem_telem_automation_custom_proto_init return nil } } - file_core_services_synchronization_telem_telem_automation_custom_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + file_core_services_synchronization_telem_telem_automation_custom_proto_msgTypes[2].Exporter = func(v any, i int) any { switch v := v.(*AutomationTelemWrapper); i { case 0: return &v.state @@ -348,7 +348,7 @@ func file_core_services_synchronization_telem_telem_automation_custom_proto_init } } } - file_core_services_synchronization_telem_telem_automation_custom_proto_msgTypes[2].OneofWrappers = []interface{}{ + file_core_services_synchronization_telem_telem_automation_custom_proto_msgTypes[2].OneofWrappers = []any{ (*AutomationTelemWrapper_BlockNumber)(nil), (*AutomationTelemWrapper_NodeVersion)(nil), } diff --git a/core/services/synchronization/telem/telem_enhanced_ea.pb.go b/core/services/synchronization/telem/telem_enhanced_ea.pb.go index 901494bcb8..c78cd65848 100644 --- a/core/services/synchronization/telem/telem_enhanced_ea.pb.go +++ b/core/services/synchronization/telem/telem_enhanced_ea.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.34.1 +// protoc-gen-go v1.34.2 // protoc v4.25.1 // source: core/services/synchronization/telem/telem_enhanced_ea.proto @@ -240,7 +240,7 @@ func file_core_services_synchronization_telem_telem_enhanced_ea_proto_rawDescGZI } var file_core_services_synchronization_telem_telem_enhanced_ea_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_core_services_synchronization_telem_telem_enhanced_ea_proto_goTypes = []interface{}{ +var file_core_services_synchronization_telem_telem_enhanced_ea_proto_goTypes = []any{ (*EnhancedEA)(nil), // 0: telem.EnhancedEA } var file_core_services_synchronization_telem_telem_enhanced_ea_proto_depIdxs = []int32{ @@ -257,7 +257,7 @@ func file_core_services_synchronization_telem_telem_enhanced_ea_proto_init() { return } if !protoimpl.UnsafeEnabled { - file_core_services_synchronization_telem_telem_enhanced_ea_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_core_services_synchronization_telem_telem_enhanced_ea_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*EnhancedEA); i { case 0: return &v.state diff --git a/core/services/synchronization/telem/telem_enhanced_ea_mercury.pb.go b/core/services/synchronization/telem/telem_enhanced_ea_mercury.pb.go index 2d6ce6f7bd..325c6708f9 100644 --- a/core/services/synchronization/telem/telem_enhanced_ea_mercury.pb.go +++ b/core/services/synchronization/telem/telem_enhanced_ea_mercury.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.34.1 +// protoc-gen-go v1.34.2 // protoc v4.25.1 // source: core/services/synchronization/telem/telem_enhanced_ea_mercury.proto @@ -21,6 +21,56 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type MarketStatus int32 + +const ( + // Same values as those used by OCR. + MarketStatus_UNKNOWN MarketStatus = 0 + MarketStatus_CLOSED MarketStatus = 1 + MarketStatus_OPEN MarketStatus = 2 +) + +// Enum value maps for MarketStatus. +var ( + MarketStatus_name = map[int32]string{ + 0: "UNKNOWN", + 1: "CLOSED", + 2: "OPEN", + } + MarketStatus_value = map[string]int32{ + "UNKNOWN": 0, + "CLOSED": 1, + "OPEN": 2, + } +) + +func (x MarketStatus) Enum() *MarketStatus { + p := new(MarketStatus) + *p = x + return p +} + +func (x MarketStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (MarketStatus) Descriptor() protoreflect.EnumDescriptor { + return file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_enumTypes[0].Descriptor() +} + +func (MarketStatus) Type() protoreflect.EnumType { + return &file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_enumTypes[0] +} + +func (x MarketStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use MarketStatus.Descriptor instead. +func (MarketStatus) EnumDescriptor() ([]byte, []int) { + return file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_rawDescGZIP(), []int{0} +} + type EnhancedEAMercury struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -59,10 +109,12 @@ type EnhancedEAMercury struct { ObservationAsk int64 `protobuf:"varint,17,opt,name=observation_ask,json=observationAsk,proto3" json:"observation_ask,omitempty"` // This value overflows, will be reserved and removed in future versions ObservationBidString string `protobuf:"bytes,23,opt,name=observation_bid_string,json=observationBidString,proto3" json:"observation_bid_string,omitempty"` ObservationAskString string `protobuf:"bytes,24,opt,name=observation_ask_string,json=observationAskString,proto3" json:"observation_ask_string,omitempty"` - ConfigDigest string `protobuf:"bytes,18,opt,name=config_digest,json=configDigest,proto3" json:"config_digest,omitempty"` - Round int64 `protobuf:"varint,19,opt,name=round,proto3" json:"round,omitempty"` - Epoch int64 `protobuf:"varint,20,opt,name=epoch,proto3" json:"epoch,omitempty"` - AssetSymbol string `protobuf:"bytes,21,opt,name=asset_symbol,json=assetSymbol,proto3" json:"asset_symbol,omitempty"` + // v4 + ObservationMarketStatus MarketStatus `protobuf:"varint,34,opt,name=observation_market_status,json=observationMarketStatus,proto3,enum=telem.MarketStatus" json:"observation_market_status,omitempty"` + ConfigDigest string `protobuf:"bytes,18,opt,name=config_digest,json=configDigest,proto3" json:"config_digest,omitempty"` + Round int64 `protobuf:"varint,19,opt,name=round,proto3" json:"round,omitempty"` + Epoch int64 `protobuf:"varint,20,opt,name=epoch,proto3" json:"epoch,omitempty"` + AssetSymbol string `protobuf:"bytes,21,opt,name=asset_symbol,json=assetSymbol,proto3" json:"asset_symbol,omitempty"` } func (x *EnhancedEAMercury) Reset() { @@ -300,6 +352,13 @@ func (x *EnhancedEAMercury) GetObservationAskString() string { return "" } +func (x *EnhancedEAMercury) GetObservationMarketStatus() MarketStatus { + if x != nil { + return x.ObservationMarketStatus + } + return MarketStatus_UNKNOWN +} + func (x *EnhancedEAMercury) GetConfigDigest() string { if x != nil { return x.ConfigDigest @@ -335,7 +394,7 @@ var file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_raw 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x5f, 0x65, 0x6e, 0x68, 0x61, 0x6e, 0x63, 0x65, 0x64, 0x5f, 0x65, 0x61, 0x5f, 0x6d, 0x65, 0x72, 0x63, 0x75, 0x72, 0x79, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x22, 0xa9, 0x0c, 0x0a, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x22, 0xfa, 0x0c, 0x0a, 0x11, 0x45, 0x6e, 0x68, 0x61, 0x6e, 0x63, 0x65, 0x64, 0x45, 0x41, 0x4d, 0x65, 0x72, 0x63, 0x75, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x20, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, @@ -427,19 +486,28 @@ var file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_raw 0x0a, 0x16, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x73, 0x6b, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x18, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x73, 0x6b, 0x53, 0x74, - 0x72, 0x69, 0x6e, 0x67, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x64, - 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, 0x75, - 0x6e, 0x64, 0x18, 0x13, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x12, - 0x14, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x14, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, - 0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, - 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x73, 0x73, - 0x65, 0x74, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x42, 0x4e, 0x5a, 0x4c, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x6d, 0x61, 0x72, 0x74, 0x63, 0x6f, 0x6e, 0x74, - 0x72, 0x61, 0x63, 0x74, 0x6b, 0x69, 0x74, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x69, 0x6e, - 0x6b, 0x2f, 0x76, 0x32, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x73, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x72, 0x69, 0x6e, 0x67, 0x12, 0x4f, 0x0a, 0x19, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x22, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x2e, + 0x4d, 0x61, 0x72, 0x6b, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x17, 0x6f, 0x62, + 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x72, 0x6b, 0x65, 0x74, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, + 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, + 0x75, 0x6e, 0x64, 0x18, 0x13, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, + 0x12, 0x14, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x14, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, + 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x2a, 0x31, 0x0a, 0x0c, 0x4d, 0x61, 0x72, + 0x6b, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, + 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x44, + 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4f, 0x50, 0x45, 0x4e, 0x10, 0x02, 0x42, 0x4e, 0x5a, 0x4c, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x6d, 0x61, 0x72, 0x74, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x6b, 0x69, 0x74, 0x2f, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x2f, 0x76, 0x32, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, + 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -454,16 +522,19 @@ func file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_ra return file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_rawDescData } +var file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_goTypes = []interface{}{ - (*EnhancedEAMercury)(nil), // 0: telem.EnhancedEAMercury +var file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_goTypes = []any{ + (MarketStatus)(0), // 0: telem.MarketStatus + (*EnhancedEAMercury)(nil), // 1: telem.EnhancedEAMercury } var file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_depIdxs = []int32{ - 0, // [0:0] is the sub-list for method output_type - 0, // [0:0] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name + 0, // 0: telem.EnhancedEAMercury.observation_market_status:type_name -> telem.MarketStatus + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name } func init() { file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_init() } @@ -472,7 +543,7 @@ func file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_in return } if !protoimpl.UnsafeEnabled { - file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*EnhancedEAMercury); i { case 0: return &v.state @@ -490,13 +561,14 @@ func file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_in File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_rawDesc, - NumEnums: 0, + NumEnums: 1, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_goTypes, DependencyIndexes: file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_depIdxs, + EnumInfos: file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_enumTypes, MessageInfos: file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_msgTypes, }.Build() File_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto = out.File diff --git a/core/services/synchronization/telem/telem_enhanced_ea_mercury.proto b/core/services/synchronization/telem/telem_enhanced_ea_mercury.proto index 8488eb1d50..bb41ff86ee 100644 --- a/core/services/synchronization/telem/telem_enhanced_ea_mercury.proto +++ b/core/services/synchronization/telem/telem_enhanced_ea_mercury.proto @@ -4,6 +4,13 @@ option go_package = "github.com/smartcontractkit/chainlink/v2/core/services/sync package telem; +enum MarketStatus { + // Same values as those used by OCR. + UNKNOWN = 0; + CLOSED = 1; + OPEN = 2; +} + message EnhancedEAMercury { uint32 version = 32; @@ -44,6 +51,8 @@ message EnhancedEAMercury { int64 observation_ask=17; // This value overflows, will be reserved and removed in future versions string observation_bid_string = 23; string observation_ask_string = 24; + // v4 + MarketStatus observation_market_status=34; string config_digest = 18; int64 round=19; diff --git a/core/services/synchronization/telem/telem_functions_request.pb.go b/core/services/synchronization/telem/telem_functions_request.pb.go index fdf44cfbb7..85cf6915d8 100644 --- a/core/services/synchronization/telem/telem_functions_request.pb.go +++ b/core/services/synchronization/telem/telem_functions_request.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.34.1 +// protoc-gen-go v1.34.2 // protoc v4.25.1 // source: core/services/synchronization/telem/telem_functions_request.proto @@ -120,7 +120,7 @@ func file_core_services_synchronization_telem_telem_functions_request_proto_rawD } var file_core_services_synchronization_telem_telem_functions_request_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_core_services_synchronization_telem_telem_functions_request_proto_goTypes = []interface{}{ +var file_core_services_synchronization_telem_telem_functions_request_proto_goTypes = []any{ (*FunctionsRequest)(nil), // 0: telem.FunctionsRequest } var file_core_services_synchronization_telem_telem_functions_request_proto_depIdxs = []int32{ @@ -137,7 +137,7 @@ func file_core_services_synchronization_telem_telem_functions_request_proto_init return } if !protoimpl.UnsafeEnabled { - file_core_services_synchronization_telem_telem_functions_request_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_core_services_synchronization_telem_telem_functions_request_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*FunctionsRequest); i { case 0: return &v.state diff --git a/core/services/synchronization/telem/telem_head_report.pb.go b/core/services/synchronization/telem/telem_head_report.pb.go index 12801314a7..a53eb6ca1d 100644 --- a/core/services/synchronization/telem/telem_head_report.pb.go +++ b/core/services/synchronization/telem/telem_head_report.pb.go @@ -7,10 +7,11 @@ package telem import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( diff --git a/core/services/synchronization/telem/telem_wsrpc.pb.go b/core/services/synchronization/telem/telem_wsrpc.pb.go index e4028b4de4..344ace7169 100644 --- a/core/services/synchronization/telem/telem_wsrpc.pb.go +++ b/core/services/synchronization/telem/telem_wsrpc.pb.go @@ -7,6 +7,7 @@ package telem import ( context "context" + wsrpc "github.com/smartcontractkit/wsrpc" ) diff --git a/core/services/synchronization/telemetry_ingress_batch_client.go b/core/services/synchronization/telemetry_ingress_batch_client.go index cade98cf60..26ce1e3066 100644 --- a/core/services/synchronization/telemetry_ingress_batch_client.go +++ b/core/services/synchronization/telemetry_ingress_batch_client.go @@ -12,8 +12,9 @@ import ( "github.com/smartcontractkit/wsrpc" "github.com/smartcontractkit/wsrpc/examples/simple/keys" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink-common/pkg/timeutil" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" telemPb "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" ) @@ -37,21 +38,18 @@ func (NoopTelemetryIngressBatchClient) Name() string { return func (NoopTelemetryIngressBatchClient) Ready() error { return nil } type telemetryIngressBatchClient struct { - services.StateMachine + services.Service + eng *services.Engine + url *url.URL ks keystore.CSA serverPubKeyHex string connected atomic.Bool telemClient telemPb.TelemClient - close func() error - - globalLogger logger.Logger - logging bool - lggr logger.Logger + closeFn func() error - wgDone sync.WaitGroup - chDone services.StopChan + logging bool telemBufferSize uint telemMaxBatchSize uint @@ -66,8 +64,8 @@ type telemetryIngressBatchClient struct { // NewTelemetryIngressBatchClient returns a client backed by wsrpc that // can send telemetry to the telemetry ingress server -func NewTelemetryIngressBatchClient(url *url.URL, serverPubKeyHex string, ks keystore.CSA, logging bool, lggr logger.Logger, telemBufferSize uint, telemMaxBatchSize uint, telemSendInterval time.Duration, telemSendTimeout time.Duration, useUniconn bool, network string, chainID string) TelemetryService { - return &telemetryIngressBatchClient{ +func NewTelemetryIngressBatchClient(url *url.URL, serverPubKeyHex string, ks keystore.CSA, logging bool, lggr logger.Logger, telemBufferSize uint, telemMaxBatchSize uint, telemSendInterval time.Duration, telemSendTimeout time.Duration, useUniconn bool) TelemetryService { + c := &telemetryIngressBatchClient{ telemBufferSize: telemBufferSize, telemMaxBatchSize: telemMaxBatchSize, telemSendInterval: telemSendInterval, @@ -75,13 +73,17 @@ func NewTelemetryIngressBatchClient(url *url.URL, serverPubKeyHex string, ks key url: url, ks: ks, serverPubKeyHex: serverPubKeyHex, - globalLogger: lggr, logging: logging, - lggr: lggr.Named("TelemetryIngressBatchClient").Named(network).Named(chainID), - chDone: make(services.StopChan), workers: make(map[string]*telemetryIngressBatchWorker), useUniConn: useUniconn, } + c.Service, c.eng = services.Config{ + Name: "TelemetryIngressBatchClient", + Start: c.start, + Close: c.close, + }.NewServiceEngine(lggr) + + return c } // Start connects the wsrpc client to the telemetry ingress server @@ -90,71 +92,53 @@ func NewTelemetryIngressBatchClient(url *url.URL, serverPubKeyHex string, ks key // an error and wsrpc will continue to retry the connection. Eventually when the ingress // server does come back up, wsrpc will establish the connection without any interaction // on behalf of the node operator. -func (tc *telemetryIngressBatchClient) Start(ctx context.Context) error { - return tc.StartOnce("TelemetryIngressBatchClient", func() error { - clientPrivKey, err := tc.getCSAPrivateKey() - if err != nil { - return err - } +func (tc *telemetryIngressBatchClient) start(ctx context.Context) error { + clientPrivKey, err := tc.getCSAPrivateKey() + if err != nil { + return err + } - serverPubKey := keys.FromHex(tc.serverPubKeyHex) - - // Initialize a new wsrpc client caller - // This is used to call RPC methods on the server - if tc.telemClient == nil { // only preset for tests - if tc.useUniConn { - tc.wgDone.Add(1) - go func() { - defer tc.wgDone.Done() - ctx2, cancel := tc.chDone.NewCtx() - defer cancel() - conn, err := wsrpc.DialUniWithContext(ctx2, tc.lggr, tc.url.String(), clientPrivKey, serverPubKey) - if err != nil { - if ctx2.Err() != nil { - tc.lggr.Warnw("gave up connecting to telemetry endpoint", "err", err) - } else { - tc.lggr.Criticalw("telemetry endpoint dial errored unexpectedly", "err", err, "server pubkey", tc.serverPubKeyHex) - tc.SvcErrBuffer.Append(err) - } - return - } - tc.telemClient = telemPb.NewTelemClient(conn) - tc.close = conn.Close - tc.connected.Store(true) - }() - } else { - // Spawns a goroutine that will eventually connect - conn, err := wsrpc.DialWithContext(ctx, tc.url.String(), wsrpc.WithTransportCreds(clientPrivKey, serverPubKey), wsrpc.WithLogger(tc.lggr)) + serverPubKey := keys.FromHex(tc.serverPubKeyHex) + + // Initialize a new wsrpc client caller + // This is used to call RPC methods on the server + if tc.telemClient == nil { // only preset for tests + if tc.useUniConn { + tc.eng.Go(func(ctx context.Context) { + conn, err := wsrpc.DialUniWithContext(ctx, tc.eng, tc.url.String(), clientPrivKey, serverPubKey) if err != nil { - return fmt.Errorf("could not start TelemIngressBatchClient, Dial returned error: %v", err) + if ctx.Err() != nil { + tc.eng.Warnw("gave up connecting to telemetry endpoint", "err", err) + } else { + tc.eng.Criticalw("telemetry endpoint dial errored unexpectedly", "err", err, "server pubkey", tc.serverPubKeyHex) + tc.eng.EmitHealthErr(err) + } + return } tc.telemClient = telemPb.NewTelemClient(conn) - tc.close = func() error { conn.Close(); return nil } + tc.closeFn = conn.Close + tc.connected.Store(true) + }) + } else { + // Spawns a goroutine that will eventually connect + conn, err := wsrpc.DialWithContext(ctx, tc.url.String(), wsrpc.WithTransportCreds(clientPrivKey, serverPubKey), wsrpc.WithLogger(tc.eng)) + if err != nil { + return fmt.Errorf("could not start TelemIngressBatchClient, Dial returned error: %v", err) } + tc.telemClient = telemPb.NewTelemClient(conn) + tc.closeFn = func() error { conn.Close(); return nil } } + } - return nil - }) + return nil } // Close disconnects the wsrpc client from the ingress server and waits for all workers to exit -func (tc *telemetryIngressBatchClient) Close() error { - return tc.StopOnce("TelemetryIngressBatchClient", func() error { - close(tc.chDone) - tc.wgDone.Wait() - if (tc.useUniConn && tc.connected.Load()) || !tc.useUniConn { - return tc.close() - } - return nil - }) -} - -func (tc *telemetryIngressBatchClient) Name() string { - return tc.lggr.Name() -} - -func (tc *telemetryIngressBatchClient) HealthReport() map[string]error { - return map[string]error{tc.Name(): tc.Healthy()} +func (tc *telemetryIngressBatchClient) close() error { + if (tc.useUniConn && tc.connected.Load()) || !tc.useUniConn { + return tc.closeFn() + } + return nil } // getCSAPrivateKey gets the client's CSA private key @@ -175,7 +159,7 @@ func (tc *telemetryIngressBatchClient) getCSAPrivateKey() (privkey []byte, err e // and a warning is logged. func (tc *telemetryIngressBatchClient) Send(ctx context.Context, telemData []byte, contractID string, telemType TelemetryType) { if tc.useUniConn && !tc.connected.Load() { - tc.lggr.Warnw("not connected to telemetry endpoint", "endpoint", tc.url.String()) + tc.eng.Warnw("not connected to telemetry endpoint", "endpoint", tc.url.String()) return } payload := TelemPayload{ @@ -206,18 +190,17 @@ func (tc *telemetryIngressBatchClient) findOrCreateWorker(payload TelemPayload) if !found { worker = NewTelemetryIngressBatchWorker( tc.telemMaxBatchSize, - tc.telemSendInterval, tc.telemSendTimeout, tc.telemClient, - &tc.wgDone, - tc.chDone, make(chan TelemPayload, tc.telemBufferSize), payload.ContractID, payload.TelemType, - tc.globalLogger, + tc.eng, tc.logging, ) - worker.Start() + tc.eng.GoTick(timeutil.NewTicker(func() time.Duration { + return tc.telemSendInterval + }), worker.Send) tc.workers[workerKey] = worker } diff --git a/core/services/synchronization/telemetry_ingress_batch_worker.go b/core/services/synchronization/telemetry_ingress_batch_worker.go index e7ea659581..7eca26f02c 100644 --- a/core/services/synchronization/telemetry_ingress_batch_worker.go +++ b/core/services/synchronization/telemetry_ingress_batch_worker.go @@ -2,13 +2,12 @@ package synchronization import ( "context" - "sync" "sync/atomic" "time" "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink-common/pkg/logger" telemPb "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" ) @@ -18,11 +17,8 @@ type telemetryIngressBatchWorker struct { services.Service telemMaxBatchSize uint - telemSendInterval time.Duration telemSendTimeout time.Duration telemClient telemPb.TelemClient - wgDone *sync.WaitGroup - chDone services.StopChan chTelemetry chan TelemPayload contractID string telemType TelemetryType @@ -35,65 +31,45 @@ type telemetryIngressBatchWorker struct { // telemetry to the ingress server via WSRPC func NewTelemetryIngressBatchWorker( telemMaxBatchSize uint, - telemSendInterval time.Duration, telemSendTimeout time.Duration, telemClient telemPb.TelemClient, - wgDone *sync.WaitGroup, - chDone chan struct{}, chTelemetry chan TelemPayload, contractID string, telemType TelemetryType, - globalLogger logger.Logger, + lggr logger.Logger, logging bool, ) *telemetryIngressBatchWorker { return &telemetryIngressBatchWorker{ - telemSendInterval: telemSendInterval, telemSendTimeout: telemSendTimeout, telemMaxBatchSize: telemMaxBatchSize, telemClient: telemClient, - wgDone: wgDone, - chDone: chDone, chTelemetry: chTelemetry, contractID: contractID, telemType: telemType, logging: logging, - lggr: globalLogger.Named("TelemetryIngressBatchWorker"), + lggr: logger.Named(lggr, "TelemetryIngressBatchWorker"), } } -// Start sends batched telemetry to the ingress server on an interval -func (tw *telemetryIngressBatchWorker) Start() { - tw.wgDone.Add(1) - sendTicker := time.NewTicker(tw.telemSendInterval) - - go func() { - defer tw.wgDone.Done() - - for { - select { - case <-sendTicker.C: - if len(tw.chTelemetry) == 0 { - continue - } +// Send sends batched telemetry to the ingress server on an interval +func (tw *telemetryIngressBatchWorker) Send(ctx context.Context) { + if len(tw.chTelemetry) == 0 { + return + } - // Send batched telemetry to the ingress server, log any errors - telemBatchReq := tw.BuildTelemBatchReq() - ctx, cancel := tw.chDone.CtxCancel(context.WithTimeout(context.Background(), tw.telemSendTimeout)) - _, err := tw.telemClient.TelemBatch(ctx, telemBatchReq) - cancel() + // Send batched telemetry to the ingress server, log any errors + telemBatchReq := tw.BuildTelemBatchReq() + ctx, cancel := context.WithTimeout(ctx, tw.telemSendTimeout) + _, err := tw.telemClient.TelemBatch(ctx, telemBatchReq) + cancel() - if err != nil { - tw.lggr.Warnf("Could not send telemetry: %v", err) - continue - } - if tw.logging { - tw.lggr.Debugw("Successfully sent telemetry to ingress server", "contractID", telemBatchReq.ContractId, "telemType", telemBatchReq.TelemetryType, "telemetry", telemBatchReq.Telemetry) - } - case <-tw.chDone: - return - } - } - }() + if err != nil { + tw.lggr.Warnf("Could not send telemetry: %v", err) + return + } + if tw.logging { + tw.lggr.Debugw("Successfully sent telemetry to ingress server", "contractID", telemBatchReq.ContractId, "telemType", telemBatchReq.TelemetryType, "telemetry", telemBatchReq.Telemetry) + } } // logBufferFullWithExpBackoff logs messages at diff --git a/core/services/synchronization/telemetry_ingress_batch_worker_test.go b/core/services/synchronization/telemetry_ingress_batch_worker_test.go index 109022c713..bf44ee9195 100644 --- a/core/services/synchronization/telemetry_ingress_batch_worker_test.go +++ b/core/services/synchronization/telemetry_ingress_batch_worker_test.go @@ -1,7 +1,6 @@ package synchronization_test import ( - "sync" "testing" "time" @@ -22,11 +21,8 @@ func TestTelemetryIngressWorker_BuildTelemBatchReq(t *testing.T) { chTelemetry := make(chan synchronization.TelemPayload, 10) worker := synchronization.NewTelemetryIngressBatchWorker( uint(maxTelemBatchSize), - time.Millisecond*1, time.Second, mocks.NewTelemClient(t), - &sync.WaitGroup{}, - make(chan struct{}), chTelemetry, "0xa", synchronization.OCR, diff --git a/core/services/synchronization/telemetry_ingress_client.go b/core/services/synchronization/telemetry_ingress_client.go index dc4ced31d0..1ed55bb546 100644 --- a/core/services/synchronization/telemetry_ingress_client.go +++ b/core/services/synchronization/telemetry_ingress_client.go @@ -4,15 +4,14 @@ import ( "context" "errors" "net/url" - "sync" "sync/atomic" "time" "github.com/smartcontractkit/wsrpc" "github.com/smartcontractkit/wsrpc/examples/simple/keys" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" telemPb "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" ) @@ -35,82 +34,59 @@ func (NoopTelemetryIngressClient) Name() string { return "Noop func (NoopTelemetryIngressClient) Ready() error { return nil } type telemetryIngressClient struct { - services.StateMachine + services.Service + eng *services.Engine + url *url.URL ks keystore.CSA serverPubKeyHex string telemClient telemPb.TelemClient logging bool - lggr logger.Logger - wgDone sync.WaitGroup - chDone services.StopChan dropMessageCount atomic.Uint32 chTelemetry chan TelemPayload } // NewTelemetryIngressClient returns a client backed by wsrpc that // can send telemetry to the telemetry ingress server -func NewTelemetryIngressClient(url *url.URL, serverPubKeyHex string, ks keystore.CSA, logging bool, lggr logger.Logger, telemBufferSize uint, network string, chainID string) TelemetryService { - return &telemetryIngressClient{ +func NewTelemetryIngressClient(url *url.URL, serverPubKeyHex string, ks keystore.CSA, logging bool, lggr logger.Logger, telemBufferSize uint) TelemetryService { + c := &telemetryIngressClient{ url: url, ks: ks, serverPubKeyHex: serverPubKeyHex, logging: logging, - lggr: lggr.Named("TelemetryIngressClient").Named(network).Named(chainID), chTelemetry: make(chan TelemPayload, telemBufferSize), - chDone: make(services.StopChan), } + c.Service, c.eng = services.Config{ + Name: "TelemetryIngressClient", + Start: c.start, + }.NewServiceEngine(lggr) + return c } // Start connects the wsrpc client to the telemetry ingress server -func (tc *telemetryIngressClient) Start(context.Context) error { - return tc.StartOnce("TelemetryIngressClient", func() error { - privkey, err := tc.getCSAPrivateKey() - if err != nil { - return err - } - - tc.connect(privkey) - - return nil - }) -} - -// Close disconnects the wsrpc client from the ingress server -func (tc *telemetryIngressClient) Close() error { - return tc.StopOnce("TelemetryIngressClient", func() error { - close(tc.chDone) - tc.wgDone.Wait() - return nil - }) -} +func (tc *telemetryIngressClient) start(context.Context) error { + privkey, err := tc.getCSAPrivateKey() + if err != nil { + return err + } -func (tc *telemetryIngressClient) Name() string { - return tc.lggr.Name() -} + tc.connect(privkey) -func (tc *telemetryIngressClient) HealthReport() map[string]error { - return map[string]error{tc.Name(): tc.Healthy()} + return nil } func (tc *telemetryIngressClient) connect(clientPrivKey []byte) { - tc.wgDone.Add(1) - - go func() { - defer tc.wgDone.Done() - ctx, cancel := tc.chDone.NewCtx() - defer cancel() - + tc.eng.Go(func(ctx context.Context) { serverPubKey := keys.FromHex(tc.serverPubKeyHex) - conn, err := wsrpc.DialWithContext(ctx, tc.url.String(), wsrpc.WithTransportCreds(clientPrivKey, serverPubKey), wsrpc.WithLogger(tc.lggr)) + conn, err := wsrpc.DialWithContext(ctx, tc.url.String(), wsrpc.WithTransportCreds(clientPrivKey, serverPubKey), wsrpc.WithLogger(tc.eng)) if err != nil { if ctx.Err() != nil { - tc.lggr.Warnw("gave up connecting to telemetry endpoint", "err", err) + tc.eng.Warnw("gave up connecting to telemetry endpoint", "err", err) } else { - tc.lggr.Criticalw("telemetry endpoint dial errored unexpectedly", "err", err) - tc.SvcErrBuffer.Append(err) + tc.eng.Criticalw("telemetry endpoint dial errored unexpectedly", "err", err) + tc.eng.EmitHealthErr(err) } return } @@ -126,16 +102,12 @@ func (tc *telemetryIngressClient) connect(clientPrivKey []byte) { tc.handleTelemetry() // Wait for close - <-tc.chDone - }() + <-ctx.Done() + }) } func (tc *telemetryIngressClient) handleTelemetry() { - tc.wgDone.Add(1) - go func() { - defer tc.wgDone.Done() - ctx, cancel := tc.chDone.NewCtx() - defer cancel() + tc.eng.Go(func(ctx context.Context) { for { select { case p := <-tc.chTelemetry: @@ -148,17 +120,17 @@ func (tc *telemetryIngressClient) handleTelemetry() { } _, err := tc.telemClient.Telem(ctx, telemReq) if err != nil { - tc.lggr.Errorf("Could not send telemetry: %v", err) + tc.eng.Errorf("Could not send telemetry: %v", err) continue } if tc.logging { - tc.lggr.Debugw("successfully sent telemetry to ingress server", "contractID", p.ContractID, "telemetry", p.Telemetry) + tc.eng.Debugw("successfully sent telemetry to ingress server", "contractID", p.ContractID, "telemetry", p.Telemetry) } - case <-tc.chDone: + case <-ctx.Done(): return } } - }() + }) } // logBufferFullWithExpBackoff logs messages at @@ -176,7 +148,7 @@ func (tc *telemetryIngressClient) handleTelemetry() { func (tc *telemetryIngressClient) logBufferFullWithExpBackoff(payload TelemPayload) { count := tc.dropMessageCount.Add(1) if count > 0 && (count%100 == 0 || count&(count-1) == 0) { - tc.lggr.Warnw("telemetry ingress client buffer full, dropping message", "telemetry", payload.Telemetry, "droppedCount", count) + tc.eng.Warnw("telemetry ingress client buffer full, dropping message", "telemetry", payload.Telemetry, "droppedCount", count) } } diff --git a/core/services/telemetry/manager.go b/core/services/telemetry/manager.go index a65759a5c6..73a94b4b12 100644 --- a/core/services/telemetry/manager.go +++ b/core/services/telemetry/manager.go @@ -1,29 +1,29 @@ package telemetry import ( - "context" "net/url" "strings" "time" "github.com/pkg/errors" - "go.uber.org/multierr" - "github.com/smartcontractkit/libocr/commontypes" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + common "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/config" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/synchronization" ) type Manager struct { - services.StateMachine - bufferSize uint - endpoints []*telemetryEndpoint - ks keystore.CSA - lggr logger.Logger + services.Service + eng *services.Engine + + bufferSize uint + endpoints []*telemetryEndpoint + ks keystore.CSA + logging bool maxBatchSize uint sendInterval time.Duration @@ -45,9 +45,7 @@ type telemetryEndpoint struct { func NewManager(cfg config.TelemetryIngress, csaKeyStore keystore.CSA, lggr logger.Logger) *Manager { m := &Manager{ bufferSize: cfg.BufferSize(), - endpoints: nil, ks: csaKeyStore, - lggr: lggr.Named("TelemetryManager"), logging: cfg.Logging(), maxBatchSize: cfg.MaxBatchSize(), sendInterval: cfg.SendInterval(), @@ -55,44 +53,21 @@ func NewManager(cfg config.TelemetryIngress, csaKeyStore keystore.CSA, lggr logg uniConn: cfg.UniConn(), useBatchSend: cfg.UseBatchSend(), } - for _, e := range cfg.Endpoints() { - if err := m.addEndpoint(e); err != nil { - m.lggr.Error(err) - } - } - return m -} - -func (m *Manager) Start(ctx context.Context) error { - return m.StartOnce("TelemetryManager", func() error { - var err error - for _, e := range m.endpoints { - err = multierr.Append(err, e.client.Start(ctx)) - } - return err - }) -} -func (m *Manager) Close() error { - return m.StopOnce("TelemetryManager", func() error { - var err error - for _, e := range m.endpoints { - err = multierr.Append(err, e.client.Close()) - } - return err - }) -} - -func (m *Manager) Name() string { - return m.lggr.Name() -} + m.Service, m.eng = services.Config{ + Name: "TelemetryManager", + NewSubServices: func(lggr common.Logger) (subs []services.Service) { + for _, e := range cfg.Endpoints() { + if sub, err := m.newEndpoint(e, lggr, cfg); err != nil { + lggr.Error(err) + } else { + subs = append(subs, sub) + } + } + return + }, + }.NewServiceEngine(lggr) -func (m *Manager) HealthReport() map[string]error { - hr := map[string]error{m.Name(): m.Healthy()} - - for _, e := range m.endpoints { - services.CopyHealth(hr, e.client.HealthReport()) - } - return hr + return m } // GenMonitoringEndpoint creates a new monitoring endpoints based on the existing available endpoints defined in the core config TOML, if no endpoint for the network and chainID exists, a NOOP agent will be used and the telemetry will not be sent @@ -100,7 +75,7 @@ func (m *Manager) GenMonitoringEndpoint(network string, chainID string, contract e, found := m.getEndpoint(network, chainID) if !found { - m.lggr.Warnf("no telemetry endpoint found for network %q chainID %q, telemetry %q for contactID %q will NOT be sent", network, chainID, telemType, contractID) + m.eng.Warnf("no telemetry endpoint found for network %q chainID %q, telemetry %q for contactID %q will NOT be sent", network, chainID, telemType, contractID) return &NoopAgent{} } @@ -111,32 +86,33 @@ func (m *Manager) GenMonitoringEndpoint(network string, chainID string, contract return NewIngressAgent(e.client, network, chainID, contractID, telemType) } -func (m *Manager) addEndpoint(e config.TelemetryIngressEndpoint) error { +func (m *Manager) newEndpoint(e config.TelemetryIngressEndpoint, lggr logger.Logger, cfg config.TelemetryIngress) (services.Service, error) { if e.Network() == "" { - return errors.New("cannot add telemetry endpoint, network cannot be empty") + return nil, errors.New("cannot add telemetry endpoint, network cannot be empty") } if e.ChainID() == "" { - return errors.New("cannot add telemetry endpoint, chainID cannot be empty") + return nil, errors.New("cannot add telemetry endpoint, chainID cannot be empty") } if e.URL() == nil { - return errors.New("cannot add telemetry endpoint, URL cannot be empty") + return nil, errors.New("cannot add telemetry endpoint, URL cannot be empty") } if e.ServerPubKey() == "" { - return errors.New("cannot add telemetry endpoint, ServerPubKey cannot be empty") + return nil, errors.New("cannot add telemetry endpoint, ServerPubKey cannot be empty") } if _, found := m.getEndpoint(e.Network(), e.ChainID()); found { - return errors.Errorf("cannot add telemetry endpoint for network %q and chainID %q, endpoint already exists", e.Network(), e.ChainID()) + return nil, errors.Errorf("cannot add telemetry endpoint for network %q and chainID %q, endpoint already exists", e.Network(), e.ChainID()) } + lggr = logger.Sugared(lggr).Named(e.Network()).Named(e.ChainID()) var tClient synchronization.TelemetryService if m.useBatchSend { - tClient = synchronization.NewTelemetryIngressBatchClient(e.URL(), e.ServerPubKey(), m.ks, m.logging, m.lggr, m.bufferSize, m.maxBatchSize, m.sendInterval, m.sendTimeout, m.uniConn, e.Network(), e.ChainID()) + tClient = synchronization.NewTelemetryIngressBatchClient(e.URL(), e.ServerPubKey(), m.ks, cfg.Logging(), lggr, cfg.BufferSize(), cfg.MaxBatchSize(), cfg.SendInterval(), cfg.SendTimeout(), cfg.UniConn()) } else { - tClient = synchronization.NewTelemetryIngressClient(e.URL(), e.ServerPubKey(), m.ks, m.logging, m.lggr, m.bufferSize, e.Network(), e.ChainID()) + tClient = synchronization.NewTelemetryIngressClient(e.URL(), e.ServerPubKey(), m.ks, cfg.Logging(), lggr, cfg.BufferSize()) } te := telemetryEndpoint{ @@ -148,7 +124,7 @@ func (m *Manager) addEndpoint(e config.TelemetryIngressEndpoint) error { } m.endpoints = append(m.endpoints, &te) - return nil + return te.client, nil } func (m *Manager) getEndpoint(network string, chainID string) (*telemetryEndpoint, bool) { diff --git a/core/services/telemetry/manager_test.go b/core/services/telemetry/manager_test.go index 4e55cb7575..fef065b572 100644 --- a/core/services/telemetry/manager_test.go +++ b/core/services/telemetry/manager_test.go @@ -156,7 +156,7 @@ func TestNewManager(t *testing.T) { require.Equal(t, uint(123), m.bufferSize) require.Equal(t, ks, m.ks) - require.Equal(t, "TelemetryManager", m.lggr.Name()) + require.Equal(t, "TelemetryManager", m.Name()) require.Equal(t, true, m.logging) require.Equal(t, uint(51), m.maxBatchSize) require.Equal(t, time.Millisecond*512, m.sendInterval) diff --git a/core/services/versioning/orm_test.go b/core/services/versioning/orm_test.go index 3504c2bc77..e2f05e3903 100644 --- a/core/services/versioning/orm_test.go +++ b/core/services/versioning/orm_test.go @@ -97,6 +97,87 @@ func Test_Version_CheckVersion(t *testing.T) { assert.Equal(t, "9.9.8", dbv.String()) } +func TestORM_CheckVersion_CCIP(t *testing.T) { + ctx := testutils.Context(t) + db := pgtest.NewSqlxDB(t) + + lggr := logger.TestLogger(t) + + orm := NewORM(db, lggr) + + tests := []struct { + name string + currentVersion string + newVersion string + expectedError bool + }{ + { + name: "ccip patch version bump from 0 -> 2", + currentVersion: "2.5.0-ccip1.4.0", + newVersion: "2.5.0-ccip1.4.2", + expectedError: false, + }, + { + name: "ccip patch downgrade errors", + currentVersion: "2.5.0-ccip1.4.2", + newVersion: "2.5.0-ccip1.4.1", + expectedError: true, + }, + { + name: "ccip patch version bump from 2 -> 10", + currentVersion: "2.5.0-ccip1.4.2", + newVersion: "2.5.0-ccip1.4.10", + expectedError: false, + }, + { + name: "ccip patch version bump from 9 -> 101", + currentVersion: "2.5.0-ccip1.4.9", + newVersion: "2.5.0-ccip1.4.101", + expectedError: false, + }, + { + name: "upgrading only core version", + currentVersion: "2.5.0-ccip1.4.10", + newVersion: "2.6.0-ccip1.4.10", + expectedError: false, + }, + { + name: "downgrading only core version errors", + currentVersion: "2.6.0-ccip1.4.10", + newVersion: "2.5.0-ccip1.4.10", + expectedError: true, + }, + { + name: "upgrading both core and ccip version", + currentVersion: "2.5.0-ccip1.4.10", + newVersion: "2.6.0-ccip1.4.11", + expectedError: false, + }, + { + name: "upgrading both core and ccip version but minor version", + currentVersion: "2.5.0-ccip1.4.10", + newVersion: "2.6.0-ccip1.5.0", + expectedError: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + _, err := db.ExecContext(ctx, `TRUNCATE node_versions;`) + require.NoError(t, err) + + require.NoError(t, orm.UpsertNodeVersion(ctx, NewNodeVersion(test.currentVersion))) + _, _, err = CheckVersion(ctx, db, lggr, test.newVersion) + if test.expectedError { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + + } +} + func TestORM_NodeVersion_FindLatestNodeVersion(t *testing.T) { ctx := testutils.Context(t) db := pgtest.NewSqlxDB(t) diff --git a/core/services/vrf/delegate_test.go b/core/services/vrf/delegate_test.go index 889b19d0e0..9718dc376a 100644 --- a/core/services/vrf/delegate_test.go +++ b/core/services/vrf/delegate_test.go @@ -83,7 +83,7 @@ func buildVrfUni(t *testing.T, db *sqlx.DB, cfg chainlink.GeneralConfig) vrfUniv btORM := bridges.NewORM(db) ks := keystore.NewInMemory(db, utils.FastScryptParams, lggr) _, dbConfig, evmConfig := txmgr.MakeTestConfigs(t) - txm, err := txmgr.NewTxm(db, evmConfig, evmConfig.GasEstimator(), evmConfig.Transactions(), nil, dbConfig, dbConfig.Listener(), ec, logger.TestLogger(t), nil, ks.Eth(), nil) + txm, err := txmgr.NewTxm(db, evmConfig, evmConfig.GasEstimator(), evmConfig.Transactions(), nil, dbConfig, dbConfig.Listener(), ec, logger.TestLogger(t), nil, ks.Eth(), nil, nil) orm := headtracker.NewORM(*testutils.FixtureChainID, db) require.NoError(t, orm.IdempotentInsertHead(testutils.Context(t), cltest.Head(51))) jrm := job.NewORM(db, prm, btORM, ks, lggr) diff --git a/core/services/vrf/v1/integration_test.go b/core/services/vrf/v1/integration_test.go index d7f791ad29..125388b6b2 100644 --- a/core/services/vrf/v1/integration_test.go +++ b/core/services/vrf/v1/integration_test.go @@ -16,6 +16,7 @@ import ( "gopkg.in/guregu/null.v4" commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/solidity_vrf_coordinator_interface" diff --git a/core/services/vrf/v2/integration_v2_plus_test.go b/core/services/vrf/v2/integration_v2_plus_test.go index 3aa2302e55..764c950ec3 100644 --- a/core/services/vrf/v2/integration_v2_plus_test.go +++ b/core/services/vrf/v2/integration_v2_plus_test.go @@ -17,6 +17,7 @@ import ( "github.com/stretchr/testify/require" commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" diff --git a/core/services/vrf/v2/integration_v2_test.go b/core/services/vrf/v2/integration_v2_test.go index becee19aaa..33ed5a76b5 100644 --- a/core/services/vrf/v2/integration_v2_test.go +++ b/core/services/vrf/v2/integration_v2_test.go @@ -142,7 +142,7 @@ func makeTestTxm(t *testing.T, txStore txmgr.TestEvmTxStore, keyStore keystore.M _, _, evmConfig := txmgr.MakeTestConfigs(t) txmConfig := txmgr.NewEvmTxmConfig(evmConfig) txm := txmgr.NewEvmTxm(ec.ConfiguredChainID(), txmConfig, evmConfig.Transactions(), keyStore.Eth(), logger.TestLogger(t), nil, nil, - nil, txStore, nil, nil, nil, nil) + nil, txStore, nil, nil, nil, nil, nil) return txm } diff --git a/core/services/vrf/v2/listener_v2_test.go b/core/services/vrf/v2/listener_v2_test.go index ac59f1fdb6..b7a8710c4f 100644 --- a/core/services/vrf/v2/listener_v2_test.go +++ b/core/services/vrf/v2/listener_v2_test.go @@ -40,7 +40,7 @@ func makeTestTxm(t *testing.T, txStore txmgr.TestEvmTxStore, keyStore keystore.M ec := evmtest.NewEthClientMockWithDefaultChain(t) txmConfig := txmgr.NewEvmTxmConfig(evmConfig) txm := txmgr.NewEvmTxm(ec.ConfiguredChainID(), txmConfig, evmConfig.Transactions(), keyStore.Eth(), logger.TestLogger(t), nil, nil, - nil, txStore, nil, nil, nil, nil) + nil, txStore, nil, nil, nil, nil, nil) return txm } diff --git a/core/services/workflows/engine.go b/core/services/workflows/engine.go index ed5daaf210..f3bca4b900 100644 --- a/core/services/workflows/engine.go +++ b/core/services/workflows/engine.go @@ -36,7 +36,7 @@ type Engine struct { localNode capabilities.Node executionStates store.Store pendingStepRequests chan stepRequest - triggerEvents chan capabilities.CapabilityResponse + triggerEvents chan capabilities.TriggerResponse stepUpdateCh chan store.WorkflowExecutionStep wg sync.WaitGroup stopCh services.StopChan @@ -319,32 +319,25 @@ func generateTriggerId(workflowID string, triggerIdx int) string { // registerTrigger is used during the initialization phase to bind a trigger to this workflow func (e *Engine) registerTrigger(ctx context.Context, t *triggerCapability, triggerIdx int) error { triggerID := generateTriggerId(e.workflow.id, triggerIdx) - triggerInputs, err := values.NewMap( - map[string]any{ - "triggerId": triggerID, - }, - ) - if err != nil { - return err - } tc, err := values.NewMap(t.Config) if err != nil { return err } - t.config = tc + t.config.Store(tc) - triggerRegRequest := capabilities.CapabilityRequest{ + triggerRegRequest := capabilities.TriggerRegistrationRequest{ Metadata: capabilities.RequestMetadata{ WorkflowID: e.workflow.id, + WorkflowOwner: e.workflow.owner, + WorkflowName: e.workflow.name, WorkflowDonID: e.localNode.WorkflowDON.ID, WorkflowDonConfigVersion: e.localNode.WorkflowDON.ConfigVersion, - WorkflowName: e.workflow.name, - WorkflowOwner: e.workflow.owner, + ReferenceID: t.Ref, }, - Config: tc, - Inputs: triggerInputs, + Config: t.config.Load(), + TriggerID: triggerID, } eventsCh, err := t.trigger.RegisterTrigger(ctx, triggerRegRequest) if err != nil { @@ -422,12 +415,7 @@ func (e *Engine) loop(ctx context.Context) { continue } - te := &capabilities.TriggerEvent{} - err := resp.Value.UnwrapTo(te) - if err != nil { - e.logger.Errorf("could not unwrap trigger event; error %v", resp.Err) - continue - } + te := resp.Event executionID, err := generateExecutionID(e.workflow.id, te.ID) if err != nil { @@ -435,7 +423,7 @@ func (e *Engine) loop(ctx context.Context) { continue } - err = e.startExecution(ctx, executionID, resp.Value) + err = e.startExecution(ctx, executionID, resp.Event.Outputs) if err != nil { e.logger.With(eIDKey, executionID).Errorf("failed to start execution: %v", err) } @@ -466,7 +454,7 @@ func generateExecutionID(workflowID, eventID string) (string, error) { } // startExecution kicks off a new workflow execution when a trigger event is received. -func (e *Engine) startExecution(ctx context.Context, executionID string, event values.Value) error { +func (e *Engine) startExecution(ctx context.Context, executionID string, event *values.Map) error { e.logger.With("event", event, eIDKey, executionID).Debug("executing on a trigger event") ec := &store.WorkflowExecution{ Steps: map[string]*store.WorkflowExecutionStep{ @@ -713,6 +701,10 @@ func (e *Engine) configForStep(ctx context.Context, executionID string, step *st return step.config, nil } + if capConfig.DefaultConfig == nil { + return step.config, nil + } + // Merge the configs for now; note that this means that a workflow can override // all of the config set by the capability. This is probably not desirable in // the long-term, but we don't know much about those use cases so stick to a simpler @@ -759,6 +751,7 @@ func (e *Engine) executeStep(ctx context.Context, msg stepRequest) (*values.Map, WorkflowName: e.workflow.name, WorkflowDonID: e.localNode.WorkflowDON.ID, WorkflowDonConfigVersion: e.localNode.WorkflowDON.ConfigVersion, + ReferenceID: msg.stepRef, }, } @@ -771,24 +764,17 @@ func (e *Engine) executeStep(ctx context.Context, msg stepRequest) (*values.Map, } func (e *Engine) deregisterTrigger(ctx context.Context, t *triggerCapability, triggerIdx int) error { - triggerInputs, err := values.NewMap( - map[string]any{ - "triggerId": generateTriggerId(e.workflow.id, triggerIdx), - }, - ) - if err != nil { - return err - } - deregRequest := capabilities.CapabilityRequest{ + deregRequest := capabilities.TriggerRegistrationRequest{ Metadata: capabilities.RequestMetadata{ WorkflowID: e.workflow.id, WorkflowDonID: e.localNode.WorkflowDON.ID, WorkflowDonConfigVersion: e.localNode.WorkflowDON.ConfigVersion, WorkflowName: e.workflow.name, WorkflowOwner: e.workflow.owner, + ReferenceID: t.Ref, }, - Inputs: triggerInputs, - Config: t.config, + TriggerID: generateTriggerId(e.workflow.id, triggerIdx), + Config: t.config.Load(), } // if t.trigger == nil, then we haven't initialized the workflow @@ -952,7 +938,7 @@ func NewEngine(cfg Config) (engine *Engine, err error) { executionStates: cfg.Store, pendingStepRequests: make(chan stepRequest, cfg.QueueSize), stepUpdateCh: make(chan store.WorkflowExecutionStep), - triggerEvents: make(chan capabilities.CapabilityResponse), + triggerEvents: make(chan capabilities.TriggerResponse), stopCh: make(chan struct{}), newWorkerTimeout: cfg.NewWorkerTimeout, maxExecutionDuration: cfg.MaxExecutionDuration, diff --git a/core/services/workflows/engine_test.go b/core/services/workflows/engine_test.go index b8d5a9591e..2d862823cf 100644 --- a/core/services/workflows/engine_test.go +++ b/core/services/workflows/engine_test.go @@ -11,8 +11,10 @@ import ( "github.com/shopspring/decimal" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" "github.com/smartcontractkit/chainlink-common/pkg/capabilities" + capabilitiespb "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" "github.com/smartcontractkit/chainlink-common/pkg/values" "github.com/smartcontractkit/chainlink-common/pkg/workflows" @@ -22,6 +24,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" + "github.com/smartcontractkit/chainlink/v2/core/services/registrysyncer" "github.com/smartcontractkit/chainlink/v2/core/services/workflows/store" ) @@ -101,7 +104,7 @@ func newTestDBStore(t *testing.T, clock clockwork.Clock) store.Store { type testConfigProvider struct { localNode func(ctx context.Context) (capabilities.Node, error) - configForCapability func(ctx context.Context, capabilityID string, donID uint32) (capabilities.CapabilityConfiguration, error) + configForCapability func(ctx context.Context, capabilityID string, donID uint32) (registrysyncer.CapabilityConfiguration, error) } func (t testConfigProvider) LocalNode(ctx context.Context) (capabilities.Node, error) { @@ -118,12 +121,12 @@ func (t testConfigProvider) LocalNode(ctx context.Context) (capabilities.Node, e }, nil } -func (t testConfigProvider) ConfigForCapability(ctx context.Context, capabilityID string, donID uint32) (capabilities.CapabilityConfiguration, error) { +func (t testConfigProvider) ConfigForCapability(ctx context.Context, capabilityID string, donID uint32) (registrysyncer.CapabilityConfiguration, error) { if t.configForCapability != nil { return t.configForCapability(ctx, capabilityID, donID) } - return capabilities.CapabilityConfiguration{DefaultConfig: values.EmptyMap()}, nil + return registrysyncer.CapabilityConfiguration{}, nil } // newTestEngine creates a new engine with some test defaults. @@ -220,20 +223,20 @@ func (m *mockCapability) UnregisterFromWorkflow(ctx context.Context, request cap type mockTriggerCapability struct { capabilities.CapabilityInfo - triggerEvent *capabilities.CapabilityResponse - ch chan capabilities.CapabilityResponse + triggerEvent *capabilities.TriggerResponse + ch chan capabilities.TriggerResponse } var _ capabilities.TriggerCapability = (*mockTriggerCapability)(nil) -func (m *mockTriggerCapability) RegisterTrigger(ctx context.Context, req capabilities.CapabilityRequest) (<-chan capabilities.CapabilityResponse, error) { +func (m *mockTriggerCapability) RegisterTrigger(ctx context.Context, req capabilities.TriggerRegistrationRequest) (<-chan capabilities.TriggerResponse, error) { if m.triggerEvent != nil { m.ch <- *m.triggerEvent } return m.ch, nil } -func (m *mockTriggerCapability) UnregisterTrigger(ctx context.Context, req capabilities.CapabilityRequest) error { +func (m *mockTriggerCapability) UnregisterTrigger(ctx context.Context, req capabilities.TriggerRegistrationRequest) error { return nil } @@ -272,8 +275,13 @@ func TestEngineWithHardcodedWorkflow(t *testing.T) { servicetest.Run(t, eng) eid := getExecutionId(t, eng, testHooks) - assert.Equal(t, cr, <-target1.response) - assert.Equal(t, cr, <-target2.response) + resp1 := <-target1.response + assert.NoError(t, resp1.Err) + assert.Equal(t, cr.Event.Outputs, resp1.Value) + + resp2 := <-target2.response + assert.NoError(t, resp2.Err) + assert.Equal(t, cr.Event.Outputs, resp2.Value) state, err := eng.executionStates.Get(ctx, eid) require.NoError(t, err) @@ -324,14 +332,14 @@ targets: ` ) -func mockTrigger(t *testing.T) (capabilities.TriggerCapability, capabilities.CapabilityResponse) { +func mockTrigger(t *testing.T) (capabilities.TriggerCapability, capabilities.TriggerResponse) { mt := &mockTriggerCapability{ CapabilityInfo: capabilities.MustNewCapabilityInfo( "mercury-trigger@1.0.0", capabilities.CapabilityTypeTrigger, "issues a trigger when a mercury report is received.", ), - ch: make(chan capabilities.CapabilityResponse, 10), + ch: make(chan capabilities.TriggerResponse, 10), } resp, err := values.NewMap(map[string]any{ "123": decimal.NewFromFloat(1.00), @@ -339,11 +347,13 @@ func mockTrigger(t *testing.T) (capabilities.TriggerCapability, capabilities.Cap "789": decimal.NewFromFloat(1.50), }) require.NoError(t, err) - cr := capabilities.CapabilityResponse{ - Value: resp, + tr := capabilities.TriggerResponse{ + Event: capabilities.TriggerEvent{ + Outputs: resp, + }, } - mt.triggerEvent = &cr - return mt, cr + mt.triggerEvent = &tr + return mt, tr } func mockNoopTrigger(t *testing.T) capabilities.TriggerCapability { @@ -353,7 +363,7 @@ func mockNoopTrigger(t *testing.T) capabilities.TriggerCapability { capabilities.CapabilityTypeTrigger, "issues a trigger when a mercury report is received.", ), - ch: make(chan capabilities.CapabilityResponse, 10), + ch: make(chan capabilities.TriggerResponse, 10), } return mt } @@ -548,7 +558,7 @@ func TestEngine_MultiStepDependencies(t *testing.T) { ctx := testutils.Context(t) reg := coreCap.NewRegistry(logger.TestLogger(t)) - trigger, cr := mockTrigger(t) + trigger, tr := mockTrigger(t) require.NoError(t, reg.Add(ctx, trigger)) require.NoError(t, reg.Add(ctx, mockConsensus())) @@ -575,9 +585,10 @@ func TestEngine_MultiStepDependencies(t *testing.T) { obs := unw.(map[string]any)["observations"] assert.Len(t, obs, 2) - tunw, err := values.Unwrap(cr.Value) require.NoError(t, err) - assert.Equal(t, obs.([]any)[0], tunw) + uo, err := values.Unwrap(tr.Event.Outputs) + require.NoError(t, err) + assert.Equal(t, obs.([]any)[0].(map[string]any), uo) o, err := values.Unwrap(out) require.NoError(t, err) @@ -1028,11 +1039,9 @@ func TestEngine_MergesWorkflowConfigAndCRConfig(t *testing.T) { simpleWorkflow, ) reg.SetLocalRegistry(testConfigProvider{ - configForCapability: func(ctx context.Context, capabilityID string, donID uint32) (capabilities.CapabilityConfiguration, error) { + configForCapability: func(ctx context.Context, capabilityID string, donID uint32) (registrysyncer.CapabilityConfiguration, error) { if capabilityID != writeID { - return capabilities.CapabilityConfiguration{ - DefaultConfig: values.EmptyMap(), - }, nil + return registrysyncer.CapabilityConfiguration{}, nil } cm, err := values.WrapMap(map[string]any{ @@ -1040,12 +1049,15 @@ func TestEngine_MergesWorkflowConfigAndCRConfig(t *testing.T) { "schedule": "allAtOnce", }) if err != nil { - return capabilities.CapabilityConfiguration{}, err + return registrysyncer.CapabilityConfiguration{}, err } - return capabilities.CapabilityConfiguration{ - DefaultConfig: cm, - }, nil + cb, err := proto.Marshal(&capabilitiespb.CapabilityConfig{ + DefaultConfig: values.ProtoMap(cm), + }) + return registrysyncer.CapabilityConfiguration{ + Config: cb, + }, err }, }) @@ -1063,3 +1075,54 @@ func TestEngine_MergesWorkflowConfigAndCRConfig(t *testing.T) { assert.Equal(t, m.(map[string]any)["deltaStage"], "1s") assert.Equal(t, m.(map[string]any)["schedule"], "allAtOnce") } + +func TestEngine_HandlesNilConfigOnchain(t *testing.T) { + ctx := testutils.Context(t) + reg := coreCap.NewRegistry(logger.TestLogger(t)) + + trigger, _ := mockTrigger(t) + + require.NoError(t, reg.Add(ctx, trigger)) + require.NoError(t, reg.Add(ctx, mockConsensus())) + writeID := "write_polygon-testnet-mumbai@1.0.0" + + gotConfig := values.EmptyMap() + target := newMockCapability( + // Create a remote capability so we don't use the local transmission protocol. + capabilities.MustNewRemoteCapabilityInfo( + writeID, + capabilities.CapabilityTypeTarget, + "a write capability targeting polygon testnet", + &capabilities.DON{ID: 1}, + ), + func(req capabilities.CapabilityRequest) (capabilities.CapabilityResponse, error) { + gotConfig = req.Config + + return capabilities.CapabilityResponse{ + Value: req.Inputs, + }, nil + }, + ) + require.NoError(t, reg.Add(ctx, target)) + + eng, testHooks := newTestEngine( + t, + reg, + simpleWorkflow, + ) + reg.SetLocalRegistry(testConfigProvider{}) + + servicetest.Run(t, eng) + + eid := getExecutionId(t, eng, testHooks) + + state, err := eng.executionStates.Get(ctx, eid) + require.NoError(t, err) + + assert.Equal(t, state.Status, store.StatusCompleted) + + m, err := values.Unwrap(gotConfig) + require.NoError(t, err) + // The write target config contains three keys + assert.Len(t, m.(map[string]any), 3) +} diff --git a/core/services/workflows/models.go b/core/services/workflows/models.go index 8d970dfa94..1ff77225c4 100644 --- a/core/services/workflows/models.go +++ b/core/services/workflows/models.go @@ -3,6 +3,7 @@ package workflows import ( "errors" "fmt" + "sync/atomic" "github.com/dominikbraun/graph" @@ -86,7 +87,8 @@ type step struct { type triggerCapability struct { workflows.StepDefinition trigger capabilities.TriggerCapability - config *values.Map + + config atomic.Pointer[values.Map] } func Parse(yamlWorkflow string) (*workflow, error) { diff --git a/core/services/workflows/state.go b/core/services/workflows/state.go index 6fc61af395..cd4247d0ee 100644 --- a/core/services/workflows/state.go +++ b/core/services/workflows/state.go @@ -17,12 +17,10 @@ func copyState(es store.WorkflowExecution) store.WorkflowExecution { for ref, step := range es.Steps { var mval *values.Map if step.Inputs != nil { - mp := values.Proto(step.Inputs).GetMapValue() - mval = values.FromMapValueProto(mp) + mval = step.Inputs.CopyMap() } - op := values.Proto(step.Outputs.Value) - copiedov := values.FromProto(op) + copiedov := values.Copy(step.Outputs.Value) newState := &store.WorkflowExecutionStep{ ExecutionID: step.ExecutionID, diff --git a/core/services/workflows/store/store_db.go b/core/services/workflows/store/store_db.go index e1d0862905..80ecfbb2d6 100644 --- a/core/services/workflows/store/store_db.go +++ b/core/services/workflows/store/store_db.go @@ -127,7 +127,10 @@ func stepToState(step workflowStepRow) (*WorkflowExecutionStep, error) { return nil, err } - inputs = values.FromMapValueProto(vmProto) + inputs, err = values.FromMapValueProto(vmProto) + if err != nil { + return nil, err + } } var ( @@ -146,7 +149,10 @@ func stepToState(step workflowStepRow) (*WorkflowExecutionStep, error) { return nil, err } - outputs = values.FromProto(vProto) + outputs, err = values.FromProto(vProto) + if err != nil { + return nil, err + } } return &WorkflowExecutionStep{ diff --git a/core/store/migrate/migrate_test.go b/core/store/migrate/migrate_test.go index 4138e0d266..adbc0ca2f6 100644 --- a/core/store/migrate/migrate_test.go +++ b/core/store/migrate/migrate_test.go @@ -618,3 +618,14 @@ func BenchmarkBackfillingRecordsWithMigration202(b *testing.B) { require.NoError(b, err) } } + +func TestRollback_247_TxStateEnumUpdate(t *testing.T) { + ctx := testutils.Context(t) + _, db := heavyweight.FullTestDBV2(t, nil) + p, err := migrate.NewProvider(ctx, db.DB) + require.NoError(t, err) + _, err = p.DownTo(ctx, 54) + require.NoError(t, err) + _, err = p.UpTo(ctx, 247) + require.NoError(t, err) +} diff --git a/core/store/migrate/migrations/0248_add_tx_finalized_state.sql b/core/store/migrate/migrations/0248_add_tx_finalized_state.sql new file mode 100644 index 0000000000..dcfe8eec73 --- /dev/null +++ b/core/store/migrate/migrations/0248_add_tx_finalized_state.sql @@ -0,0 +1,135 @@ +-- +goose Up +-- Creating new column and enum instead of just adding new value to the existing enum so the migration changes match the rollback logic +-- Otherwise, migration will complain about mismatching column order + +-- +goose StatementBegin +-- Rename the existing enum with finalized state to mark it as old +ALTER TYPE evm.txes_state RENAME TO txes_state_old; + +-- Create new enum without finalized state +CREATE TYPE evm.txes_state AS ENUM ( + 'unstarted', + 'in_progress', + 'fatal_error', + 'unconfirmed', + 'confirmed_missing_receipt', + 'confirmed', + 'finalized' +); + +-- Add a new state column with the new enum type to the txes table +ALTER TABLE evm.txes ADD COLUMN state_new evm.txes_state; + +-- Copy data from the old column to the new +UPDATE evm.txes SET state_new = state::text::evm.txes_state; + +-- Drop constraints referring to old enum type on the old state column +ALTER TABLE evm.txes ALTER COLUMN state DROP DEFAULT; +ALTER TABLE evm.txes DROP CONSTRAINT chk_eth_txes_fsm; +DROP INDEX IF EXISTS idx_eth_txes_state_from_address_evm_chain_id; +DROP INDEX IF EXISTS idx_eth_txes_min_unconfirmed_nonce_for_key_evm_chain_id; +DROP INDEX IF EXISTS idx_only_one_in_progress_tx_per_account_id_per_evm_chain_id; +DROP INDEX IF EXISTS idx_eth_txes_unstarted_subject_id_evm_chain_id; + +-- Drop the old state column +ALTER TABLE evm.txes DROP state; + +-- Drop the old enum type +DROP TYPE evm.txes_state_old; + +-- Rename the new column name state to replace the old column +ALTER TABLE evm.txes RENAME state_new TO state; + +-- Reset the state column's default +ALTER TABLE evm.txes ALTER COLUMN state SET DEFAULT 'unstarted'::evm.txes_state, ALTER COLUMN state SET NOT NULL; + +-- Recreate constraint with finalized state +ALTER TABLE evm.txes ADD CONSTRAINT chk_eth_txes_fsm CHECK ( + state = 'unstarted'::evm.txes_state AND nonce IS NULL AND error IS NULL AND broadcast_at IS NULL AND initial_broadcast_at IS NULL + OR + state = 'in_progress'::evm.txes_state AND nonce IS NOT NULL AND error IS NULL AND broadcast_at IS NULL AND initial_broadcast_at IS NULL + OR + state = 'fatal_error'::evm.txes_state AND error IS NOT NULL + OR + state = 'unconfirmed'::evm.txes_state AND nonce IS NOT NULL AND error IS NULL AND broadcast_at IS NOT NULL AND initial_broadcast_at IS NOT NULL + OR + state = 'confirmed'::evm.txes_state AND nonce IS NOT NULL AND error IS NULL AND broadcast_at IS NOT NULL AND initial_broadcast_at IS NOT NULL + OR + state = 'confirmed_missing_receipt'::evm.txes_state AND nonce IS NOT NULL AND error IS NULL AND broadcast_at IS NOT NULL AND initial_broadcast_at IS NOT NULL + OR + state = 'finalized'::evm.txes_state AND nonce IS NOT NULL AND error IS NULL AND broadcast_at IS NOT NULL AND initial_broadcast_at IS NOT NULL +) NOT VALID; + +-- Recreate index to include finalized state +CREATE INDEX idx_eth_txes_state_from_address_evm_chain_id ON evm.txes(evm_chain_id, from_address, state) WHERE state <> 'confirmed'::evm.txes_state AND state <> 'finalized'::evm.txes_state; +CREATE INDEX idx_eth_txes_min_unconfirmed_nonce_for_key_evm_chain_id ON evm.txes(evm_chain_id, from_address, nonce) WHERE state = 'unconfirmed'::evm.txes_state; +CREATE UNIQUE INDEX idx_only_one_in_progress_tx_per_account_id_per_evm_chain_id ON evm.txes(evm_chain_id, from_address) WHERE state = 'in_progress'::evm.txes_state; +CREATE INDEX idx_eth_txes_unstarted_subject_id_evm_chain_id ON evm.txes(evm_chain_id, subject, id) WHERE subject IS NOT NULL AND state = 'unstarted'::evm.txes_state; +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin + +-- Rename the existing enum with finalized state to mark it as old +ALTER TYPE evm.txes_state RENAME TO txes_state_old; + +-- Create new enum without finalized state +CREATE TYPE evm.txes_state AS ENUM ( + 'unstarted', + 'in_progress', + 'fatal_error', + 'unconfirmed', + 'confirmed_missing_receipt', + 'confirmed' +); + +-- Add a new state column with the new enum type to the txes table +ALTER TABLE evm.txes ADD COLUMN state_new evm.txes_state; + +-- Update all transactions with finalized state to confirmed in the old state column +UPDATE evm.txes SET state = 'confirmed'::evm.txes_state_old WHERE state = 'finalized'::evm.txes_state_old; + +-- Copy data from the old column to the new +UPDATE evm.txes SET state_new = state::text::evm.txes_state; + +-- Drop constraints referring to old enum type on the old state column +ALTER TABLE evm.txes ALTER COLUMN state DROP DEFAULT; +ALTER TABLE evm.txes DROP CONSTRAINT chk_eth_txes_fsm; +DROP INDEX IF EXISTS idx_eth_txes_state_from_address_evm_chain_id; +DROP INDEX IF EXISTS idx_eth_txes_min_unconfirmed_nonce_for_key_evm_chain_id; +DROP INDEX IF EXISTS idx_only_one_in_progress_tx_per_account_id_per_evm_chain_id; +DROP INDEX IF EXISTS idx_eth_txes_unstarted_subject_id_evm_chain_id; + +-- Drop the old state column +ALTER TABLE evm.txes DROP state; + +-- Drop the old enum type +DROP TYPE evm.txes_state_old; + +-- Rename the new column name state to replace the old column +ALTER TABLE evm.txes RENAME state_new TO state; + +-- Reset the state column's default +ALTER TABLE evm.txes ALTER COLUMN state SET DEFAULT 'unstarted'::evm.txes_state, ALTER COLUMN state SET NOT NULL; + +-- Recereate constraint without finalized state +ALTER TABLE evm.txes ADD CONSTRAINT chk_eth_txes_fsm CHECK ( + state = 'unstarted'::evm.txes_state AND nonce IS NULL AND error IS NULL AND broadcast_at IS NULL AND initial_broadcast_at IS NULL + OR + state = 'in_progress'::evm.txes_state AND nonce IS NOT NULL AND error IS NULL AND broadcast_at IS NULL AND initial_broadcast_at IS NULL + OR + state = 'fatal_error'::evm.txes_state AND error IS NOT NULL + OR + state = 'unconfirmed'::evm.txes_state AND nonce IS NOT NULL AND error IS NULL AND broadcast_at IS NOT NULL AND initial_broadcast_at IS NOT NULL + OR + state = 'confirmed'::evm.txes_state AND nonce IS NOT NULL AND error IS NULL AND broadcast_at IS NOT NULL AND initial_broadcast_at IS NOT NULL + OR + state = 'confirmed_missing_receipt'::evm.txes_state AND nonce IS NOT NULL AND error IS NULL AND broadcast_at IS NOT NULL AND initial_broadcast_at IS NOT NULL +) NOT VALID; + +-- Recreate index with new enum type +CREATE INDEX idx_eth_txes_state_from_address_evm_chain_id ON evm.txes(evm_chain_id, from_address, state) WHERE state <> 'confirmed'::evm.txes_state; +CREATE INDEX idx_eth_txes_min_unconfirmed_nonce_for_key_evm_chain_id ON evm.txes(evm_chain_id, from_address, nonce) WHERE state = 'unconfirmed'::evm.txes_state; +CREATE UNIQUE INDEX idx_only_one_in_progress_tx_per_account_id_per_evm_chain_id ON evm.txes(evm_chain_id, from_address) WHERE state = 'in_progress'::evm.txes_state; +CREATE INDEX idx_eth_txes_unstarted_subject_id_evm_chain_id ON evm.txes(evm_chain_id, subject, id) WHERE subject IS NOT NULL AND state = 'unstarted'::evm.txes_state; +-- +goose StatementEnd diff --git a/core/store/migrate/migrations/0249_registry_syncer_state.sql b/core/store/migrate/migrations/0249_registry_syncer_state.sql new file mode 100644 index 0000000000..e34a3790a3 --- /dev/null +++ b/core/store/migrate/migrations/0249_registry_syncer_state.sql @@ -0,0 +1,11 @@ +-- +goose Up +CREATE TABLE registry_syncer_states ( + id SERIAL PRIMARY KEY, + data JSONB NOT NULL, + data_hash TEXT NOT NULL UNIQUE, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); +-- +goose Down +-- +goose StatementBegin +DROP TABLE registry_syncer_states; +-- +goose StatementEnd diff --git a/core/store/migrate/migrations/0250_ccip_token_prices_fix.sql b/core/store/migrate/migrations/0250_ccip_token_prices_fix.sql new file mode 100644 index 0000000000..6c6cf02b43 --- /dev/null +++ b/core/store/migrate/migrations/0250_ccip_token_prices_fix.sql @@ -0,0 +1,49 @@ +-- +goose Up + +-- We need to re-create tables from scratch because of the unique constraint on tokens and chains selectors +DROP TABLE ccip.observed_token_prices; +DROP TABLE ccip.observed_gas_prices; + +CREATE TABLE ccip.observed_token_prices +( + chain_selector NUMERIC(20, 0) NOT NULL, + token_addr BYTEA NOT NULL, + token_price NUMERIC(78, 0) NOT NULL, + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + PRIMARY KEY (chain_selector, token_addr) +); + +CREATE TABLE ccip.observed_gas_prices +( + chain_selector NUMERIC(20, 0) NOT NULL, + source_chain_selector NUMERIC(20, 0) NOT NULL, + gas_price NUMERIC(78, 0) NOT NULL, + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + PRIMARY KEY (chain_selector, source_chain_selector) +); + +-- +goose Down +DROP TABLE ccip.observed_token_prices; +DROP TABLE ccip.observed_gas_prices; + +-- Restore state from migration 0236_ccip_prices_cache.sql +CREATE TABLE ccip.observed_gas_prices +( + chain_selector NUMERIC(20, 0) NOT NULL, + job_id INTEGER NOT NULL, + source_chain_selector NUMERIC(20, 0) NOT NULL, + gas_price NUMERIC(78, 0) NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE TABLE ccip.observed_token_prices +( + chain_selector NUMERIC(20, 0) NOT NULL, + job_id INTEGER NOT NULL, + token_addr BYTEA NOT NULL, + token_price NUMERIC(78, 0) NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE INDEX idx_ccip_gas_prices_chain_gas_price_timestamp ON ccip.observed_gas_prices (chain_selector, source_chain_selector, created_at DESC); +CREATE INDEX idx_ccip_token_prices_token_price_timestamp ON ccip.observed_token_prices (chain_selector, token_addr, created_at DESC); diff --git a/core/store/migrate/migrations/0251_add_don_id_to_channel_definitions.sql b/core/store/migrate/migrations/0251_add_don_id_to_channel_definitions.sql new file mode 100644 index 0000000000..9c77592b0a --- /dev/null +++ b/core/store/migrate/migrations/0251_add_don_id_to_channel_definitions.sql @@ -0,0 +1,13 @@ +-- +goose Up +DELETE FROM channel_definitions; +ALTER TABLE channel_definitions DROP CONSTRAINT channel_definitions_pkey; +ALTER TABLE channel_definitions ADD COLUMN don_id bigint, ADD COLUMN version bigint; +ALTER TABLE channel_definitions RENAME COLUMN evm_chain_id TO chain_selector; +ALTER TABLE channel_definitions ALTER COLUMN chain_selector TYPE NUMERIC(20, 0); +ALTER TABLE channel_definitions ADD PRIMARY KEY (chain_selector, addr, don_id); + +-- +goose Down +ALTER TABLE channel_definitions DROP COLUMN don_id, DROP COLUMN version; +ALTER TABLE channel_definitions RENAME COLUMN chain_selector TO evm_chain_id; +ALTER TABLE channel_definitions ALTER COLUMN evm_chain_id TYPE bigint; +ALTER TABLE channel_definitions ADD PRIMARY KEY (evm_chain_id, addr); diff --git a/core/utils/http/http.go b/core/utils/http/http.go index 3336ac9f42..0c713f9662 100644 --- a/core/utils/http/http.go +++ b/core/utils/http/http.go @@ -7,7 +7,7 @@ import ( "net/url" "time" - "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink-common/pkg/logger" ) type httpClientConfig interface { @@ -38,9 +38,13 @@ func newDefaultTransport() *http.Transport { return t } +type HTTPClient interface { + Do(req *http.Request) (*http.Response, error) +} + // HTTPRequest holds the request and config struct for a http request type HTTPRequest struct { - Client *http.Client + Client HTTPClient Request *http.Request Config HTTPRequestConfig Logger logger.Logger @@ -48,35 +52,46 @@ type HTTPRequest struct { // HTTPRequestConfig holds the configurable settings for a http request type HTTPRequestConfig struct { + // SizeLimit in bytes SizeLimit int64 } -// SendRequest sends a HTTPRequest, -// returns a body, status code, and error. -func (h *HTTPRequest) SendRequest() (responseBody []byte, statusCode int, headers http.Header, err error) { +// SendRequestReader allows for streaming the body directly and does not read +// it all into memory +// +// CALLER IS RESPONSIBLE FOR CLOSING RETURNED RESPONSE BODY +func (h *HTTPRequest) SendRequestReader() (responseBody io.ReadCloser, statusCode int, headers http.Header, err error) { start := time.Now() - r, err := h.Client.Do(h.Request) if err != nil { - h.Logger.Tracew("http adapter got error", "err", err) + logger.Sugared(h.Logger).Tracew("http adapter got error", "err", err) return nil, 0, nil, err } - defer logger.Sugared(h.Logger).ErrorIfFn(r.Body.Close, "Error closing SendRequest response body") statusCode = r.StatusCode elapsed := time.Since(start) - h.Logger.Tracew(fmt.Sprintf("http adapter got %v in %s", statusCode, elapsed), "statusCode", statusCode, "timeElapsedSeconds", elapsed) + logger.Sugared(h.Logger).Tracew(fmt.Sprintf("http adapter got %v in %s", statusCode, elapsed), "statusCode", statusCode, "timeElapsedSeconds", elapsed) source := http.MaxBytesReader(nil, r.Body, h.Config.SizeLimit) - bytes, err := io.ReadAll(source) + + return source, statusCode, r.Header, nil +} + +// SendRequest sends a HTTPRequest, +// returns a body, status code, and error. +func (h *HTTPRequest) SendRequest() (responseBody []byte, statusCode int, headers http.Header, err error) { + start := time.Now() + + source, statusCode, headers, err := h.SendRequestReader() if err != nil { - h.Logger.Errorw("http adapter error reading body", "err", err) - return nil, statusCode, nil, err + return nil, statusCode, headers, err } - elapsed = time.Since(start) - h.Logger.Tracew(fmt.Sprintf("http adapter finished after %s", elapsed), "statusCode", statusCode, "timeElapsedSeconds", elapsed) + defer logger.Sugared(h.Logger).ErrorIfFn(source.Close, "Error closing SendRequest response body") + bytes, err := io.ReadAll(source) + elapsed := time.Since(start) + logger.Sugared(h.Logger).Tracew(fmt.Sprintf("http adapter finished after %s", elapsed), "statusCode", statusCode, "timeElapsedSeconds", elapsed) responseBody = bytes - return responseBody, statusCode, r.Header, nil + return responseBody, statusCode, headers, nil } diff --git a/core/utils/http/http_allowed_ips.go b/core/utils/http/http_allowed_ips.go index 6432e4ff91..2b77e89c7d 100644 --- a/core/utils/http/http_allowed_ips.go +++ b/core/utils/http/http_allowed_ips.go @@ -9,7 +9,7 @@ import ( "github.com/pkg/errors" "go.uber.org/multierr" - "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink-common/pkg/logger" ) var privateIPBlocks []*net.IPNet diff --git a/core/utils/utils.go b/core/utils/utils.go index d076284112..237f6a4358 100644 --- a/core/utils/utils.go +++ b/core/utils/utils.go @@ -481,6 +481,23 @@ func NewRedialBackoff() backoff.Backoff { } } +func NewHTTPFetchBackoff() backoff.Backoff { + return backoff.Backoff{ + Min: 100 * time.Millisecond, + Max: 15 * time.Second, + Jitter: true, + } +} + +// NewDBBackoff is a standard backoff to use for database connection issues +func NewDBBackoff() backoff.Backoff { + return backoff.Backoff{ + Min: 100 * time.Millisecond, + Max: 5 * time.Second, + Jitter: true, + } +} + // KeyedMutex allows to lock based on particular values type KeyedMutex struct { mutexes sync.Map diff --git a/core/web/health_controller.go b/core/web/health_controller.go index bd775671d7..ee08c39fcf 100644 --- a/core/web/health_controller.go +++ b/core/web/health_controller.go @@ -69,6 +69,8 @@ func (hc *HealthController) Readyz(c *gin.Context) { } func (hc *HealthController) Health(c *gin.Context) { + _, failing := c.GetQuery("failing") + status := http.StatusOK checker := hc.App.GetHealthChecker() @@ -89,6 +91,8 @@ func (hc *HealthController) Health(c *gin.Context) { if err != nil { status = HealthStatusFailing output = err.Error() + } else if failing { + continue // omit from returned data } checks = append(checks, presenters.Check{ diff --git a/core/web/health_controller_test.go b/core/web/health_controller_test.go index 21da1fb2e4..14367b1e4b 100644 --- a/core/web/health_controller_test.go +++ b/core/web/health_controller_test.go @@ -97,6 +97,12 @@ var ( bodyHTML string //go:embed testdata/body/health.txt bodyTXT string + //go:embed testdata/body/health-failing.json + bodyJSONFailing string + //go:embed testdata/body/health-failing.html + bodyHTMLFailing string + //go:embed testdata/body/health-failing.txt + bodyTXTFailing string ) func TestHealthController_Health_body(t *testing.T) { @@ -111,6 +117,12 @@ func TestHealthController_Health_body(t *testing.T) { {"html", "/health", map[string]string{"Accept": gin.MIMEHTML}, bodyHTML}, {"text", "/health", map[string]string{"Accept": gin.MIMEPlain}, bodyTXT}, {".txt", "/health.txt", nil, bodyTXT}, + + {"default-failing", "/health?failing", nil, bodyJSONFailing}, + {"json-failing", "/health?failing", map[string]string{"Accept": gin.MIMEJSON}, bodyJSONFailing}, + {"html-failing", "/health?failing", map[string]string{"Accept": gin.MIMEHTML}, bodyHTMLFailing}, + {"text-failing", "/health?failing", map[string]string{"Accept": gin.MIMEPlain}, bodyTXTFailing}, + {".txt-failing", "/health.txt?failing", nil, bodyTXTFailing}, } { t.Run(tc.name, func(t *testing.T) { app := cltest.NewApplicationWithKey(t) diff --git a/core/web/presenters/aptos_key.go b/core/web/presenters/aptos_key.go index 6460c325f9..8c0c09ed10 100644 --- a/core/web/presenters/aptos_key.go +++ b/core/web/presenters/aptos_key.go @@ -5,7 +5,8 @@ import "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/apt // AptosKeyResource represents a Aptos key JSONAPI resource. type AptosKeyResource struct { JAID - PubKey string `json:"publicKey"` + Account string `json:"account"` + PubKey string `json:"publicKey"` } // GetName implements the api2go EntityNamer interface @@ -15,8 +16,9 @@ func (AptosKeyResource) GetName() string { func NewAptosKeyResource(key aptoskey.Key) *AptosKeyResource { r := &AptosKeyResource{ - JAID: JAID{ID: key.ID()}, - PubKey: key.PublicKeyStr(), + JAID: JAID{ID: key.ID()}, + Account: key.Account(), + PubKey: key.PublicKeyStr(), } return r diff --git a/core/web/resolver/testdata/config-full.toml b/core/web/resolver/testdata/config-full.toml index b546042756..567768c80b 100644 --- a/core/web/resolver/testdata/config-full.toml +++ b/core/web/resolver/testdata/config-full.toml @@ -383,6 +383,7 @@ L2Full = '(: |^)l2 full' TransactionAlreadyMined = '(: |^)transaction already mined' Fatal = '(: |^)fatal' ServiceUnavailable = '(: |^)service unavailable' +TooManyResults = '(: |^)too many results' [EVM.OCR] ContractConfirmations = 11 diff --git a/core/web/testdata/body/health-failing.html b/core/web/testdata/body/health-failing.html new file mode 100644 index 0000000000..6b667a3ba6 --- /dev/null +++ b/core/web/testdata/body/health-failing.html @@ -0,0 +1,47 @@ + +
+ EVM +
+ 0 +
+ HeadTracker +
+ HeadListener +
Listener is not connected
+
+
+
+
diff --git a/core/web/testdata/body/health-failing.json b/core/web/testdata/body/health-failing.json new file mode 100644 index 0000000000..185b98b8da --- /dev/null +++ b/core/web/testdata/body/health-failing.json @@ -0,0 +1 @@ +{"data":[{"type":"checks","id":"EVM.0.HeadTracker.HeadListener","attributes":{"name":"EVM.0.HeadTracker.HeadListener","status":"failing","output":"Listener is not connected"}}]} diff --git a/core/web/testdata/body/health-failing.txt b/core/web/testdata/body/health-failing.txt new file mode 100644 index 0000000000..c6b948c3f9 --- /dev/null +++ b/core/web/testdata/body/health-failing.txt @@ -0,0 +1,2 @@ +! EVM.0.HeadTracker.HeadListener + Listener is not connected diff --git a/core/web/testdata/body/health.html b/core/web/testdata/body/health.html index 2a1b222753..d2b6db906b 100644 --- a/core/web/testdata/body/health.html +++ b/core/web/testdata/body/health.html @@ -63,12 +63,18 @@
Confirmer
+
+ Finalizer +
WrappedEvmEstimator
+
+ HeadReporter +
JobSpawner
@@ -96,9 +102,6 @@ BridgeCache -
- PromReporter -
TelemetryManager
diff --git a/core/web/testdata/body/health.json b/core/web/testdata/body/health.json index 10415c0abd..81ed7ff6d1 100644 --- a/core/web/testdata/body/health.json +++ b/core/web/testdata/body/health.json @@ -90,6 +90,15 @@ "output": "" } }, + { + "type": "checks", + "id": "EVM.0.Txm.Finalizer", + "attributes": { + "name": "EVM.0.Txm.Finalizer", + "status": "passing", + "output": "" + } + }, { "type": "checks", "id": "EVM.0.Txm.WrappedEvmEstimator", @@ -99,6 +108,15 @@ "output": "" } }, + { + "type": "checks", + "id": "HeadReporter", + "attributes": { + "name": "HeadReporter", + "status": "passing", + "output": "" + } + }, { "type": "checks", "id": "JobSpawner", @@ -162,15 +180,6 @@ "output": "" } }, - { - "type": "checks", - "id": "PromReporter", - "attributes": { - "name": "PromReporter", - "status": "passing", - "output": "" - } - }, { "type": "checks", "id": "TelemetryManager", diff --git a/core/web/testdata/body/health.txt b/core/web/testdata/body/health.txt index 09c8cff6c2..6b165d26d9 100644 --- a/core/web/testdata/body/health.txt +++ b/core/web/testdata/body/health.txt @@ -9,7 +9,9 @@ ok EVM.0.Txm ok EVM.0.Txm.BlockHistoryEstimator ok EVM.0.Txm.Broadcaster ok EVM.0.Txm.Confirmer +ok EVM.0.Txm.Finalizer ok EVM.0.Txm.WrappedEvmEstimator +ok HeadReporter ok JobSpawner ok Mailbox.Monitor ok Mercury.WSRPCPool @@ -17,5 +19,4 @@ ok Mercury.WSRPCPool.CacheSet ok PipelineORM ok PipelineRunner ok PipelineRunner.BridgeCache -ok PromReporter ok TelemetryManager diff --git a/dashboard-lib/atlas-don/component.go b/dashboard-lib/atlas-don/component.go index 39218c7aea..beda6f48ec 100644 --- a/dashboard-lib/atlas-don/component.go +++ b/dashboard-lib/atlas-don/component.go @@ -2,6 +2,7 @@ package atlas_don import ( "fmt" + "github.com/K-Phoen/grabana/dashboard" "github.com/K-Phoen/grabana/row" "github.com/K-Phoen/grabana/stat" diff --git a/dashboard-lib/ccip-load-test-view/component.go b/dashboard-lib/ccip-load-test-view/component.go index 4fa2880d0e..700a0a5107 100644 --- a/dashboard-lib/ccip-load-test-view/component.go +++ b/dashboard-lib/ccip-load-test-view/component.go @@ -3,6 +3,7 @@ package ccip_load_test_view import ( "encoding/json" "fmt" + "github.com/K-Phoen/grabana/dashboard" "github.com/K-Phoen/grabana/logs" "github.com/K-Phoen/grabana/row" diff --git a/dashboard-lib/config.go b/dashboard-lib/config.go index 2e0b9cad99..386082dae0 100644 --- a/dashboard-lib/config.go +++ b/dashboard-lib/config.go @@ -2,9 +2,10 @@ package dashboard_lib import ( "encoding/base64" - "github.com/pkg/errors" "os" "strings" + + "github.com/pkg/errors" ) type EnvConfig struct { diff --git a/dashboard-lib/core-don/component.go b/dashboard-lib/core-don/component.go index 24173fb6cc..0589ab5cf8 100644 --- a/dashboard-lib/core-don/component.go +++ b/dashboard-lib/core-don/component.go @@ -2,6 +2,7 @@ package core_don import ( "fmt" + "github.com/K-Phoen/grabana/dashboard" "github.com/K-Phoen/grabana/gauge" "github.com/K-Phoen/grabana/row" diff --git a/dashboard-lib/core-ocrv2-ccip/component.go b/dashboard-lib/core-ocrv2-ccip/component.go index 837f693fcc..56b35587e1 100644 --- a/dashboard-lib/core-ocrv2-ccip/component.go +++ b/dashboard-lib/core-ocrv2-ccip/component.go @@ -2,6 +2,7 @@ package core_ocrv2_ccip import ( "fmt" + "github.com/K-Phoen/grabana/dashboard" "github.com/K-Phoen/grabana/row" "github.com/K-Phoen/grabana/target/prometheus" diff --git a/dashboard-lib/dashboard.go b/dashboard-lib/dashboard.go index 70892586bb..92c4691a5e 100644 --- a/dashboard-lib/dashboard.go +++ b/dashboard-lib/dashboard.go @@ -4,11 +4,12 @@ import ( "context" "encoding/json" "fmt" + "net/http" + "os" + "github.com/K-Phoen/grabana" "github.com/K-Phoen/grabana/dashboard" "github.com/pkg/errors" - "net/http" - "os" ) type Dashboard struct { diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 10e2519049..dd4bfa50c8 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -2599,7 +2599,7 @@ Enabled = true Mode = 'BlockHistory' PriceDefault = '5 gwei' PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' -PriceMin = '1 gwei' +PriceMin = '3 gwei' LimitDefault = 8000000 LimitMax = 8000000 LimitMultiplier = '1' @@ -5989,6 +5989,198 @@ GasLimit = 5400000

+
L3X Mainnet (12324)

+ +```toml +AutoCreateKey = true +BlockBackfillDepth = 10 +BlockBackfillSkip = false +ChainType = 'arbitrum' +FinalityDepth = 10 +FinalityTagEnabled = true +LinkContractAddress = '0x79f531a3D07214304F259DC28c7191513223bcf3' +LogBackfillBatchSize = 1000 +LogPollInterval = '10s' +LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 +MinIncomingConfirmations = 3 +MinContractPayment = '0.00001 link' +NonceAutoSync = true +NoNewHeadsThreshold = '0s' +RPCDefaultBatchSize = 250 +RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 +NoNewFinalizedHeadsThreshold = '0s' + +[Transactions] +ForwardersEnabled = false +MaxInFlight = 16 +MaxQueued = 250 +ReaperInterval = '1h0m0s' +ReaperThreshold = '168h0m0s' +ResendAfterThreshold = '1m0s' + +[Transactions.AutoPurge] +Enabled = false + +[BalanceMonitor] +Enabled = true + +[GasEstimator] +Mode = 'Arbitrum' +PriceDefault = '100 mwei' +PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' +PriceMin = '0' +LimitDefault = 500000 +LimitMax = 1000000000 +LimitMultiplier = '1' +LimitTransfer = 21000 +EstimateLimit = false +BumpMin = '5 gwei' +BumpPercent = 20 +BumpThreshold = 5 +EIP1559DynamicFees = false +FeeCapDefault = '1 micro' +TipCapDefault = '1 wei' +TipCapMin = '1 wei' + +[GasEstimator.BlockHistory] +BatchSize = 25 +BlockHistorySize = 8 +CheckInclusionBlocks = 12 +CheckInclusionPercentile = 90 +TransactionPercentile = 60 + +[HeadTracker] +HistoryDepth = 100 +MaxBufferSize = 3 +SamplingInterval = '1s' +MaxAllowedFinalityDepth = 10000 +FinalityTagBypass = true + +[NodePool] +PollFailureThreshold = 5 +PollInterval = '10s' +SelectionMode = 'HighestHead' +SyncThreshold = 5 +LeaseDuration = '0s' +NodeIsSyncingEnabled = false +FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' + +[OCR] +ContractConfirmations = 1 +ContractTransmitterTransmitTimeout = '10s' +DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' +ObservationGracePeriod = '1s' + +[OCR2] +[OCR2.Automation] +GasLimit = 5400000 +``` + +

+ +
L3X Sepolia (12325)

+ +```toml +AutoCreateKey = true +BlockBackfillDepth = 10 +BlockBackfillSkip = false +ChainType = 'arbitrum' +FinalityDepth = 10 +FinalityTagEnabled = true +LinkContractAddress = '0xa71848C99155DA0b245981E5ebD1C94C4be51c43' +LogBackfillBatchSize = 1000 +LogPollInterval = '10s' +LogKeepBlocksDepth = 100000 +LogPrunePageSize = 0 +BackupLogPollerBlockDelay = 100 +MinIncomingConfirmations = 3 +MinContractPayment = '0.00001 link' +NonceAutoSync = true +NoNewHeadsThreshold = '0s' +RPCDefaultBatchSize = 250 +RPCBlockQueryDelay = 1 +FinalizedBlockOffset = 0 +NoNewFinalizedHeadsThreshold = '0s' + +[Transactions] +ForwardersEnabled = false +MaxInFlight = 16 +MaxQueued = 250 +ReaperInterval = '1h0m0s' +ReaperThreshold = '168h0m0s' +ResendAfterThreshold = '1m0s' + +[Transactions.AutoPurge] +Enabled = false + +[BalanceMonitor] +Enabled = true + +[GasEstimator] +Mode = 'Arbitrum' +PriceDefault = '100 mwei' +PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' +PriceMin = '0' +LimitDefault = 500000 +LimitMax = 1000000000 +LimitMultiplier = '1' +LimitTransfer = 21000 +EstimateLimit = false +BumpMin = '5 gwei' +BumpPercent = 20 +BumpThreshold = 5 +EIP1559DynamicFees = false +FeeCapDefault = '1 micro' +TipCapDefault = '1 wei' +TipCapMin = '1 wei' + +[GasEstimator.BlockHistory] +BatchSize = 25 +BlockHistorySize = 8 +CheckInclusionBlocks = 12 +CheckInclusionPercentile = 90 +TransactionPercentile = 60 + +[HeadTracker] +HistoryDepth = 100 +MaxBufferSize = 3 +SamplingInterval = '1s' +MaxAllowedFinalityDepth = 10000 +FinalityTagBypass = true + +[NodePool] +PollFailureThreshold = 5 +PollInterval = '10s' +SelectionMode = 'HighestHead' +SyncThreshold = 5 +LeaseDuration = '0s' +NodeIsSyncingEnabled = false +FinalizedBlockPollInterval = '5s' +EnforceRepeatableRead = false +DeathDeclarationDelay = '10s' + +[OCR] +ContractConfirmations = 1 +ContractTransmitterTransmitTimeout = '10s' +DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' +ObservationGracePeriod = '1s' + +[OCR2] +[OCR2.Automation] +GasLimit = 5400000 +``` + +

+
Arbitrum Mainnet (42161)

```toml @@ -9486,6 +9678,7 @@ L2Full = '(: |^)l2 full' # Example TransactionAlreadyMined = '(: |^)transaction already mined' # Example Fatal = '(: |^)fatal' # Example ServiceUnavailable = '(: |^)service unavailable' # Example +TooManyResults = '(: |^)too many results' # Example ``` Errors enable the node to provide custom regex patterns to match against error messages from RPCs. @@ -9573,6 +9766,12 @@ ServiceUnavailable = '(: |^)service unavailable' # Example ``` ServiceUnavailable is a regex pattern to match against service unavailable errors. +### TooManyResults +```toml +TooManyResults = '(: |^)too many results' # Example +``` +TooManyResults is a regex pattern to match an eth_getLogs error indicating the result set is too large to return + ## EVM.OCR ```toml [EVM.OCR] @@ -9684,6 +9883,7 @@ GasLimit controls the gas limit for transmit transactions from ocr2automation jo [EVM.Workflow] FromAddress = '0x2a3e23c6f242F5345320814aC8a1b4E58707D292' # Example ForwarderAddress = '0x2a3e23c6f242F5345320814aC8a1b4E58707D292' # Example +DefaultGasLimit = 400_000 # Default ``` @@ -9699,6 +9899,12 @@ ForwarderAddress = '0x2a3e23c6f242F5345320814aC8a1b4E58707D292' # Example ``` ForwarderAddress is the keystone forwarder contract address on chain. +### DefaultGasLimit +```toml +DefaultGasLimit = 400_000 # Default +``` +DefaultGasLimit is the default gas limit for workflow transactions. + ## Cosmos ```toml [[Cosmos]] diff --git a/go.md b/go.md index d9ed0d0a66..f58a5e23e4 100644 --- a/go.md +++ b/go.md @@ -5,11 +5,7 @@ flowchart LR chainlink-cosmos chainlink-solana chainlink-starknet/relayer - subgraph chainlink-integrations - direction LR - chainlink-integrations/evm/relayer - chainlink-integrations/common - end + chainlink-evm end subgraph products @@ -21,13 +17,20 @@ flowchart LR chainlink-vrf end + subgraph tdh2 + tdh2/go/tdh2 + tdh2/go/ocr2/decryptionplugin + end + classDef outline stroke-dasharray:6,fill:none; - class chains,products outline + class chains,products,tdh2 outline chainlink/v2 --> chain-selectors click chain-selectors href "https://github.com/smartcontractkit/chain-selectors" chainlink/v2 --> chainlink-automation click chainlink-automation href "https://github.com/smartcontractkit/chainlink-automation" + chainlink/v2 --> chainlink-ccip + click chainlink-ccip href "https://github.com/smartcontractkit/chainlink-ccip" chainlink/v2 --> chainlink-common click chainlink-common href "https://github.com/smartcontractkit/chainlink-common" chainlink/v2 --> chainlink-cosmos @@ -50,6 +53,8 @@ flowchart LR click wsrpc href "https://github.com/smartcontractkit/wsrpc" chainlink-automation --> chainlink-common chainlink-automation --> libocr + chainlink-ccip --> chainlink-common + chainlink-ccip --> libocr chainlink-common --> libocr chainlink-cosmos --> chainlink-common chainlink-cosmos --> libocr diff --git a/go.mod b/go.mod index 2cf454ccae..3fe0a1f465 100644 --- a/go.mod +++ b/go.mod @@ -75,11 +75,12 @@ require ( github.com/shopspring/decimal v1.4.0 github.com/smartcontractkit/chain-selectors v1.0.24 github.com/smartcontractkit/chainlink-automation v1.0.4 - github.com/smartcontractkit/chainlink-common v0.2.2-0.20240723123524-e407ecd120b1 + github.com/smartcontractkit/chainlink-ccip v0.0.0-20240806144315-04ac101e9c95 + github.com/smartcontractkit/chainlink-common v0.2.2-0.20240828121637-da5837469949 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240710121324-3ed288aa9b45 - github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240718160222-2dc0c8136bfa + github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240820130645-cf4b159fbba2 github.com/smartcontractkit/chainlink-feeds v0.0.0-20240710170203-5b41615da827 - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240712132946-267a37c5ac6e + github.com/smartcontractkit/chainlink-solana v1.1.1-0.20240821170223-a2f5c39f457f github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240709043547-03612098f799 github.com/smartcontractkit/libocr v0.0.0-20240717100443-f6226e09bee7 github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 @@ -102,15 +103,15 @@ require ( go.opentelemetry.io/otel v1.28.0 go.uber.org/multierr v1.11.0 go.uber.org/zap v1.27.0 - golang.org/x/crypto v0.25.0 - golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 - golang.org/x/mod v0.19.0 - golang.org/x/net v0.27.0 - golang.org/x/sync v0.7.0 - golang.org/x/term v0.22.0 - golang.org/x/text v0.16.0 + golang.org/x/crypto v0.26.0 + golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa + golang.org/x/mod v0.20.0 + golang.org/x/net v0.28.0 + golang.org/x/sync v0.8.0 + golang.org/x/term v0.23.0 + golang.org/x/text v0.17.0 golang.org/x/time v0.5.0 - golang.org/x/tools v0.23.0 + golang.org/x/tools v0.24.0 gonum.org/v1/gonum v0.15.0 google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.2 @@ -230,12 +231,12 @@ require ( github.com/google/go-tpm v0.9.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/gorilla/context v1.1.1 // indirect - github.com/grafana/pyroscope-go/godeltaprof v0.1.6 // indirect + github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/gtank/merlin v0.1.1 // indirect github.com/gtank/ristretto255 v0.1.2 // indirect @@ -262,7 +263,7 @@ require ( github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.3 // indirect + github.com/klauspost/compress v1.17.8 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect @@ -335,17 +336,15 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 // indirect go.opentelemetry.io/otel/metric v1.28.0 // indirect go.opentelemetry.io/otel/sdk v1.28.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.28.0 // indirect go.opentelemetry.io/otel/trace v1.28.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/ratelimit v0.3.0 // indirect golang.org/x/arch v0.8.0 // indirect - golang.org/x/oauth2 v0.21.0 // indirect - golang.org/x/sys v0.22.0 // indirect + golang.org/x/sys v0.24.0 // indirect google.golang.org/api v0.188.0 // indirect google.golang.org/genproto v0.0.0-20240711142825-46eb208f015d // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd // indirect gopkg.in/guregu/null.v2 v2.1.2 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 1ee3d32944..6dc6a23bb6 100644 --- a/go.sum +++ b/go.sum @@ -555,8 +555,8 @@ github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/ github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/grafana/pyroscope-go v1.1.1 h1:PQoUU9oWtO3ve/fgIiklYuGilvsm8qaGhlY4Vw6MAcQ= github.com/grafana/pyroscope-go v1.1.1/go.mod h1:Mw26jU7jsL/KStNSGGuuVYdUq7Qghem5P8aXYXSXG88= -github.com/grafana/pyroscope-go/godeltaprof v0.1.6 h1:nEdZ8louGAplSvIJi1HVp7kWvFvdiiYg3COLlTwJiFo= -github.com/grafana/pyroscope-go/godeltaprof v0.1.6/go.mod h1:Tk376Nbldo4Cha9RgiU7ik8WKFkNpfds98aUzS8omLE= +github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg= +github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= github.com/graph-gophers/dataloader v5.0.0+incompatible h1:R+yjsbrNq1Mo3aPG+Z/EKYrXrXXUNJHOgbRt+U6jOug= github.com/graph-gophers/dataloader v5.0.0+incompatible/go.mod h1:jk4jk0c5ZISbKaMe8WsVopGB5/15GvGHMdMdPtwlRp4= github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0= @@ -572,8 +572,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= @@ -744,8 +744,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.17.3 h1:qkRjuerhUU1EmXLYGkSH6EZL+vPSxIrYjLNAK4slzwA= -github.com/klauspost/compress v1.17.3/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= +github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= @@ -1030,16 +1030,18 @@ github.com/smartcontractkit/chain-selectors v1.0.24 h1:day+2l0vBErgVIs2Y9ifSz+T7 github.com/smartcontractkit/chain-selectors v1.0.24/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.4 h1:iyW181JjKHLNMnDleI8umfIfVVlwC7+n5izbLSFgjw8= github.com/smartcontractkit/chainlink-automation v1.0.4/go.mod h1:u4NbPZKJ5XiayfKHD/v3z3iflQWqvtdhj13jVZXj/cM= -github.com/smartcontractkit/chainlink-common v0.2.2-0.20240723123524-e407ecd120b1 h1:pdEpjgbZ5w/Sd5lzg/XiuC5gVyrmSovOo+3nUD46SP8= -github.com/smartcontractkit/chainlink-common v0.2.2-0.20240723123524-e407ecd120b1/go.mod h1:Jg1sCTsbxg76YByI8ifpFby3FvVqISStHT8ypy9ocmY= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20240806144315-04ac101e9c95 h1:LAgJTg9Yr/uCo2g7Krp88Dco2U45Y6sbJVl8uKoLkys= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20240806144315-04ac101e9c95/go.mod h1:/ZWraCBaDDgaIN1prixYcbVvIk/6HeED9+8zbWQ+TMo= +github.com/smartcontractkit/chainlink-common v0.2.2-0.20240828121637-da5837469949 h1:9YHYswxhxMAgdJhb+APrf57ZEPsxML8H71oxxvU36eU= +github.com/smartcontractkit/chainlink-common v0.2.2-0.20240828121637-da5837469949/go.mod h1:bE6E7KwB8dkFUWKxJTTTtrNAl9xFPGlurKpDVhRz1tk= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240710121324-3ed288aa9b45 h1:NBQLtqk8zsyY4qTJs+NElI3aDFTcAo83JHvqD04EvB0= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240710121324-3ed288aa9b45/go.mod h1:LV0h7QBQUpoC2UUi6TcUvcIFm1xjP/DtEcqV8+qeLUs= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240718160222-2dc0c8136bfa h1:g75H8oh2ws52s8BekwvGQ9XvBVu3E7WM1rfiA0PN0zk= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240718160222-2dc0c8136bfa/go.mod h1:wZvLHX/Sd9hskN51016cTFcT3G62KXVa6xbVDS7tRjc= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240820130645-cf4b159fbba2 h1:KH6tpCw5hu8u6UTtgll7a8mE4sIbHCbmtzHJdKuRwBw= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240820130645-cf4b159fbba2/go.mod h1:V/86loaFSH0dqqUEHqyXVbyNqDRSjvcf9BRomWFTljU= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240710170203-5b41615da827 h1:BCHu4pNP6arrcHLEWx61XjLaonOd2coQNyL0NTUcaMc= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240710170203-5b41615da827/go.mod h1:OPX+wC2TWQsyLNpR7daMt2vMpmsNcoBxbZyGTHr6tiA= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240712132946-267a37c5ac6e h1:PzwzlHNv1YbJ6ZIdl/pIFRoOuOS4V4WLvjZvFUnZFL4= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240712132946-267a37c5ac6e/go.mod h1:hsFhop+SlQHKD+DEFjZrMJmbauT1A/wvtZIeeo4PxFU= +github.com/smartcontractkit/chainlink-solana v1.1.1-0.20240821170223-a2f5c39f457f h1:b0Ifwk7eK3fwJ0R69Ovhv5XvZ1/TUAfHkU5Jp7wbNZ0= +github.com/smartcontractkit/chainlink-solana v1.1.1-0.20240821170223-a2f5c39f457f/go.mod h1:Ml88TJTwZCj6yHDkAEN/EhxVutzSlk+kDZgfibRIqF0= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240709043547-03612098f799 h1:HyLTySm7BR+oNfZqDTkVJ25wnmcTtxBBD31UkFL+kEM= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240709043547-03612098f799/go.mod h1:UVFRacRkP7O7TQAzFmR52v5mUlxf+G1ovMlCQAB/cHU= github.com/smartcontractkit/go-plugin v0.0.0-20240208201424-b3b91517de16 h1:TFe+FvzxClblt6qRfqEhUfa4kFQx5UobuoFGO2W4mMo= @@ -1294,8 +1296,8 @@ golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= -golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1306,8 +1308,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI= +golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1332,8 +1334,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= -golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= +golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1375,15 +1377,15 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= -golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= -golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= +golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1395,8 +1397,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1466,8 +1468,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1476,8 +1478,8 @@ golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= -golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1491,8 +1493,8 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1544,8 +1546,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= -golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= +golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1602,10 +1604,10 @@ google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEY google.golang.org/genproto v0.0.0-20210401141331-865547bb08e2/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20240711142825-46eb208f015d h1:/hmn0Ku5kWij/kjGsrcJeC1T/MrJi2iNWwgAqrihFwc= google.golang.org/genproto v0.0.0-20240711142825-46eb208f015d/go.mod h1:FfBgJBJg9GcpPvKIuHSZ/aE1g2ecGL74upMzGZjiGEY= -google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d h1:kHjw/5UfflP/L5EbledDrcG4C2597RtymmGRZvHiCuY= -google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d/go.mod h1:mw8MG/Qz5wfgYr6VqVCiZcHe/GJEfI+oGGDCohaVgB0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d h1:JU0iKnSg02Gmb5ZdV8nYsKEKsP6o/FGVWTrw4i1DA9A= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd h1:BBOTEWLuuEGQy9n1y9MhVJ9Qt0BDu21X8qZs71/uPZo= +google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:fO8wJzT2zbQbAjbIoos1285VfEIYKDDY+Dt+WpTkh6g= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd h1:6TEm2ZxXoQmFWFlt1vNxvVOa1Q0dXFQD1m/rYjXmS0E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= diff --git a/heroku.yml b/heroku.yml deleted file mode 100644 index bb95afa121..0000000000 --- a/heroku.yml +++ /dev/null @@ -1,6 +0,0 @@ -build: - docker: - web: Dockerfile.web - config: - REACT_APP_INFURA_KEY: - REACT_APP_GA_ID: diff --git a/integration-tests/.golangci.yml b/integration-tests/.golangci.yml index 8969110d98..304249d24c 100644 --- a/integration-tests/.golangci.yml +++ b/integration-tests/.golangci.yml @@ -70,6 +70,9 @@ linters-settings: - name: atomic issues: exclude-rules: + - path: deployment/memory/(.+)\.go + linters: + - revive - text: "^G404: Use of weak random number generator" linters: - gosec diff --git a/integration-tests/README.md b/integration-tests/README.md index fcfefe97a7..5647a62316 100644 --- a/integration-tests/README.md +++ b/integration-tests/README.md @@ -27,6 +27,8 @@ version = "your tag" The `./testconfig/overrides.toml` file **should never be committed** and has been added to the [.gitignore](../.gitignore) file as it can often contain secrets like private keys and RPC URLs. +For more information on how to configure the tests, see the [testconfig README](./testconfig/README.md). + ## Build If you'd like to run the tests on a local build of Chainlink, you can point to your own docker image, or build a fresh one with `make`. @@ -51,7 +53,7 @@ It's generally recommended to run only one test at a time on a local machine as ### Configure Seth -Our new evm client is Seth. Detailed instructions on how to configure it can be found in the [Seth README](./README_SETH.md) in this repo as well as in [Seth repository](https://github.com/smartcontractkit/seth). +Our new evm client is Seth. Detailed instructions on how to configure it can be found in the [Seth README](./README_SETH.md) in this repo as well as in [Seth repository](https://github.com/smartcontractkit/chainlink-testing-framework/tree/main/seth). ## Analyze @@ -76,8 +78,7 @@ make test_soak_ocr_reorg_2 Run reorg/automation_reorg_test.go with reorg settings: -1. Use Simulated Geth network and put GethReorgConfig in overrides.toml - +1. Use Simulated Geth network and put GethReorgConfig in overrides.toml ```toml [Network] @@ -128,3 +129,9 @@ Run soak/ocr_test.go with RPC network chaos by bringing down network to RPC node ```bash make test_soak_ocr_rpc_down_half_cl_nodes ``` + +### Debugging HTTP and RPC clients +```bash +export SETH_LOG_LEVEL=debug +export RESTY_DEBUG=true +``` diff --git a/integration-tests/README_SETH.md b/integration-tests/README_SETH.md index 92302ab9ce..26fbfc1a79 100644 --- a/integration-tests/README_SETH.md +++ b/integration-tests/README_SETH.md @@ -41,7 +41,7 @@ ## Introduction -[Seth](https://github.com/smartcontractkit/seth) is the Ethereum client we use for integration tests. It is designed to be a thin wrapper over `go-ethereum` client that adds a couple of key features: +[Seth](https://github.com/smartcontractkit/chainlink-testing-framework/tree/main/seth) is the Ethereum client we use for integration tests. It is designed to be a thin wrapper over `go-ethereum` client that adds a couple of key features: * key management * transaction decoding and tracing * gas estimation @@ -65,7 +65,7 @@ tracing_level = "all" # trace all transactions regardless of whether they are re ``` ### Documentation and Further Details -For a comprehensive description of all available configuration options, refer to the `[Seth]` section of configuration documentation in the [default.toml](./testconfig/default.toml) file or consult the Seth [README.md on GitHub](https://github.com/smartcontractkit/seth/blob/master/README.md). +For a comprehensive description of all available configuration options, refer to the `[Seth]` section of configuration documentation in the [default.toml](./testconfig/default.toml) file or consult the Seth [README.md on GitHub](https://github.com/smartcontractkit/chainlink-testing-framework/tree/main/seth/blob/master/README.md). ## How to set Seth logging level ### Locally @@ -155,7 +155,7 @@ The most important thing to keep in mind that the CLI requires you to provide a * `keys` commands requires `SETH_KEYFILE_PATH`, `SETH_CONFIG_PATH` and `SETH_ROOT_PRIVATE_KEY` environment variables * `gas` and `stats` command requires `SETH_CONFIG_PATH` environment variable -You can find a sample `Seth.toml` file [here](https://github.com/smartcontractkit/seth/blob/master/seth.toml). Currently, you cannot use your test TOML file as a Seth configuration file, but we will add ability that in the future. +You can find a sample `Seth.toml` file [here](https://github.com/smartcontractkit/chainlink-testing-framework/tree/main/seth/blob/master/seth.toml). Currently, you cannot use your test TOML file as a Seth configuration file, but we will add ability that in the future. ## How to get Fallback (Hardcoded) Values There are two primary methods to obtain fallback values for network configuration: @@ -166,7 +166,7 @@ There are two primary methods to obtain fallback values for network configuratio 1. **Clone the Seth Repository:** Clone the repository from GitHub using: ```bash -git clone https://github.com/smartcontractkit/seth +git clone https://github.com/smartcontractkit/chainlink-testing-framework/tree/main/seth ``` 2. **Run Seth CLI:** diff --git a/integration-tests/actions/actions.go b/integration-tests/actions/actions.go index 6f7a301f2f..12a0f67705 100644 --- a/integration-tests/actions/actions.go +++ b/integration-tests/actions/actions.go @@ -30,6 +30,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/contracts" ethContracts "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" + "github.com/smartcontractkit/chainlink/integration-tests/wrappers" "github.com/ethereum/go-ethereum/accounts/abi" @@ -1091,18 +1092,42 @@ func SendLinkFundsToDeploymentAddresses( toTransferToMultiCallContract := big.NewInt(0).Mul(linkAmountPerUpkeep, big.NewInt(int64(totalUpkeeps+concurrency))) toTransferPerClient := big.NewInt(0).Mul(linkAmountPerUpkeep, big.NewInt(int64(operationsPerAddress+1))) - err := linkToken.Transfer(multicallAddress.Hex(), toTransferToMultiCallContract) + + // As a hack we use the geth wrapper directly, because we need to access receipt to get block number, which we will use to query the balance + // This is needed as querying with 'latest' block number very rarely, but still, return stale balance. That's happening even though we wait for + // the transaction to be mined. + linkInstance, err := link_token_interface.NewLinkToken(common.HexToAddress(linkToken.Address()), wrappers.MustNewWrappedContractBackend(nil, chainClient)) + if err != nil { + return err + } + + tx, err := chainClient.Decode(linkInstance.Transfer(chainClient.NewTXOpts(), multicallAddress, toTransferToMultiCallContract)) if err != nil { - return errors.Wrapf(err, "Error transferring LINK to multicall contract") + return err + } + + if tx.Receipt == nil { + return fmt.Errorf("transaction receipt for LINK transfer to multicall contract is nil") } - balance, err := linkToken.BalanceOf(context.Background(), multicallAddress.Hex()) + multiBalance, err := linkInstance.BalanceOf(&bind.CallOpts{From: chainClient.Addresses[0], BlockNumber: tx.Receipt.BlockNumber}, multicallAddress) if err != nil { return errors.Wrapf(err, "Error getting LINK balance of multicall contract") } - if balance.Cmp(toTransferToMultiCallContract) < 0 { - return fmt.Errorf("Incorrect LINK balance of multicall contract. Expected at least: %s. Got: %s", toTransferToMultiCallContract.String(), balance.String()) + // Old code that's querying latest block + //err := linkToken.Transfer(multicallAddress.Hex(), toTransferToMultiCallContract) + //if err != nil { + // return errors.Wrapf(err, "Error transferring LINK to multicall contract") + //} + // + //balance, err := linkToken.BalanceOf(context.Background(), multicallAddress.Hex()) + //if err != nil { + // return errors.Wrapf(err, "Error getting LINK balance of multicall contract") + //} + + if multiBalance.Cmp(toTransferToMultiCallContract) < 0 { + return fmt.Errorf("Incorrect LINK balance of multicall contract. Expected at least: %s. Got: %s", toTransferToMultiCallContract.String(), multiBalance.String()) } // Transfer LINK to ephemeral keys @@ -1127,18 +1152,24 @@ func SendLinkFundsToDeploymentAddresses( } boundContract := bind.NewBoundContract(multicallAddress, multiCallABI, chainClient.Client, chainClient.Client, chainClient.Client) // call aggregate3 to group all msg call data and send them in a single transaction - _, err = chainClient.Decode(boundContract.Transact(chainClient.NewTXOpts(), "aggregate3", call)) + ephemeralTx, err := chainClient.Decode(boundContract.Transact(chainClient.NewTXOpts(), "aggregate3", call)) if err != nil { return errors.Wrapf(err, "Error calling Multicall contract") } + if ephemeralTx.Receipt == nil { + return fmt.Errorf("transaction receipt for LINK transfer to ephemeral keys is nil") + } + for i := 1; i <= concurrency; i++ { - balance, err := linkToken.BalanceOf(context.Background(), chainClient.Addresses[i].Hex()) + ephemeralBalance, err := linkInstance.BalanceOf(&bind.CallOpts{From: chainClient.Addresses[0], BlockNumber: ephemeralTx.Receipt.BlockNumber}, chainClient.Addresses[i]) + // Old code that's querying latest block, for now we prefer to use block number from the transaction receipt + //balance, err := linkToken.BalanceOf(context.Background(), chainClient.Addresses[i].Hex()) if err != nil { return errors.Wrapf(err, "Error getting LINK balance of ephemeral key %d", i) } - if balance.Cmp(toTransferPerClient) < 0 { - return fmt.Errorf("Incorrect LINK balance after transfer. Ephemeral key %d. Expected: %s. Got: %s", i, toTransferPerClient.String(), balance.String()) + if ephemeralBalance.Cmp(toTransferPerClient) < 0 { + return fmt.Errorf("Incorrect LINK balance after transfer. Ephemeral key %d. Expected: %s. Got: %s", i, toTransferPerClient.String(), ephemeralBalance.String()) } } @@ -1279,11 +1310,16 @@ func IsOPStackChain(chainID int64) bool { chainID == 11155420 //OPTIMISM SEPOLIA } +func IsArbitrumChain(chainID int64) bool { + return chainID == 42161 || //Arbitrum MAINNET + chainID == 421614 //Arbitrum Sepolia +} + func RandBool() bool { return rand.Intn(2) == 1 } -func ContinuouslyGenerateTXsOnChain(sethClient *seth.Client, stopChannel chan bool, l zerolog.Logger) (bool, error) { +func ContinuouslyGenerateTXsOnChain(sethClient *seth.Client, stopChannel chan bool, wg *sync.WaitGroup, l zerolog.Logger) (bool, error) { counterContract, err := contracts.DeployCounterContract(sethClient) if err != nil { return false, err @@ -1297,6 +1333,10 @@ func ContinuouslyGenerateTXsOnChain(sethClient *seth.Client, stopChannel chan bo select { case <-stopChannel: l.Info().Str("Number of generated transactions on chain", count.String()).Msg("Stopping generating txs on chain. Desired block number reached.") + sleepDuration := time.Second * 10 + l.Info().Str("Waiting for", sleepDuration.String()).Msg("Waiting for transactions to be mined and avoid nonce issues") + time.Sleep(sleepDuration) + wg.Done() return true, nil default: err = counterContract.Increment() diff --git a/integration-tests/actions/automation_ocr_helpers.go b/integration-tests/actions/automation_ocr_helpers.go index 9157faf202..df9168e25d 100644 --- a/integration-tests/actions/automation_ocr_helpers.go +++ b/integration-tests/actions/automation_ocr_helpers.go @@ -439,7 +439,7 @@ func deployRegistry( wethToken contracts.WETHToken, ethUSDFeed contracts.MockETHUSDFeed, ) contracts.KeeperRegistry { - ef, err := contracts.DeployMockETHLINKFeed(client, big.NewInt(2e18)) + ef, err := contracts.DeployMockLINKETHFeed(client, big.NewInt(2e18)) require.NoError(t, err, "Deploying mock ETH-Link feed shouldn't fail") gf, err := contracts.DeployMockGASFeed(client, big.NewInt(2e11)) require.NoError(t, err, "Deploying mock gas feed shouldn't fail") diff --git a/integration-tests/actions/automationv2/actions.go b/integration-tests/actions/automationv2/actions.go index d3504b8fb1..0aebec546d 100644 --- a/integration-tests/actions/automationv2/actions.go +++ b/integration-tests/actions/automationv2/actions.go @@ -63,8 +63,9 @@ type AutomationTest struct { LinkToken contracts.LinkToken Transcoder contracts.UpkeepTranscoder - EthLinkFeed contracts.MockETHLINKFeed - EthUSDFeed contracts.MockETHUSDFeed + LINKETHFeed contracts.MockLINKETHFeed + ETHUSDFeed contracts.MockETHUSDFeed + LINKUSDFeed contracts.MockETHUSDFeed WETHToken contracts.WETHToken GasFeed contracts.MockGasFeed Registry contracts.KeeperRegistry @@ -193,31 +194,30 @@ func (a *AutomationTest) LoadTranscoder(address string) error { return nil } -func (a *AutomationTest) DeployEthLinkFeed() error { - ethLinkFeed, err := contracts.DeployMockETHLINKFeed(a.ChainClient, a.RegistrySettings.FallbackLinkPrice) +func (a *AutomationTest) DeployLinkEthFeed() error { + ethLinkFeed, err := contracts.DeployMockLINKETHFeed(a.ChainClient, a.RegistrySettings.FallbackLinkPrice) if err != nil { return err } - a.EthLinkFeed = ethLinkFeed + a.LINKETHFeed = ethLinkFeed return nil } -func (a *AutomationTest) LoadEthLinkFeed(address string) error { - ethLinkFeed, err := contracts.LoadMockETHLINKFeed(a.ChainClient, common.HexToAddress(address)) +func (a *AutomationTest) LoadLinkEthFeed(address string) error { + ethLinkFeed, err := contracts.LoadMockLINKETHFeed(a.ChainClient, common.HexToAddress(address)) if err != nil { return err } - a.EthLinkFeed = ethLinkFeed + a.LINKETHFeed = ethLinkFeed return nil } func (a *AutomationTest) DeployEthUSDFeed() error { - // FallbackLinkPrice and FallbackETHPrice are the same ethUSDFeed, err := contracts.DeployMockETHUSDFeed(a.ChainClient, a.RegistrySettings.FallbackLinkPrice) if err != nil { return err } - a.EthUSDFeed = ethUSDFeed + a.ETHUSDFeed = ethUSDFeed return nil } @@ -226,7 +226,25 @@ func (a *AutomationTest) LoadEthUSDFeed(address string) error { if err != nil { return err } - a.EthUSDFeed = ethUSDFeed + a.ETHUSDFeed = ethUSDFeed + return nil +} + +func (a *AutomationTest) DeployLinkUSDFeed() error { + linkUSDFeed, err := contracts.DeployMockETHUSDFeed(a.ChainClient, a.RegistrySettings.FallbackLinkPrice) + if err != nil { + return err + } + a.LINKUSDFeed = linkUSDFeed + return nil +} + +func (a *AutomationTest) LoadLinkUSDFeed(address string) error { + linkUSDFeed, err := contracts.LoadMockETHUSDFeed(a.ChainClient, common.HexToAddress(address)) + if err != nil { + return err + } + a.LINKUSDFeed = linkUSDFeed return nil } @@ -270,13 +288,13 @@ func (a *AutomationTest) DeployRegistry() error { registryOpts := &contracts.KeeperRegistryOpts{ RegistryVersion: a.RegistrySettings.RegistryVersion, LinkAddr: a.LinkToken.Address(), - ETHFeedAddr: a.EthLinkFeed.Address(), + ETHFeedAddr: a.LINKETHFeed.Address(), GasFeedAddr: a.GasFeed.Address(), TranscoderAddr: a.Transcoder.Address(), RegistrarAddr: utils.ZeroAddress.Hex(), Settings: a.RegistrySettings, - LinkUSDFeedAddr: a.EthUSDFeed.Address(), - NativeUSDFeedAddr: a.EthUSDFeed.Address(), + LinkUSDFeedAddr: a.ETHUSDFeed.Address(), + NativeUSDFeedAddr: a.LINKUSDFeed.Address(), WrappedNativeAddr: a.WETHToken.Address(), } registry, err := contracts.DeployKeeperRegistry(a.ChainClient, registryOpts) @@ -564,7 +582,7 @@ func (a *AutomationTest) SetConfigOnRegistry() error { { GasFeePPB: 100, FlatFeeMilliCents: big.NewInt(500), - PriceFeed: common.HexToAddress(a.EthUSDFeed.Address()), // ETH/USD feed and LINK/USD feed are the same + PriceFeed: common.HexToAddress(a.ETHUSDFeed.Address()), Decimals: 18, FallbackPrice: big.NewInt(1000), MinSpend: big.NewInt(200), @@ -572,7 +590,7 @@ func (a *AutomationTest) SetConfigOnRegistry() error { { GasFeePPB: 100, FlatFeeMilliCents: big.NewInt(500), - PriceFeed: common.HexToAddress(a.EthUSDFeed.Address()), // ETH/USD feed and LINK/USD feed are the same + PriceFeed: common.HexToAddress(a.LINKUSDFeed.Address()), Decimals: 18, FallbackPrice: big.NewInt(1000), MinSpend: big.NewInt(200), @@ -854,14 +872,17 @@ func (a *AutomationTest) SetupAutomationDeployment(t *testing.T) { err = a.DeployWETH() require.NoError(t, err, "Error deploying weth token contract") - err = a.DeployEthLinkFeed() - require.NoError(t, err, "Error deploying eth link feed contract") + err = a.DeployLinkEthFeed() + require.NoError(t, err, "Error deploying link eth feed contract") err = a.DeployGasFeed() require.NoError(t, err, "Error deploying gas feed contract") err = a.DeployEthUSDFeed() require.NoError(t, err, "Error deploying eth usd feed contract") + err = a.DeployLinkUSDFeed() + require.NoError(t, err, "Error deploying link usd feed contract") + err = a.DeployTranscoder() require.NoError(t, err, "Error deploying transcoder contract") @@ -874,7 +895,7 @@ func (a *AutomationTest) SetupAutomationDeployment(t *testing.T) { } func (a *AutomationTest) LoadAutomationDeployment(t *testing.T, linkTokenAddress, - ethLinkFeedAddress, gasFeedAddress, transcoderAddress, registryAddress, registrarAddress string) { + linkEthFeedAddress, linkUsdFeedAddress, EthUsdFeedAddress, gasFeedAddress, transcoderAddress, registryAddress, registrarAddress string) { l := logging.GetTestLogger(t) err := a.CollectNodeDetails() require.NoError(t, err, "Error collecting node details") @@ -884,10 +905,14 @@ func (a *AutomationTest) LoadAutomationDeployment(t *testing.T, linkTokenAddress err = a.LoadLINK(linkTokenAddress) require.NoError(t, err, "Error loading link token contract") - err = a.LoadEthLinkFeed(ethLinkFeedAddress) - require.NoError(t, err, "Error loading eth link feed contract") + err = a.LoadLinkEthFeed(linkEthFeedAddress) + require.NoError(t, err, "Error loading link eth feed contract") err = a.LoadEthGasFeed(gasFeedAddress) require.NoError(t, err, "Error loading gas feed contract") + err = a.LoadEthUSDFeed(EthUsdFeedAddress) + require.NoError(t, err, "Error loading eth usd feed contract") + err = a.LoadLinkUSDFeed(linkUsdFeedAddress) + require.NoError(t, err, "Error loading link usd feed contract") err = a.LoadTranscoder(transcoderAddress) require.NoError(t, err, "Error loading transcoder contract") err = a.LoadRegistry(registryAddress) diff --git a/integration-tests/actions/keeper_helpers.go b/integration-tests/actions/keeper_helpers.go index d5bb3462f1..d5994676e4 100644 --- a/integration-tests/actions/keeper_helpers.go +++ b/integration-tests/actions/keeper_helpers.go @@ -14,8 +14,8 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/seth" - ctf_concurrency "github.com/smartcontractkit/chainlink-testing-framework/lib/concurrency" - "github.com/smartcontractkit/chainlink-testing-framework/lib/logging" + ctf_concurrency "github.com/smartcontractkit/chainlink-testing-framework/concurrency" + "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" "github.com/ethereum/go-ethereum/common" @@ -82,7 +82,7 @@ func DeployKeeperContracts( client *seth.Client, linkFundsForEachUpkeep *big.Int, ) (contracts.KeeperRegistry, contracts.KeeperRegistrar, []contracts.KeeperConsumer, []*big.Int) { - ef, err := contracts.DeployMockETHLINKFeed(client, big.NewInt(2e18)) + ef, err := contracts.DeployMockLINKETHFeed(client, big.NewInt(2e18)) require.NoError(t, err, "Deploying mock ETH-Link feed shouldn't fail") gf, err := contracts.DeployMockGASFeed(client, big.NewInt(2e11)) require.NoError(t, err, "Deploying mock gas feed shouldn't fail") @@ -137,7 +137,7 @@ func DeployPerformanceKeeperContracts( checkGasToBurn, // How much gas should be burned on checkUpkeep() calls performGasToBurn int64, // How much gas should be burned on performUpkeep() calls ) (contracts.KeeperRegistry, contracts.KeeperRegistrar, []contracts.KeeperConsumerPerformance, []*big.Int) { - ef, err := contracts.DeployMockETHLINKFeed(chainClient, big.NewInt(2e18)) + ef, err := contracts.DeployMockLINKETHFeed(chainClient, big.NewInt(2e18)) require.NoError(t, err, "Deploying mock ETH-Link feed shouldn't fail") gf, err := contracts.DeployMockGASFeed(chainClient, big.NewInt(2e11)) require.NoError(t, err, "Deploying mock gas feed shouldn't fail") @@ -197,7 +197,7 @@ func DeployPerformDataCheckerContracts( linkFundsForEachUpkeep *big.Int, expectedData []byte, ) (contracts.KeeperRegistry, contracts.KeeperRegistrar, []contracts.KeeperPerformDataChecker, []*big.Int) { - ef, err := contracts.DeployMockETHLINKFeed(chainClient, big.NewInt(2e18)) + ef, err := contracts.DeployMockLINKETHFeed(chainClient, big.NewInt(2e18)) require.NoError(t, err, "Deploying mock ETH-Link feed shouldn't fail") gf, err := contracts.DeployMockGASFeed(chainClient, big.NewInt(2e11)) require.NoError(t, err, "Deploying mock gas feed shouldn't fail") diff --git a/integration-tests/actions/private_network.go b/integration-tests/actions/private_network.go index 40fe317ab3..f10371d41a 100644 --- a/integration-tests/actions/private_network.go +++ b/integration-tests/actions/private_network.go @@ -3,9 +3,9 @@ package actions import ( "github.com/rs/zerolog" - ctf_config "github.com/smartcontractkit/chainlink-testing-framework/lib/config" - ctf_config_types "github.com/smartcontractkit/chainlink-testing-framework/lib/config/types" - ctf_test_env "github.com/smartcontractkit/chainlink-testing-framework/lib/docker/test_env" + ctf_config "github.com/smartcontractkit/chainlink-testing-framework/config" + ctf_config_types "github.com/smartcontractkit/chainlink-testing-framework/config/types" + ctf_test_env "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" ) func EthereumNetworkConfigFromConfig(l zerolog.Logger, config ctf_config.GlobalTestConfig) (network ctf_test_env.EthereumNetwork, err error) { diff --git a/integration-tests/actions/refund.go b/integration-tests/actions/refund.go index 1835d9a04a..d49b74c699 100644 --- a/integration-tests/actions/refund.go +++ b/integration-tests/actions/refund.go @@ -19,7 +19,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/seth" - "github.com/smartcontractkit/chainlink-testing-framework/lib/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" clClient "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" diff --git a/integration-tests/actions/vrf/common/actions.go b/integration-tests/actions/vrf/common/actions.go index 7d9feb275c..59ee324ea0 100644 --- a/integration-tests/actions/vrf/common/actions.go +++ b/integration-tests/actions/vrf/common/actions.go @@ -10,6 +10,8 @@ import ( "testing" "time" + "github.com/smartcontractkit/chainlink/integration-tests/utils" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/go-resty/resty/v2" @@ -18,9 +20,9 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/seth" - ctf_test_env "github.com/smartcontractkit/chainlink-testing-framework/lib/docker/test_env" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/conversions" - seth_utils "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/seth" + ctf_test_env "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" + "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" @@ -367,7 +369,7 @@ func BuildNewCLEnvForVRF(l zerolog.Logger, t *testing.T, envConfig VRFEnvConfig, if err != nil { return nil, nil, fmt.Errorf("%s, err: %w", "error getting first evm network", err) } - sethClient, err := seth_utils.GetChainClient(envConfig.TestConfig, *evmNetwork) + sethClient, err := utils.TestAwareSethClient(t, envConfig.TestConfig, evmNetwork) if err != nil { return nil, nil, fmt.Errorf("%s, err: %w", "error getting seth client", err) } @@ -385,6 +387,35 @@ func BuildNewCLEnvForVRF(l zerolog.Logger, t *testing.T, envConfig VRFEnvConfig, return env, sethClient, nil } +func LoadExistingCLEnvForVRF( + t *testing.T, + envConfig VRFEnvConfig, + commonExistingEnvConfig *vrf_common_config.ExistingEnvConfig, + l zerolog.Logger, +) (*test_env.CLClusterTestEnv, *seth.Client, error) { + env, err := test_env.NewCLTestEnvBuilder(). + WithTestInstance(t). + WithTestConfig(&envConfig.TestConfig). + WithCustomCleanup(envConfig.CleanupFn). + Build() + if err != nil { + return nil, nil, fmt.Errorf("%s, err: %w", "error creating test env", err) + } + evmNetwork, err := env.GetFirstEvmNetwork() + if err != nil { + return nil, nil, err + } + sethClient, err := utils.TestAwareSethClient(t, envConfig.TestConfig, evmNetwork) + if err != nil { + return nil, nil, err + } + err = FundNodesIfNeeded(testcontext.Get(t), commonExistingEnvConfig, sethClient, l) + if err != nil { + return nil, nil, err + } + return env, sethClient, nil +} + func GetRPCUrl(env *test_env.CLClusterTestEnv, chainID int64) (string, error) { provider, err := env.GetRpcProvider(chainID) if err != nil { @@ -400,7 +431,7 @@ type RPCRawClient struct { } func NewRPCRawClient(url string) *RPCRawClient { - isDebug := os.Getenv("DEBUG_RESTY") == "true" + isDebug := os.Getenv("RESTY_DEBUG") == "true" restyClient := resty.New().SetDebug(isDebug).SetBaseURL(url) return &RPCRawClient{ resty: restyClient, diff --git a/integration-tests/actions/vrf/common/models.go b/integration-tests/actions/vrf/common/models.go index 9baa5c96e1..f51fd84ba0 100644 --- a/integration-tests/actions/vrf/common/models.go +++ b/integration-tests/actions/vrf/common/models.go @@ -55,6 +55,7 @@ type VRFContracts struct { VRFV2PlusConsumer []contracts.VRFv2PlusLoadTestConsumer LinkToken contracts.LinkToken MockETHLINKFeed contracts.VRFMockETHLINKFeed + LinkNativeFeedAddress string } type VRFOwnerConfig struct { diff --git a/integration-tests/actions/vrf/vrfv2/contract_steps.go b/integration-tests/actions/vrf/vrfv2/contract_steps.go index bab4de8624..25239537f4 100644 --- a/integration-tests/actions/vrf/vrfv2/contract_steps.go +++ b/integration-tests/actions/vrf/vrfv2/contract_steps.go @@ -635,7 +635,7 @@ func SetupNewConsumersAndSubs( ) ([]contracts.VRFv2LoadTestConsumer, []uint64, error) { consumers, err := DeployVRFV2Consumers(sethClient, coordinator.Address(), numberOfConsumerContractsToDeployAndAddToSub) if err != nil { - return nil, nil, fmt.Errorf("err: %w", err) + return nil, nil, err } l.Info(). Str("Coordinator", *testConfig.VRFv2.ExistingEnvConfig.ExistingEnvConfig.CoordinatorAddress). @@ -649,7 +649,7 @@ func SetupNewConsumersAndSubs( numberOfSubToCreate, ) if err != nil { - return nil, nil, fmt.Errorf("err: %w", err) + return nil, nil, err } return consumers, subIDs, nil } diff --git a/integration-tests/actions/vrf/vrfv2/setup_steps.go b/integration-tests/actions/vrf/vrfv2/setup_steps.go index 6f1edc6b04..97948c8a1f 100644 --- a/integration-tests/actions/vrf/vrfv2/setup_steps.go +++ b/integration-tests/actions/vrf/vrfv2/setup_steps.go @@ -8,15 +8,13 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/seth" - seth_utils "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/seth" - "github.com/ethereum/go-ethereum/common" "github.com/rs/zerolog" "golang.org/x/sync/errgroup" "github.com/google/uuid" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" testconfig "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2" @@ -44,7 +42,7 @@ func CreateVRFV2Job( } ost, err := os.String() if err != nil { - return nil, fmt.Errorf("%s, err %w", vrfcommon.ErrParseJob, err) + return nil, fmt.Errorf(vrfcommon.ErrGenericFormat, vrfcommon.ErrParseJob, err) } spec := &client.VRFV2JobSpec{ @@ -71,7 +69,7 @@ func CreateVRFV2Job( } job, err := chainlinkNode.MustCreateJob(spec) if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrCreatingVRFv2Job, err) + return nil, fmt.Errorf(vrfcommon.ErrGenericFormat, ErrCreatingVRFv2Job, err) } return job, nil } @@ -115,15 +113,14 @@ func SetupVRFV2Environment( if err != nil { return nil, nil, nil, err } - l.Info().Str("Coordinator", vrfContracts.CoordinatorV2.Address()).Msg("Registering Proving Key") provingKey, err := VRFV2RegisterProvingKey(vrfKey, registerProvingKeyAgainstAddress, vrfContracts.CoordinatorV2) if err != nil { - return nil, nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrRegisteringProvingKey, err) + return nil, nil, nil, fmt.Errorf(vrfcommon.ErrGenericFormat, vrfcommon.ErrRegisteringProvingKey, err) } keyHash, err := vrfContracts.CoordinatorV2.HashOfKey(ctx, provingKey) if err != nil { - return nil, nil, nil, fmt.Errorf("%s, err %w", vrfcommon.ErrCreatingProvingKeyHash, err) + return nil, nil, nil, fmt.Errorf(vrfcommon.ErrGenericFormat, vrfcommon.ErrCreatingProvingKeyHash, err) } vrfTXKeyAddressStrings, vrfTXKeyAddresses, err := vrfcommon.CreateFundAndGetSendingKeys( @@ -191,44 +188,47 @@ func SetupVRFV2Environment( return vrfContracts, &vrfKeyData, nodeTypeToNodeMap, nil } -func setupVRFNode(contracts *vrfcommon.VRFContracts, chainID *big.Int, vrfv2Config *testconfig.General, pubKeyCompressed string, vrfOwnerConfig *vrfcommon.VRFOwnerConfig, l zerolog.Logger, vrfNode *vrfcommon.VRFNode) error { +func setupVRFNode(contracts *vrfcommon.VRFContracts, chainID *big.Int, config *testconfig.General, pubKeyCompressed string, vrfOwnerConfig *vrfcommon.VRFOwnerConfig, l zerolog.Logger, vrfNode *vrfcommon.VRFNode) error { vrfJobSpecConfig := vrfcommon.VRFJobSpecConfig{ - ForwardingAllowed: *vrfv2Config.VRFJobForwardingAllowed, + ForwardingAllowed: *config.VRFJobForwardingAllowed, CoordinatorAddress: contracts.CoordinatorV2.Address(), BatchCoordinatorAddress: contracts.BatchCoordinatorV2.Address(), FromAddresses: vrfNode.TXKeyAddressStrings, EVMChainID: chainID.String(), - MinIncomingConfirmations: int(*vrfv2Config.MinimumConfirmations), + MinIncomingConfirmations: int(*config.MinimumConfirmations), PublicKey: pubKeyCompressed, - EstimateGasMultiplier: *vrfv2Config.VRFJobEstimateGasMultiplier, - BatchFulfillmentEnabled: *vrfv2Config.VRFJobBatchFulfillmentEnabled, - BatchFulfillmentGasMultiplier: *vrfv2Config.VRFJobBatchFulfillmentGasMultiplier, - PollPeriod: vrfv2Config.VRFJobPollPeriod.Duration, - RequestTimeout: vrfv2Config.VRFJobRequestTimeout.Duration, - SimulationBlock: vrfv2Config.VRFJobSimulationBlock, + EstimateGasMultiplier: *config.VRFJobEstimateGasMultiplier, + BatchFulfillmentEnabled: *config.VRFJobBatchFulfillmentEnabled, + BatchFulfillmentGasMultiplier: *config.VRFJobBatchFulfillmentGasMultiplier, + PollPeriod: config.VRFJobPollPeriod.Duration, + RequestTimeout: config.VRFJobRequestTimeout.Duration, + SimulationBlock: config.VRFJobSimulationBlock, VRFOwnerConfig: vrfOwnerConfig, } l.Info().Msg("Creating VRFV2 Job") - vrfV2job, err := CreateVRFV2Job( + job, err := CreateVRFV2Job( vrfNode.CLNode.API, vrfJobSpecConfig, ) if err != nil { return fmt.Errorf("%s, err %w", ErrCreateVRFV2Jobs, err) } - vrfNode.Job = vrfV2job + vrfNode.Job = job // this part is here because VRFv2 can work with only a specific key // [[EVM.KeySpecific]] // Key = '...' nodeConfig := node.NewConfig(vrfNode.CLNode.NodeConfig, - node.WithKeySpecificMaxGasPrice(vrfNode.TXKeyAddressStrings, *vrfv2Config.CLNodeMaxGasPriceGWei), + node.WithKeySpecificMaxGasPrice(vrfNode.TXKeyAddressStrings, *config.CLNodeMaxGasPriceGWei), ) - l.Info().Msg("Restarting Node with new sending key PriceMax configuration") + l.Info(). + Strs("Sending Keys", vrfNode.TXKeyAddressStrings). + Int64("Price Max Setting", *config.CLNodeMaxGasPriceGWei). + Msg("Restarting Node with new sending key PriceMax configuration") err = vrfNode.CLNode.Restart(nodeConfig) if err != nil { - return fmt.Errorf("%s, err %w", vrfcommon.ErrRestartCLNode, err) + return fmt.Errorf(vrfcommon.ErrGenericFormat, vrfcommon.ErrRestartCLNode, err) } return nil } @@ -371,38 +371,30 @@ func SetupVRFV2ForNewEnv( func SetupVRFV2ForExistingEnv(t *testing.T, envConfig vrfcommon.VRFEnvConfig, l zerolog.Logger) (*vrfcommon.VRFContracts, *vrfcommon.VRFKeyData, *test_env.CLClusterTestEnv, *seth.Client, error) { commonExistingEnvConfig := envConfig.TestConfig.VRFv2.ExistingEnvConfig.ExistingEnvConfig - env, err := test_env.NewCLTestEnvBuilder(). - WithTestInstance(t). - WithTestConfig(&envConfig.TestConfig). - WithCustomCleanup(envConfig.CleanupFn). - Build() - if err != nil { - return nil, nil, nil, nil, fmt.Errorf("%s, err: %w", "error creating test env", err) - } - evmNetwork, err := env.GetFirstEvmNetwork() - if err != nil { - return nil, nil, nil, nil, err - } - sethClient, err := seth_utils.GetChainClient(envConfig.TestConfig, *evmNetwork) + env, sethClient, err := vrfcommon.LoadExistingCLEnvForVRF( + t, + envConfig, + commonExistingEnvConfig, + l, + ) if err != nil { - return nil, nil, nil, nil, err + return nil, nil, nil, nil, fmt.Errorf("%s, err: %w", "error loading existing CL env", err) } - coordinator, err := contracts.LoadVRFCoordinatorV2(sethClient, *commonExistingEnvConfig.ConsumerAddress) + coordinator, err := contracts.LoadVRFCoordinatorV2(sethClient, *commonExistingEnvConfig.CoordinatorAddress) if err != nil { return nil, nil, nil, nil, fmt.Errorf("%s, err: %w", "error loading VRFCoordinator2", err) } - linkAddr := common.HexToAddress(*commonExistingEnvConfig.LinkAddress) - linkToken, err := contracts.LoadLinkTokenContract(l, sethClient, linkAddr) + linkAddress, err := coordinator.GetLinkAddress(testcontext.Get(t)) if err != nil { - return nil, nil, nil, nil, fmt.Errorf("%s, err: %w", "error loading LinkToken", err) + return nil, nil, nil, nil, fmt.Errorf("%s, err: %w", "error getting Link address from Coordinator", err) } - err = vrfcommon.FundNodesIfNeeded(testcontext.Get(t), commonExistingEnvConfig, sethClient, l) + linkToken, err := contracts.LoadLinkTokenContract(l, sethClient, common.HexToAddress(linkAddress.String())) if err != nil { - return nil, nil, nil, nil, fmt.Errorf("err: %w", err) + return nil, nil, nil, nil, fmt.Errorf("%s, err: %w", "error loading LinkToken", err) } blockHashStoreAddress, err := coordinator.GetBlockHashStoreAddress(testcontext.Get(t)) if err != nil { - return nil, nil, nil, nil, fmt.Errorf("err: %w", err) + return nil, nil, nil, nil, err } blockHashStore, err := contracts.LoadBlockHashStore(sethClient, blockHashStoreAddress.String()) if err != nil { @@ -449,13 +441,13 @@ func SetupSubsAndConsumersForExistingEnv( l, ) if err != nil { - return nil, nil, fmt.Errorf("err: %w", err) + return nil, nil, err } } else { addr := common.HexToAddress(*commonExistingEnvConfig.ConsumerAddress) consumer, err := contracts.LoadVRFv2LoadTestConsumer(sethClient, addr) if err != nil { - return nil, nil, fmt.Errorf("err: %w", err) + return nil, nil, err } consumers = append(consumers, consumer) subIDs = append(subIDs, *testConfig.VRFv2.ExistingEnvConfig.SubID) @@ -471,7 +463,7 @@ func SetupSubsAndConsumersForExistingEnv( l, ) if err != nil { - return nil, nil, fmt.Errorf("err: %w", err) + return nil, nil, err } } return subIDs, consumers, nil diff --git a/integration-tests/actions/vrf/vrfv2plus/contract_steps.go b/integration-tests/actions/vrf/vrfv2plus/contract_steps.go index 6aa9ef6c70..6b3345edd0 100644 --- a/integration-tests/actions/vrf/vrfv2plus/contract_steps.go +++ b/integration-tests/actions/vrf/vrfv2plus/contract_steps.go @@ -13,7 +13,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/seth" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/conversions" + "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" "github.com/smartcontractkit/chainlink/integration-tests/actions" vrfcommon "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/common" "github.com/smartcontractkit/chainlink/integration-tests/client" @@ -49,6 +49,24 @@ func DeployVRFV2_5Contracts( if err != nil { return nil, fmt.Errorf(vrfcommon.ErrGenericFormat, vrfcommon.ErrLoadingCoordinator, err) } + } else if actions.IsArbitrumChain(chainClient.ChainID) { + arbitrumCoordinator, err := contracts.DeployVRFCoordinatorV2_5_Arbitrum(chainClient, bhs.Address()) + if err != nil { + return nil, fmt.Errorf(vrfcommon.ErrGenericFormat, ErrDeployCoordinatorV2Plus, err) + } + coordinator, err = contracts.LoadVRFCoordinatorV2_5(chainClient, arbitrumCoordinator.Address.String()) + if err != nil { + return nil, fmt.Errorf(vrfcommon.ErrGenericFormat, vrfcommon.ErrLoadingCoordinator, err) + } + } else if *configGeneral.UseTestCoordinator { + testCoordinator, err := contracts.DeployVRFCoordinatorTestV2_5(chainClient, bhs.Address()) + if err != nil { + return nil, fmt.Errorf(vrfcommon.ErrGenericFormat, ErrDeployCoordinatorV2Plus, err) + } + coordinator, err = contracts.LoadVRFCoordinatorV2_5(chainClient, testCoordinator.Address.String()) + if err != nil { + return nil, fmt.Errorf(vrfcommon.ErrGenericFormat, vrfcommon.ErrLoadingCoordinator, err) + } } else { coordinator, err = contracts.DeployVRFCoordinatorV2_5(chainClient, bhs.Address()) if err != nil { @@ -57,7 +75,7 @@ func DeployVRFV2_5Contracts( } batchCoordinator, err := contracts.DeployBatchVRFCoordinatorV2Plus(chainClient, coordinator.Address()) if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrDeployBatchCoordinatorV2Plus, err) + return nil, fmt.Errorf(vrfcommon.ErrGenericFormat, ErrDeployBatchCoordinatorV2Plus, err) } return &vrfcommon.VRFContracts{ CoordinatorV2Plus: coordinator, @@ -408,7 +426,7 @@ func DeployVRFV2PlusDirectFundingContracts( linkTokenAddress string, linkEthFeedAddress string, coordinator contracts.VRFCoordinatorV2_5, - consumerContractsAmount int, + numberOfConsumerContracts int, wrapperSubId *big.Int, configGeneral *vrfv2plusconfig.General, ) (*VRFV2PlusWrapperContracts, error) { @@ -427,13 +445,22 @@ func DeployVRFV2PlusDirectFundingContracts( if err != nil { return nil, fmt.Errorf(vrfcommon.ErrGenericFormat, vrfcommon.ErrLoadingCoordinator, err) } + } else if actions.IsArbitrumChain(sethClient.ChainID) { + arbitrumWrapper, err := contracts.DeployVRFV2PlusWrapperArbitrum(sethClient, linkTokenAddress, linkEthFeedAddress, coordinator.Address(), wrapperSubId) + if err != nil { + return nil, fmt.Errorf(vrfcommon.ErrGenericFormat, ErrDeployCoordinatorV2Plus, err) + } + vrfv2PlusWrapper, err = contracts.LoadVRFV2PlusWrapper(sethClient, arbitrumWrapper.Address.String()) + if err != nil { + return nil, fmt.Errorf(vrfcommon.ErrGenericFormat, vrfcommon.ErrLoadingCoordinator, err) + } } else { vrfv2PlusWrapper, err = contracts.DeployVRFV2PlusWrapper(sethClient, linkTokenAddress, linkEthFeedAddress, coordinator.Address(), wrapperSubId) if err != nil { return nil, fmt.Errorf(vrfcommon.ErrGenericFormat, ErrDeployWrapper, err) } } - consumers, err := DeployVRFV2PlusWrapperConsumers(sethClient, vrfv2PlusWrapper, consumerContractsAmount) + consumers, err := DeployVRFV2PlusWrapperConsumers(sethClient, vrfv2PlusWrapper, numberOfConsumerContracts) if err != nil { return nil, err } @@ -546,9 +573,9 @@ func WaitRandomWordsFulfilledEvent( return randomWordsFulfilledEvent, err } -func DeployVRFV2PlusWrapperConsumers(client *seth.Client, vrfV2PlusWrapper contracts.VRFV2PlusWrapper, consumerContractsAmount int) ([]contracts.VRFv2PlusWrapperLoadTestConsumer, error) { +func DeployVRFV2PlusWrapperConsumers(client *seth.Client, vrfV2PlusWrapper contracts.VRFV2PlusWrapper, numberOfConsumerContracts int) ([]contracts.VRFv2PlusWrapperLoadTestConsumer, error) { var consumers []contracts.VRFv2PlusWrapperLoadTestConsumer - for i := 1; i <= consumerContractsAmount; i++ { + for i := 1; i <= numberOfConsumerContracts; i++ { loadTestConsumer, err := contracts.DeployVRFV2PlusWrapperLoadTestConsumer(client, vrfV2PlusWrapper.Address()) if err != nil { return nil, fmt.Errorf(vrfcommon.ErrGenericFormat, ErrAdvancedConsumer, err) @@ -610,7 +637,7 @@ func SetupNewConsumersAndSubs( ) ([]contracts.VRFv2PlusLoadTestConsumer, []*big.Int, error) { consumers, err := DeployVRFV2PlusConsumers(sethClient, coordinator, consumerContractsAmount) if err != nil { - return nil, nil, fmt.Errorf("err: %w", err) + return nil, nil, err } l.Info(). Str("Coordinator", *testConfig.VRFv2Plus.ExistingEnvConfig.ExistingEnvConfig.CoordinatorAddress). @@ -628,7 +655,7 @@ func SetupNewConsumersAndSubs( *testConfig.VRFv2Plus.General.SubscriptionBillingType, ) if err != nil { - return nil, nil, fmt.Errorf("err: %w", err) + return nil, nil, err } return consumers, subIDs, nil } @@ -653,3 +680,57 @@ func CancelSubsAndReturnFunds(ctx context.Context, vrfContracts *vrfcommon.VRFCo } } } + +func FundWrapperConsumer( + sethClient *seth.Client, + subFundingType string, + linkToken contracts.LinkToken, + wrapperConsumer contracts.VRFv2PlusWrapperLoadTestConsumer, + vrfv2PlusConfig *vrfv2plusconfig.General, + l zerolog.Logger, +) error { + fundConsumerWithLink := func() error { + //fund consumer with Link + linkAmount := big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(*vrfv2PlusConfig.WrapperConsumerFundingAmountLink)) + l.Info(). + Str("Link Amount", linkAmount.String()). + Str("WrapperConsumerAddress", wrapperConsumer.Address()).Msg("Funding WrapperConsumer with Link") + return linkToken.Transfer( + wrapperConsumer.Address(), + linkAmount, + ) + } + fundConsumerWithNative := func() error { + //fund consumer with Eth (native token) + _, err := actions.SendFunds(l, sethClient, actions.FundsToSendPayload{ + ToAddress: common.HexToAddress(wrapperConsumer.Address()), + Amount: conversions.EtherToWei(big.NewFloat(*vrfv2PlusConfig.WrapperConsumerFundingAmountNativeToken)), + PrivateKey: sethClient.PrivateKeys[0], + }) + return err + } + switch vrfv2plusconfig.BillingType(subFundingType) { + case vrfv2plusconfig.BillingType_Link: + err := fundConsumerWithLink() + if err != nil { + return err + } + case vrfv2plusconfig.BillingType_Native: + err := fundConsumerWithNative() + if err != nil { + return err + } + case vrfv2plusconfig.BillingType_Link_and_Native: + err := fundConsumerWithLink() + if err != nil { + return err + } + err = fundConsumerWithNative() + if err != nil { + return err + } + default: + return fmt.Errorf("invalid billing type: %s", subFundingType) + } + return nil +} diff --git a/integration-tests/actions/vrf/vrfv2plus/models.go b/integration-tests/actions/vrf/vrfv2plus/models.go index a2ca8ec582..5198439c05 100644 --- a/integration-tests/actions/vrf/vrfv2plus/models.go +++ b/integration-tests/actions/vrf/vrfv2plus/models.go @@ -5,6 +5,6 @@ import ( ) type VRFV2PlusWrapperContracts struct { - VRFV2PlusWrapper contracts.VRFV2PlusWrapper - LoadTestConsumers []contracts.VRFv2PlusWrapperLoadTestConsumer + VRFV2PlusWrapper contracts.VRFV2PlusWrapper + WrapperConsumers []contracts.VRFv2PlusWrapperLoadTestConsumer } diff --git a/integration-tests/actions/vrf/vrfv2plus/setup_steps.go b/integration-tests/actions/vrf/vrfv2plus/setup_steps.go index caa5e26a69..c937046434 100644 --- a/integration-tests/actions/vrf/vrfv2plus/setup_steps.go +++ b/integration-tests/actions/vrf/vrfv2plus/setup_steps.go @@ -8,8 +8,6 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/seth" - seth_utils "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/seth" - "github.com/shopspring/decimal" "golang.org/x/sync/errgroup" @@ -17,8 +15,7 @@ import ( "github.com/google/uuid" "github.com/rs/zerolog" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/conversions" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" vrfcommon "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/common" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" @@ -28,7 +25,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" - vrfv2plus_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2plus" + vrfv2plusconfig "github.com/smartcontractkit/chainlink/integration-tests/testconfig/vrfv2plus" "github.com/smartcontractkit/chainlink/integration-tests/types" ) @@ -66,12 +63,10 @@ func CreateVRFV2PlusJob( if vrfJobSpecConfig.BatchFulfillmentEnabled { jobSpec.BatchCoordinatorAddress = vrfJobSpecConfig.BatchCoordinatorAddress } - job, err := chainlinkNode.MustCreateJob(&jobSpec) if err != nil { return nil, fmt.Errorf(vrfcommon.ErrGenericFormat, ErrCreatingVRFv2PlusJob, err) } - return job, nil } @@ -90,7 +85,6 @@ func SetupVRFV2_5Environment( ) (*vrfcommon.VRFContracts, *vrfcommon.VRFKeyData, map[vrfcommon.VRFNodeType]*vrfcommon.VRFNode, error) { l.Info().Msg("Starting VRFV2 Plus environment setup") configGeneral := vrfv2PlusTestConfig.GetVRFv2PlusConfig().General - vrfContracts, err := SetupVRFV2PlusContracts( sethClient, linkToken, @@ -201,7 +195,7 @@ func SetupVRFV2_5Environment( return vrfContracts, &vrfKeyData, nodeTypeToNodeMap, nil } -func setupVRFNode(contracts *vrfcommon.VRFContracts, chainID *big.Int, config *vrfv2plus_config.General, pubKeyCompressed string, l zerolog.Logger, vrfNode *vrfcommon.VRFNode) error { +func setupVRFNode(contracts *vrfcommon.VRFContracts, chainID *big.Int, config *vrfv2plusconfig.General, pubKeyCompressed string, l zerolog.Logger, vrfNode *vrfcommon.VRFNode) error { vrfJobSpecConfig := vrfcommon.VRFJobSpecConfig{ ForwardingAllowed: *config.VRFJobForwardingAllowed, CoordinatorAddress: contracts.CoordinatorV2Plus.Address(), @@ -235,7 +229,10 @@ func setupVRFNode(contracts *vrfcommon.VRFContracts, chainID *big.Int, config *v nodeConfig := node.NewConfig(vrfNode.CLNode.NodeConfig, node.WithKeySpecificMaxGasPrice(vrfNode.TXKeyAddressStrings, *config.CLNodeMaxGasPriceGWei), ) - l.Info().Msg("Restarting Node with new sending key PriceMax configuration") + l.Info(). + Strs("Sending Keys", vrfNode.TXKeyAddressStrings). + Int64("Price Max Setting", *config.CLNodeMaxGasPriceGWei). + Msg("Restarting Node with new sending key PriceMax configuration") err = vrfNode.CLNode.Restart(nodeConfig) if err != nil { return fmt.Errorf(vrfcommon.ErrGenericFormat, vrfcommon.ErrRestartCLNode, err) @@ -243,43 +240,131 @@ func setupVRFNode(contracts *vrfcommon.VRFContracts, chainID *big.Int, config *v return nil } -func SetupVRFV2PlusWrapperEnvironment( +func SetupVRFV2PlusWrapperForExistingEnv( ctx context.Context, + sethClient *seth.Client, + vrfContracts *vrfcommon.VRFContracts, + keyHash [32]byte, + vrfv2PlusTestConfig types.VRFv2PlusTestConfig, + numberOfConsumerContracts int, l zerolog.Logger, +) (*VRFV2PlusWrapperContracts, *big.Int, error) { + config := *vrfv2PlusTestConfig.GetVRFv2PlusConfig() + var wrapper contracts.VRFV2PlusWrapper + var err error + if *config.ExistingEnvConfig.UseExistingWrapper { + wrapper, err = contracts.LoadVRFV2PlusWrapper(sethClient, *config.ExistingEnvConfig.WrapperAddress) + if err != nil { + return nil, nil, fmt.Errorf(vrfcommon.ErrGenericFormat, "error loading VRFV2PlusWrapper", err) + } + } else { + wrapperSubId, err := CreateSubAndFindSubID(ctx, sethClient, vrfContracts.CoordinatorV2Plus) + if err != nil { + return nil, nil, err + } + wrapper, err = contracts.DeployVRFV2PlusWrapper(sethClient, vrfContracts.LinkToken.Address(), vrfContracts.LinkNativeFeedAddress, vrfContracts.CoordinatorV2Plus.Address(), wrapperSubId) + if err != nil { + return nil, nil, fmt.Errorf(vrfcommon.ErrGenericFormat, ErrDeployWrapper, err) + } + err = FundSubscriptions( + big.NewFloat(*config.General.SubscriptionFundingAmountNative), + big.NewFloat(*config.General.SubscriptionFundingAmountLink), + vrfContracts.LinkToken, + vrfContracts.CoordinatorV2Plus, + []*big.Int{wrapperSubId}, + *config.General.SubscriptionBillingType, + ) + if err != nil { + return nil, nil, err + } + err = vrfContracts.CoordinatorV2Plus.AddConsumer(wrapperSubId, wrapper.Address()) + if err != nil { + return nil, nil, err + } + err = wrapper.SetConfig( + *config.General.WrapperGasOverhead, + *config.General.CoordinatorGasOverheadNative, + *config.General.CoordinatorGasOverheadLink, + *config.General.CoordinatorGasOverheadPerWord, + *config.General.CoordinatorNativePremiumPercentage, + *config.General.CoordinatorLinkPremiumPercentage, + keyHash, + *config.General.WrapperMaxNumberOfWords, + *config.General.StalenessSeconds, + decimal.RequireFromString(*config.General.FallbackWeiPerUnitLink).BigInt(), + *config.General.FulfillmentFlatFeeNativePPM, + *config.General.FulfillmentFlatFeeLinkDiscountPPM, + ) + if err != nil { + return nil, nil, err + } + } + wrapperSubID, err := wrapper.GetSubID(ctx) + if err != nil { + return nil, nil, fmt.Errorf(vrfcommon.ErrGenericFormat, "error getting subID", err) + } + var wrapperConsumers []contracts.VRFv2PlusWrapperLoadTestConsumer + if *config.ExistingEnvConfig.CreateFundAddWrapperConsumers { + wrapperConsumers, err = DeployVRFV2PlusWrapperConsumers(sethClient, wrapper, numberOfConsumerContracts) + if err != nil { + return nil, nil, err + } + } else { + wrapperConsumer, err := contracts.LoadVRFV2WrapperLoadTestConsumer(sethClient, *config.ExistingEnvConfig.WrapperConsumerAddress) + if err != nil { + return nil, nil, fmt.Errorf(vrfcommon.ErrGenericFormat, "error loading VRFV2WrapperLoadTestConsumer", err) + } + wrapperConsumers = append(wrapperConsumers, wrapperConsumer) + } + wrapperContracts := &VRFV2PlusWrapperContracts{wrapper, wrapperConsumers} + for _, consumer := range wrapperConsumers { + err = FundWrapperConsumer( + sethClient, + *config.General.SubscriptionBillingType, + vrfContracts.LinkToken, + consumer, + config.General, + l, + ) + if err != nil { + return nil, nil, err + } + } + return wrapperContracts, wrapperSubID, nil +} + +func SetupVRFV2PlusWrapperForNewEnv( + ctx context.Context, sethClient *seth.Client, vrfv2PlusTestConfig types.VRFv2PlusTestConfig, - linkToken contracts.LinkToken, - mockNativeLINKFeed contracts.MockETHLINKFeed, - coordinator contracts.VRFCoordinatorV2_5, + vrfContracts *vrfcommon.VRFContracts, keyHash [32]byte, wrapperConsumerContractsAmount int, + l zerolog.Logger, ) (*VRFV2PlusWrapperContracts, *big.Int, error) { // external EOA has to create a subscription for the wrapper first - wrapperSubId, err := CreateSubAndFindSubID(ctx, sethClient, coordinator) + wrapperSubId, err := CreateSubAndFindSubID(ctx, sethClient, vrfContracts.CoordinatorV2Plus) if err != nil { return nil, nil, err } - vrfv2PlusConfig := vrfv2PlusTestConfig.GetVRFv2PlusConfig().General wrapperContracts, err := DeployVRFV2PlusDirectFundingContracts( sethClient, - linkToken.Address(), - mockNativeLINKFeed.Address(), - coordinator, + vrfContracts.LinkToken.Address(), + vrfContracts.MockETHLINKFeed.Address(), + vrfContracts.CoordinatorV2Plus, wrapperConsumerContractsAmount, wrapperSubId, vrfv2PlusConfig, ) if err != nil { - return nil, nil, fmt.Errorf(vrfcommon.ErrGenericFormat, vrfcommon.ErrWaitTXsComplete, err) + return nil, nil, err } - // once the wrapper is deployed, wrapper address will become consumer of external EOA subscription - err = coordinator.AddConsumer(wrapperSubId, wrapperContracts.VRFV2PlusWrapper.Address()) + err = vrfContracts.CoordinatorV2Plus.AddConsumer(wrapperSubId, wrapperContracts.VRFV2PlusWrapper.Address()) if err != nil { return nil, nil, err } - err = wrapperContracts.VRFV2PlusWrapper.SetConfig( *vrfv2PlusConfig.WrapperGasOverhead, *vrfv2PlusConfig.CoordinatorGasOverheadNative, @@ -297,53 +382,35 @@ func SetupVRFV2PlusWrapperEnvironment( if err != nil { return nil, nil, err } - //fund sub wrapperSubID, err := wrapperContracts.VRFV2PlusWrapper.GetSubID(ctx) if err != nil { return nil, nil, err } - err = FundSubscriptions( big.NewFloat(*vrfv2PlusTestConfig.GetVRFv2PlusConfig().General.SubscriptionFundingAmountNative), big.NewFloat(*vrfv2PlusTestConfig.GetVRFv2PlusConfig().General.SubscriptionFundingAmountLink), - linkToken, - coordinator, + vrfContracts.LinkToken, + vrfContracts.CoordinatorV2Plus, []*big.Int{wrapperSubID}, *vrfv2PlusConfig.SubscriptionBillingType, ) if err != nil { return nil, nil, err } - - //fund consumer with Link - err = linkToken.Transfer( - wrapperContracts.LoadTestConsumers[0].Address(), - big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(*vrfv2PlusConfig.WrapperConsumerFundingAmountLink)), - ) - if err != nil { - return nil, nil, err - } - - //fund consumer with Eth (native token) - _, err = actions.SendFunds(l, sethClient, actions.FundsToSendPayload{ - ToAddress: common.HexToAddress(wrapperContracts.LoadTestConsumers[0].Address()), - Amount: conversions.EtherToWei(big.NewFloat(*vrfv2PlusConfig.WrapperConsumerFundingAmountNativeToken)), - PrivateKey: sethClient.PrivateKeys[0], - }) - if err != nil { - return nil, nil, err - } - - wrapperConsumerBalanceBeforeRequestWei, err := sethClient.Client.BalanceAt(ctx, common.HexToAddress(wrapperContracts.LoadTestConsumers[0].Address()), nil) - if err != nil { - return nil, nil, err + for _, consumer := range wrapperContracts.WrapperConsumers { + err = FundWrapperConsumer( + sethClient, + *vrfv2PlusConfig.SubscriptionBillingType, + vrfContracts.LinkToken, + consumer, + vrfv2PlusConfig, + l, + ) + if err != nil { + return nil, nil, err + } } - l.Info(). - Str("WrapperConsumerBalanceBeforeRequestWei", wrapperConsumerBalanceBeforeRequestWei.String()). - Str("WrapperConsumerAddress", wrapperContracts.LoadTestConsumers[0].Address()). - Msg("WrapperConsumerBalanceBeforeRequestWei") - return wrapperContracts, wrapperSubID, nil } @@ -421,47 +488,45 @@ func SetupVRFV2PlusForNewEnv( func SetupVRFV2PlusForExistingEnv(t *testing.T, envConfig vrfcommon.VRFEnvConfig, l zerolog.Logger) (*vrfcommon.VRFContracts, *vrfcommon.VRFKeyData, *test_env.CLClusterTestEnv, *seth.Client, error) { commonExistingEnvConfig := envConfig.TestConfig.VRFv2Plus.ExistingEnvConfig.ExistingEnvConfig - env, err := test_env.NewCLTestEnvBuilder(). - WithTestInstance(t). - WithTestConfig(&envConfig.TestConfig). - WithCustomCleanup(envConfig.CleanupFn). - Build() - if err != nil { - return nil, nil, nil, nil, fmt.Errorf("%s, err: %w", "error creating test env", err) - } - evmNetwork, err := env.GetFirstEvmNetwork() - if err != nil { - return nil, nil, nil, nil, err - } - sethClient, err := seth_utils.GetChainClient(envConfig.TestConfig, *evmNetwork) + env, sethClient, err := vrfcommon.LoadExistingCLEnvForVRF( + t, + envConfig, + commonExistingEnvConfig, + l, + ) if err != nil { - return nil, nil, nil, nil, err + return nil, nil, nil, nil, fmt.Errorf("%s, err: %w", "error loading existing CL env", err) } coordinator, err := contracts.LoadVRFCoordinatorV2_5(sethClient, *commonExistingEnvConfig.CoordinatorAddress) if err != nil { return nil, nil, nil, nil, fmt.Errorf("%s, err: %w", "error loading VRFCoordinator2_5", err) } - linkToken, err := contracts.LoadLinkTokenContract(l, sethClient, common.HexToAddress(*commonExistingEnvConfig.LinkAddress)) + linkAddress, err := coordinator.GetLinkAddress(testcontext.Get(t)) + if err != nil { + return nil, nil, nil, nil, fmt.Errorf("%s, err: %w", "error getting Link address from Coordinator", err) + } + linkToken, err := contracts.LoadLinkTokenContract(l, sethClient, common.HexToAddress(linkAddress.String())) if err != nil { return nil, nil, nil, nil, fmt.Errorf("%s, err: %w", "error loading LinkToken", err) } - err = vrfcommon.FundNodesIfNeeded(testcontext.Get(t), commonExistingEnvConfig, sethClient, l) + linkNativeFeedAddress, err := coordinator.GetLinkNativeFeed(testcontext.Get(t)) if err != nil { - return nil, nil, nil, nil, fmt.Errorf("err: %w", err) + return nil, nil, nil, nil, fmt.Errorf("%s, err: %w", "error getting Link address from Coordinator", err) } blockHashStoreAddress, err := coordinator.GetBlockHashStoreAddress(testcontext.Get(t)) if err != nil { - return nil, nil, nil, nil, fmt.Errorf("err: %w", err) + return nil, nil, nil, nil, err } blockHashStore, err := contracts.LoadBlockHashStore(sethClient, blockHashStoreAddress.String()) if err != nil { return nil, nil, nil, nil, fmt.Errorf("%s, err: %w", "error loading BlockHashStore", err) } vrfContracts := &vrfcommon.VRFContracts{ - CoordinatorV2Plus: coordinator, - VRFV2PlusConsumer: nil, - LinkToken: linkToken, - BHS: blockHashStore, + CoordinatorV2Plus: coordinator, + VRFV2PlusConsumer: nil, + LinkToken: linkToken, + BHS: blockHashStore, + LinkNativeFeedAddress: linkNativeFeedAddress.String(), } vrfKey := &vrfcommon.VRFKeyData{ VRFKey: nil, @@ -500,12 +565,12 @@ func SetupSubsAndConsumersForExistingEnv( l, ) if err != nil { - return nil, nil, fmt.Errorf("err: %w", err) + return nil, nil, err } } else { consumer, err := contracts.LoadVRFv2PlusLoadTestConsumer(sethClient, *commonExistingEnvConfig.ConsumerAddress) if err != nil { - return nil, nil, fmt.Errorf("err: %w", err) + return nil, nil, err } consumers = append(consumers, consumer) var ok bool @@ -527,21 +592,65 @@ func SetupSubsAndConsumersForExistingEnv( l, ) if err != nil { - return nil, nil, fmt.Errorf("err: %w", err) + return nil, nil, err } } return subIDs, consumers, nil } func SelectBillingTypeWithDistribution(billingType string, distributionFn func() bool) (bool, error) { - switch vrfv2plus_config.BillingType(billingType) { - case vrfv2plus_config.BillingType_Link: + switch vrfv2plusconfig.BillingType(billingType) { + case vrfv2plusconfig.BillingType_Link: return false, nil - case vrfv2plus_config.BillingType_Native: + case vrfv2plusconfig.BillingType_Native: return true, nil - case vrfv2plus_config.BillingType_Link_and_Native: + case vrfv2plusconfig.BillingType_Link_and_Native: return distributionFn(), nil default: return false, fmt.Errorf("invalid billing type: %s", billingType) } } + +func SetupVRFV2PlusWrapperUniverse( + ctx context.Context, + sethClient *seth.Client, + vrfContracts *vrfcommon.VRFContracts, + config *tc.TestConfig, + keyHash [32]byte, + numberOfConsumerContracts int, + l zerolog.Logger, +) (*VRFV2PlusWrapperContracts, *big.Int, error) { + var ( + wrapperContracts *VRFV2PlusWrapperContracts + wrapperSubID *big.Int + err error + ) + if *config.VRFv2Plus.General.UseExistingEnv { + wrapperContracts, wrapperSubID, err = SetupVRFV2PlusWrapperForExistingEnv( + ctx, + sethClient, + vrfContracts, + keyHash, + config, + numberOfConsumerContracts, + l, + ) + if err != nil { + return nil, nil, err + } + } else { + wrapperContracts, wrapperSubID, err = SetupVRFV2PlusWrapperForNewEnv( + ctx, + sethClient, + config, + vrfContracts, + keyHash, + numberOfConsumerContracts, + l, + ) + if err != nil { + return nil, nil, err + } + } + return wrapperContracts, wrapperSubID, nil +} diff --git a/integration-tests/benchmark/keeper_test.go b/integration-tests/benchmark/keeper_test.go index 7c947d0b64..ab54885b28 100644 --- a/integration-tests/benchmark/keeper_test.go +++ b/integration-tests/benchmark/keeper_test.go @@ -9,27 +9,27 @@ import ( "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-testing-framework/lib/blockchain" - ctf_config "github.com/smartcontractkit/chainlink-testing-framework/lib/config" - env_client "github.com/smartcontractkit/chainlink-testing-framework/lib/k8s/client" - "github.com/smartcontractkit/chainlink-testing-framework/lib/k8s/environment" - "github.com/smartcontractkit/chainlink-testing-framework/lib/k8s/pkg/helm/chainlink" - "github.com/smartcontractkit/chainlink-testing-framework/lib/k8s/pkg/helm/ethereum" - "github.com/smartcontractkit/chainlink-testing-framework/lib/k8s/pkg/helm/reorg" - "github.com/smartcontractkit/chainlink-testing-framework/lib/logging" - "github.com/smartcontractkit/chainlink-testing-framework/lib/networks" - seth_utils "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/seth" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + ctfconfig "github.com/smartcontractkit/chainlink-testing-framework/config" + envclient "github.com/smartcontractkit/chainlink-testing-framework/k8s/client" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/reorg" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/networks" + sethutils "github.com/smartcontractkit/chainlink-testing-framework/utils/seth" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/contracts" - eth_contracts "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" + ethcontracts "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" "github.com/smartcontractkit/chainlink/integration-tests/testsetups" "github.com/smartcontractkit/chainlink/integration-tests/types" ) var ( - performanceChainlinkResources = map[string]interface{}{ + chainlinkResources = map[string]interface{}{ "resources": map[string]interface{}{ "requests": map[string]interface{}{ "cpu": "1000m", @@ -41,7 +41,7 @@ var ( }, }, } - performanceDbResources = map[string]interface{}{ + dbResources = map[string]interface{}{ "resources": map[string]interface{}{ "requests": map[string]interface{}{ "cpu": "1000m", @@ -55,33 +55,6 @@ var ( "stateful": true, "capacity": "10Gi", } - - soakChainlinkResources = map[string]interface{}{ - "resources": map[string]interface{}{ - "requests": map[string]interface{}{ - "cpu": "350m", - "memory": "1Gi", - }, - "limits": map[string]interface{}{ - "cpu": "350m", - "memory": "1Gi", - }, - }, - } - soakDbResources = map[string]interface{}{ - "resources": map[string]interface{}{ - "requests": map[string]interface{}{ - "cpu": "250m", - "memory": "256Mi", - }, - "limits": map[string]interface{}{ - "cpu": "250m", - "memory": "256Mi", - }, - }, - "stateful": true, - "capacity": "10Gi", - } ) type NetworkConfig struct { @@ -115,9 +88,9 @@ func TestAutomationBenchmark(t *testing.T) { benchmarkTestNetwork := getNetworkConfig(&config) l.Info().Str("Namespace", testEnvironment.Cfg.Namespace).Msg("Connected to Keepers Benchmark Environment") - testNetwork := seth_utils.MustReplaceSimulatedNetworkUrlWithK8(l, benchmarkNetwork, *testEnvironment) + testNetwork := sethutils.MustReplaceSimulatedNetworkUrlWithK8(l, benchmarkNetwork, *testEnvironment) - chainClient, err := seth_utils.GetChainClientWithConfigFunction(&config, testNetwork, seth_utils.OneEphemeralKeysLiveTestnetAutoFixFn) + chainClient, err := sethutils.GetChainClientWithConfigFunction(&config, testNetwork, sethutils.OneEphemeralKeysLiveTestnetAutoFixFn) require.NoError(t, err, "Error getting Seth client") registryVersions := addRegistry(&config) @@ -167,46 +140,53 @@ func TestAutomationBenchmark(t *testing.T) { t.Cleanup(func() { if err = actions.TeardownRemoteSuite(keeperBenchmarkTest.TearDownVals(t)); err != nil { l.Error().Err(err).Msg("Error when tearing down remote suite") + } else { + err := testEnvironment.Client.RemoveNamespace(testEnvironment.Cfg.Namespace) + if err != nil { + l.Error().Err(err).Msg("Error removing namespace") + } } }) keeperBenchmarkTest.Setup(testEnvironment, &config) keeperBenchmarkTest.Run() } -func addRegistry(config *tc.TestConfig) []eth_contracts.KeeperRegistryVersion { +func addRegistry(config *tc.TestConfig) []ethcontracts.KeeperRegistryVersion { switch *config.Keeper.Common.RegistryToTest { case "1_1": - return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_1_1} + return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_1_1} case "1_2": - return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_1_2} + return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_1_2} case "1_3": - return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_1_3} + return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_1_3} case "2_0": - return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_0} + return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_2_0} case "2_1": - return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_1} + return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_2_1} case "2_2": - return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_2} + return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_2_2} + case "2_3": + return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_2_3} case "2_0-1_3": - return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_0, eth_contracts.RegistryVersion_1_3} + return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_2_0, ethcontracts.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} + return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_2_1, + ethcontracts.RegistryVersion_2_0, ethcontracts.RegistryVersion_1_3} case "2_2-2_1": - return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_2, eth_contracts.RegistryVersion_2_1} + return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_2_2, ethcontracts.RegistryVersion_2_1} case "2_0-Multiple": - return repeatRegistries(eth_contracts.RegistryVersion_2_0, *config.Keeper.Common.NumberOfRegistries) + return repeatRegistries(ethcontracts.RegistryVersion_2_0, *config.Keeper.Common.NumberOfRegistries) case "2_1-Multiple": - return repeatRegistries(eth_contracts.RegistryVersion_2_1, *config.Keeper.Common.NumberOfRegistries) + return repeatRegistries(ethcontracts.RegistryVersion_2_1, *config.Keeper.Common.NumberOfRegistries) case "2_2-Multiple": - return repeatRegistries(eth_contracts.RegistryVersion_2_2, *config.Keeper.Common.NumberOfRegistries) + return repeatRegistries(ethcontracts.RegistryVersion_2_2, *config.Keeper.Common.NumberOfRegistries) default: - return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_0} + return []ethcontracts.KeeperRegistryVersion{ethcontracts.RegistryVersion_2_0} } } -func repeatRegistries(registryVersion eth_contracts.KeeperRegistryVersion, numberOfRegistries int) []eth_contracts.KeeperRegistryVersion { - repeatedRegistries := make([]eth_contracts.KeeperRegistryVersion, 0) +func repeatRegistries(registryVersion ethcontracts.KeeperRegistryVersion, numberOfRegistries int) []ethcontracts.KeeperRegistryVersion { + repeatedRegistries := make([]ethcontracts.KeeperRegistryVersion, 0) for i := 0; i < numberOfRegistries; i++ { repeatedRegistries = append(repeatedRegistries, registryVersion) } @@ -288,6 +268,11 @@ var networkConfig = map[string]NetworkConfig{ blockTime: time.Second, deltaStage: 20 * time.Second, }, + networks.ScrollSepolia.Name: { + upkeepSLA: int64(120), + blockTime: 3 * time.Second, + deltaStage: 20 * time.Second, + }, } func SetupAutomationBenchmarkEnv(t *testing.T, keeperTestConfig types.KeeperBenchmarkTestConfig) (*environment.Environment, blockchain.EVMNetwork) { @@ -316,12 +301,8 @@ func SetupAutomationBenchmarkEnv(t *testing.T, keeperTestConfig types.KeeperBenc PreventPodEviction: true, }) - dbResources := performanceDbResources - chainlinkResources := performanceChainlinkResources - if strings.Contains(strings.ToLower(strings.Join(keeperTestConfig.GetConfigurationNames(), ",")), "soak") { - chainlinkResources = soakChainlinkResources - dbResources = soakDbResources - } + dbResources := dbResources + chainlinkResources := chainlinkResources // Test can run on simulated, simulated-non-dev, testnets if testNetwork.Name == networks.SimulatedEVMNonDev.Name { @@ -389,10 +370,10 @@ func SetupAutomationBenchmarkEnv(t *testing.T, keeperTestConfig types.KeeperBenc // for simulated-nod-dev each CL node gets its own RPC node if testNetwork.Name == networks.SimulatedEVMNonDev.Name { podName := fmt.Sprintf("%s-ethereum-geth:%d", testNetwork.Name, i) - txNodeInternalWs, err := testEnvironment.Fwd.FindPort(podName, "geth", "ws-rpc").As(env_client.RemoteConnection, env_client.WS) + txNodeInternalWs, err := testEnvironment.Fwd.FindPort(podName, "geth", "ws-rpc").As(envclient.RemoteConnection, envclient.WS) require.NoError(t, err, "Error finding WS ports") internalWsURLs = append(internalWsURLs, txNodeInternalWs) - txNodeInternalHttp, err := testEnvironment.Fwd.FindPort(podName, "geth", "http-rpc").As(env_client.RemoteConnection, env_client.HTTP) + txNodeInternalHttp, err := testEnvironment.Fwd.FindPort(podName, "geth", "http-rpc").As(envclient.RemoteConnection, envclient.HTTP) require.NoError(t, err, "Error finding HTTP ports") internalHttpURLs = append(internalHttpURLs, txNodeInternalHttp) // for testnets with more than 1 RPC nodes @@ -412,8 +393,8 @@ func SetupAutomationBenchmarkEnv(t *testing.T, keeperTestConfig types.KeeperBenc testNetwork.URLs = []string{internalWsURLs[i]} var overrideFn = func(_ interface{}, target interface{}) { - ctf_config.MustConfigOverrideChainlinkVersion(keeperTestConfig.GetChainlinkImageConfig(), target) - ctf_config.MightConfigOverridePyroscopeKey(keeperTestConfig.GetPyroscopeConfig(), target) + ctfconfig.MustConfigOverrideChainlinkVersion(keeperTestConfig.GetChainlinkImageConfig(), target) + ctfconfig.MightConfigOverridePyroscopeKey(keeperTestConfig.GetPyroscopeConfig(), target) } tomlConfig, err := actions.BuildTOMLNodeConfigForK8s(keeperTestConfig, testNetwork) diff --git a/integration-tests/ccip-tests/Makefile b/integration-tests/ccip-tests/Makefile index 40b4cc8338..1acd566af3 100644 --- a/integration-tests/ccip-tests/Makefile +++ b/integration-tests/ccip-tests/Makefile @@ -1,5 +1,5 @@ ## To Override the default config: -# example usage: make set_config override_toml=../config/config.toml +# example usage: make set_config override_toml=../config/config.toml network_config_toml=../config/network.toml .PHONY: set_config set_config: if [ -s "$(override_toml)" ]; then \ @@ -12,6 +12,10 @@ set_config: echo "No override config found, using default config"; \ echo > ./testconfig/override/.env; \ fi + if [ -s "$(network_config_toml)" ]; then \ + echo "Overriding network config with $(network_config_toml)"; \ + echo "export BASE64_NETWORK_CONFIG=$$(base64 -i $(network_config_toml))" >> ./testconfig/override/.env; \ + fi @echo "Checking for test secrets file in ~/.testsecrets..."; @if [ ! -f ~/.testsecrets ]; then \ diff --git a/integration-tests/ccip-tests/README.md b/integration-tests/ccip-tests/README.md index 9c4da98dcb..cef4c95588 100644 --- a/integration-tests/ccip-tests/README.md +++ b/integration-tests/ccip-tests/README.md @@ -35,11 +35,21 @@ For example, if you want to override the `Network` input in test and want to run ``` 3. Secrets - You also need to set some secrets. This is a mandatory step needed to run the tests. Please refer to [.testsecrets.example](./examples/.testsecrets.example) for the list of secrets and instruction how to set them up. - - The chainlink image is a required secret for all tests + - The chainlink image and tag are required secrets for all the tests. - If you are running tests in live networks like testnet and mainnet, you need to set the secrets (rpc urls and private keys) for the respective networks. + - If you are running tests in simulated networks no network specific secrets are required. + here is a sample secrets.toml file, for running the tests in simulated networks, with the chainlink image and tag set as secrets: **Please note that the secrets should NOT be checked in to the repo and should be kept locally.** +We recommend against changing the content of [secrets.toml.example](./testconfig/examples/secrets.toml.example). Please create a new file and set it as the secrets file. + +You can run this command to ignore any changes to the file. + +```bash +git update-index --skip-worktree +``` + ## Running the Tests There are two ways to run the tests: diff --git a/integration-tests/ccip-tests/chaos/ccip_test.go b/integration-tests/ccip-tests/chaos/ccip_test.go index 125e683dc5..4b1dda7a91 100644 --- a/integration-tests/ccip-tests/chaos/ccip_test.go +++ b/integration-tests/ccip-tests/chaos/ccip_test.go @@ -7,9 +7,9 @@ import ( "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-testing-framework/lib/k8s/chaos" - "github.com/smartcontractkit/chainlink-testing-framework/lib/logging" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/ptr" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/chaos" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" "github.com/smartcontractkit/chainlink/integration-tests/ccip-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/ccip-tests/testconfig" diff --git a/integration-tests/ccip-tests/testconfig/README.md b/integration-tests/ccip-tests/testconfig/README.md index cf1431baa9..1363cbfde5 100644 --- a/integration-tests/ccip-tests/testconfig/README.md +++ b/integration-tests/ccip-tests/testconfig/README.md @@ -110,7 +110,7 @@ Or, DataFile = '' ``` -## CCIP.Env +## CCIP.Env Specifies the environment details for the test to be run on. Mandatory fields are: @@ -119,7 +119,16 @@ Mandatory fields are: - **NewCLCluster**: [CCIP.Env.NewCLCluster](#ccipenvnewclcluster) - This is mandatory if the test needs to deploy Chainlink nodes. - **ExistingCLCluster**: [CCIP.Env.ExistingCLCluster](#ccipenvexistingclcluster) - This is mandatory if the test needs to run on existing Chainlink nodes to deploy ccip jobs. -Test needs network/chain details to be set through configuration. Set network urls in ~/.testsecrets [see docs](https://github.com/smartcontractkit/chainlink-testing-framework/blob/main/config/README.md#test-secrets). +Test needs network/chain details to be set through configuration. This configuration is mandatory for running the tests. +you have option to set the network details in two ways: + +1. Using [CCIP.Env.Networks](#ccipenvnetworks) +2. Using a separate network config file - + - refer to the example - [network_config.toml.example](./examples/network_config.toml.example) + - once all necessary values are set, encode the toml file content in base64 format, + - set the base64'ed string content in `BASE64_NETWORK_CONFIG` environment variable. + +Set network urls in ~/.testsecrets [see docs](https://github.com/smartcontractkit/chainlink-testing-framework/blob/main/config/README.md#test-secrets). ### CCIP.Env.Networks @@ -139,7 +148,7 @@ If the network is not present in known_networks, then the network details can be #### CCIP.Env.Network.EVMNetworks -Specifies the network config to be used while creating blockchain EVMClient for test. +Specifies the network config to be used while creating blockchain EVMClient for test. It is a map of network name to EVMNetworks where key is network name specified under `CCIP.Env.Networks.selected_networks` and value is `EVMNetwork`. The EVMNetwork is imported from [EVMNetwork](https://github.com/smartcontractkit/chainlink-testing-framework/blob/main/blockchain/config.go#L43) in chainlink-testing-framework. @@ -743,6 +752,11 @@ Specifies the duration network delay used for `NetworkChaos` experiment. This is If there are multiple chaos experiments, this specifies the duration to wait between each chaos experiment. This is only valid if the test is run on k8s and not on [existing deployments](#ccipgroupstestgroupexistingdeployment). +#### CCIP.Groups.[testgroup].LoadProfile.SkipRequestIfAnotherRequestTriggeredWithin + +If a request is triggered within this duration, the test will skip sending another request during load run. For Example, if `SkipRequestIfAnotherRequestTriggeredWithin` is set to `40m`, and a request is triggered at 0th second, the test will skip sending another request for another 40m. +This particular field is used to avoid sending multiple requests in a short duration during load run. + #### CCIP.Groups.[testgroup].LoadProfile.OptimizeSpace This is used internally to optimize memory usage during load run. If set to true, after the initial lane set up is over the test will discard the lane config to save memory. diff --git a/integration-tests/ccip-tests/testconfig/examples/network_config.toml.example b/integration-tests/ccip-tests/testconfig/examples/network_config.toml.example new file mode 100644 index 0000000000..ffed99a771 --- /dev/null +++ b/integration-tests/ccip-tests/testconfig/examples/network_config.toml.example @@ -0,0 +1,168 @@ +[RpcHttpUrls] +ETHEREUM_MAINNET = [ + 'https:....', + 'https:......', +] +AVALANCHE_MAINNET = [ + 'https:....', + 'https:......', +] +BASE_MAINNET = [ + 'https:....', + 'https:......', +] +ARBITRUM_MAINNET = [ + 'https:....', + 'https:......', +] +BSC_MAINNET = [ + 'https:....', + 'https:......', +] +OPTIMISM_MAINNET = [ + 'https:....', + 'https:......', +] +POLYGON_MAINNET = [ + 'https:....', + 'https:......', +] +WEMIX_MAINNET = [ + 'https:....', + 'https:......', +] +KROMA_MAINNET = [ + 'https:....', + 'https:......', +] + +OPTIMISM_SEPOLIA = [ + 'https:....', + 'https:......', +] +SEPOLIA = [ + 'https:....', + 'https:......', +] +AVALANCHE_FUJI = [ + 'https:....', + 'https:......', +] +ARBITRUM_SEPOLIA = [ + 'https:....', + 'https:......', +] +POLYGON_MUMBAI = [ + 'https:....', + 'https:......', +] +BASE_SEPOLIA = [ + 'https:....', + 'https:......', +] +BSC_TESTNET = [ + 'https:....', + 'https:......', +] +KROMA_SEPOLIA = [ + 'https:....', + 'https:......', +] +WEMIX_TESTNET = [ + 'https:....', + 'https:......', +] + +[RpcWsUrls] +ETHEREUM_MAINNET = [ + 'wss://......', + 'wss://.........' +] +AVALANCHE_MAINNET = [ + 'wss://......', + 'wss://.........' +] +BASE_MAINNET = [ + 'wss://......', + 'wss://.........' +] +ARBITRUM_MAINNET = [ + 'wss://......', + 'wss://.........' +] +BSC_MAINNET = [ + 'wss://......', + 'wss://.........' +] +POLYGON_MAINNET = [ + 'wss://......', + 'wss://.........' +] +OPTIMISM_MAINNET = [ + 'wss://......', + 'wss://.........' +] +WEMIX_MAINNET = [ + 'wss://......', + 'wss://.........' +] +KROMA_MAINNET = [ + 'wss://......', + 'wss://.........' +] +OPTIMISM_SEPOLIA = [ + 'wss://......', + 'wss://.........' +] +SEPOLIA = [ + 'wss://......', + 'wss://.........' +] +AVALANCHE_FUJI = [ + 'wss://......', + 'wss://.........' +] +ARBITRUM_SEPOLIA = [ + 'wss://......', + 'wss://.........' +] +POLYGON_MUMBAI = [ + 'wss://......', + 'wss://.........' +] +BASE_SEPOLIA = [ + 'wss://......', + 'wss://.........' +] +BSC_TESTNET = [ + 'wss://......', + 'wss://.........' +] +KROMA_SEPOLIA = [ + 'wss://......', + 'wss://.........' +] +WEMIX_TESTNET = [ + 'wss://......', + 'wss://.........' +] + +[WalletKeys] +ETHEREUM_MAINNET = [''] +AVALANCHE_MAINNET = [''] +BASE_MAINNET = [''] +ARBITRUM_MAINNET = [''] +BSC_MAINNET = [''] +POLYGON_MAINNET = [''] +OPTIMISM_MAINNET = [''] +WEMIX_MAINNET = [''] +KROMA_MAINNET = [''] +OPTIMISM_SEPOLIA = [''] +SEPOLIA = [''] +AVALANCHE_FUJI = [''] +ARBITRUM_SEPOLIA = [''] +POLYGON_MUMBAI = [''] +BASE_SEPOLIA = [''] +BSC_TESTNET = [''] +KROMA_SEPOLIA = [''] +WEMIX_TESTNET = [''] \ No newline at end of file diff --git a/integration-tests/ccip-tests/testconfig/override/mainnet-secondary.toml b/integration-tests/ccip-tests/testconfig/override/mainnet-secondary.toml new file mode 100644 index 0000000000..7d457774b0 --- /dev/null +++ b/integration-tests/ccip-tests/testconfig/override/mainnet-secondary.toml @@ -0,0 +1,712 @@ +[CCIP] +[CCIP.ContractVersions] +PriceRegistry = '1.2.0' +OffRamp = '1.2.0' +OnRamp = '1.2.0' +TokenPool = '1.4.0' +CommitStore = '1.2.0' + +[CCIP.Deployments] +Data = """ +{ + "lane_configs": { + "Arbitrum Mainnet": { + "is_native_fee_token": true, + "fee_token": "0xf97f4df75117a78c1A5a0DBb814Af92458539FB4", + "bridge_tokens": ["0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"], + "bridge_tokens_pools": ["0x34700F5faE61Ba628c4269BdCbA12DA53bbfa726"], + "arm": "0xe06b0e8c4bd455153e8794ad7Ea8Ff5A14B64E4b", + "router": "0x141fa059441E0ca23ce184B6A78bafD2A517DdE8", + "price_registry": "0x13015e4E6f839E1Aa1016DF521ea458ecA20438c", + "wrapped_native": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", + "version" : "1.4.0", + "src_contracts": { + "Avalanche Mainnet": { + "on_ramp": "0x05B723f3db92430FbE4395fD03E40Cc7e9D17988", + "deployed_at": 11111111 + }, + "Base Mainnet": { + "on_ramp": "0x77b60F85b25fD501E3ddED6C1fe7bF565C08A22A", + "deployed_at": 11111111 + }, + "BSC Mainnet": { + "on_ramp": "0x79f3ABeCe5A3AFFf32D47F4CFe45e7b65c9a2D91", + "deployed_at": 11111111 + }, + "Ethereum Mainnet": { + "on_ramp": "0xCe11020D56e5FDbfE46D9FC3021641FfbBB5AdEE", + "deployed_at": 11111111 + }, + "Optimism Mainnet": { + "on_ramp": "0xC09b72E8128620C40D89649019d995Cc79f030C3", + "deployed_at": 11111111 + }, + "Polygon Mainnet": { + "on_ramp": "0x122F05F49e90508F089eE8D0d868d1a4f3E5a809", + "deployed_at": 11111111 + }, + "WeMix Mainnet": { + "on_ramp": "0x66a0046ac9FA104eB38B04cfF391CcD0122E6FbC", + "deployed_at": 11111111 + } + }, + "dest_contracts": { + "Avalanche Mainnet": { + "off_ramp": "0xe0109912157d5B75ea8b3181123Cf32c73bc9920", + "commit_store": "0xDaa61b8Cd85977820f92d1e749E1D9F55Da6CCEA", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Base Mainnet": { + "off_ramp": "0xdB19F77F87661f9be0F557cf9a1ebeCf7D8F206c", + "commit_store": "0x6e37f4c82d9A31cc42B445874dd3c3De97AB553f", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "BSC Mainnet": { + "off_ramp": "0xB1b705c2315fced1B38baE463BE7DDef531e47fA", + "commit_store": "0x310cECbFf14Ad0307EfF762F461a487C1abb90bf", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Ethereum Mainnet": { + "off_ramp": "0x542ba1902044069330e8c5b36A84EC503863722f", + "commit_store": "0x060331fEdA35691e54876D957B4F9e3b8Cb47d20", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Optimism Mainnet": { + "off_ramp": "0xeeed4D86F3E0e6d32A6Ad29d8De6A0Dc91963A5f", + "commit_store": "0xbbB563c4d98020b9c0f3Cc34c2C0Ef9676806E35", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Polygon Mainnet": { + "off_ramp": "0x9bDA7c8DCda4E39aFeB483cc0B7E3C1f6E0D5AB1", + "commit_store": "0x63a0AeaadAe851b990bBD9dc41f5C1B08b32026d", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "WeMix Mainnet": { + "off_ramp": "0xEEf5Fb4c4953F9cA9ab1f25cE590776AfFc2c455", + "commit_store": "0xD268286A277095a9C3C90205110831a84505881c", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "Avalanche Mainnet": { + "is_native_fee_token": true, + "fee_token": "0x5947BB275c521040051D82396192181b413227A3", + "bridge_tokens": [], + "bridge_tokens_pools": [], + "arm": "0xdFD6C0dc67666DE3bB36b31eec5c7B1542A82C1E", + "router": "0xF4c7E640EdA248ef95972845a62bdC74237805dB", + "price_registry": "0xfA4edD04eaAcDB07c8D73621bc1790eC50D8c489", + "wrapped_native": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7", + "src_contracts": { + "Arbitrum Mainnet": { + "on_ramp": "0x98f51B041e493fc4d72B8BD33218480bA0c66DDF", + "deployed_at": 11111111 + }, + "Base Mainnet": { + "on_ramp": "0x268fb4311D2c6CB2bbA01CCA9AC073Fb3bfd1C7c", + "deployed_at": 11111111 + }, + "BSC Mainnet": { + "on_ramp": "0x8eaae6462816CB4957184c48B86afA7642D8Bf2B", + "deployed_at": 11111111 + }, + "Ethereum Mainnet": { + "on_ramp": "0xD0701FcC7818c31935331B02Eb21e91eC71a1704", + "deployed_at": 11111111 + }, + "Optimism Mainnet": { + "on_ramp": "0x8629008887E073260c5434D6CaCFc83C3001d211", + "deployed_at": 11111111 + }, + "Polygon Mainnet": { + "on_ramp": "0x97500490d9126f34cf9aA0126d64623E170319Ef", + "deployed_at": 11111111 + }, + "WeMix Mainnet": { + "on_ramp": "0x9b1ed9De069Be4d50957464b359f98eD0Bf34dd5", + "deployed_at": 11111111 + } + }, + "dest_contracts": { + "Arbitrum Mainnet": { + "off_ramp": "0x770b1375F86E7a9bf30DBe3F97bea67193dC9135", + "commit_store": "0x23E2b34Ce8e12c53f8a39AD4b3FFCa845f8E617C", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Base Mainnet": { + "off_ramp": "0x4d6A796Bc85dcDF41ce9AaEB50B094C6b589748f", + "commit_store": "0xc4C4358FA01a04D6c6FE3b96a351946d4c2715C2", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "BSC Mainnet": { + "off_ramp": "0x83F53Fc798FEbfFbdF84830AD403b9989187a06C", + "commit_store": "0xD8ceCE2D7794385E00Ce3EF94550E732b0A0B959", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Ethereum Mainnet": { + "off_ramp": "0x5B833BD6456c604Eb396C0fBa477aD49e82B1A2a", + "commit_store": "0x23E23958D220B774680f91c2c91a6f2B2f610d7e", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Optimism Mainnet": { + "off_ramp": "0xb68A3EE8bD0A09eE221cf1859Dd5a4d5765188Fe", + "commit_store": "0x83DCeeCf822981F9F8552925eEfd88CAc1905dEA", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Polygon Mainnet": { + "off_ramp": "0x19250aBE66B88F214d02B6f3BF80F4118290C619", + "commit_store": "0x87A0935cE6254dB1252bBac90d1D07D04846aDCA", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "WeMix Mainnet": { + "off_ramp": "0x317dE8bc5c3292E494b6496586696d4966A922B0", + "commit_store": "0x97Fbf3d6DEac16adC721aE9187CeEa1e610aC7Af", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "Base Mainnet": { + "is_native_fee_token": true, + "fee_token": "0x88Fb150BDc53A65fe94Dea0c9BA0a6dAf8C6e196", + "bridge_tokens": [], + "bridge_tokens_pools": [], + "arm": "0x38660c8CC222c0192b635c2ac09687B4F25cCE5F", + "router": "0x881e3A65B4d4a04dD529061dd0071cf975F58bCD", + "price_registry": "0x6337a58D4BD7Ba691B66341779e8f87d4679923a", + "wrapped_native": "0x4200000000000000000000000000000000000006", + "src_contracts": { + "Arbitrum Mainnet": { + "on_ramp": "0x1E5Ca70d1e7A1B26061125738a880BBeA42FeB21", + "deployed_at": 11111111 + }, + "Avalanche Mainnet": { + "on_ramp": "0xBE5a9E336D9614024B4Fa10D8112671fc9A42d96", + "deployed_at": 11111111 + }, + "BSC Mainnet": { + "on_ramp": "0xdd4Fb402d41Beb0eEeF6CfB1bf445f50bDC8c981", + "deployed_at": 11111111 + }, + "Ethereum Mainnet": { + "on_ramp": "0xDEA286dc0E01Cb4755650A6CF8d1076b454eA1cb", + "deployed_at": 11111111 + }, + "Optimism Mainnet": { + "on_ramp": "0xd952FEAcDd5919Cc5E9454b53bF45d4E73dD6457", + "deployed_at": 11111111 + }, + "Polygon Mainnet": { + "on_ramp": "0x3DB8Bea142e41cA3633890d0e5640F99a895D6A5", + "deployed_at": 11111111 + } + }, + "dest_contracts": { + "Arbitrum Mainnet": { + "off_ramp": "0x8531E63aE9279a1f0D09eba566CD1b092b95f3D5", + "commit_store": "0x327E13f54c7871a2416006B33B4822eAAD357916", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Avalanche Mainnet": { + "off_ramp": "0x8345F2fF67e5A65e85dc955DE1414832608E00aD", + "commit_store": "0xd0b13be4c53A6262b47C5DDd36F0257aa714F562", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "BSC Mainnet": { + "off_ramp": "0x48a51f5D38BE630Ddd6417Ea2D9052B8efc91a18", + "commit_store": "0xF97127e77252284EC9D4bc13C247c9D1A99F72B0", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Ethereum Mainnet": { + "off_ramp": "0xEC0cFe335a4d53dBA70CB650Ab56eEc32788F0BB", + "commit_store": "0x0ae3c2c7FB789bd05A450CD3075D11f6c2Ca4F77", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Optimism Mainnet": { + "off_ramp": "0xf50c0d2a8B6Db60f1D93E60f03d0413D56153E4F", + "commit_store": "0x16f72C15165f7C9d74c12fDF188E399d4d3724e4", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Polygon Mainnet": { + "off_ramp": "0x75F29f058b31106F99caFdc17c9b26ADfcC7b5D7", + "commit_store": "0xb719616E732581B570232DfB13Ca49D27667Af9f", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "BSC Mainnet": { + "is_native_fee_token": true, + "fee_token": "0x404460C6A5EdE2D891e8297795264fDe62ADBB75", + "bridge_tokens": [], + "bridge_tokens_pools": [], + "arm": "0x3DB43b96B2625F4232e9Df900d464dd2c64C0021", + "router": "0x34B03Cb9086d7D758AC55af71584F81A598759FE", + "price_registry": "0xd64aAbD70A71d9f0A00B99F6EFc1626aA2dD43C7", + "wrapped_native": "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", + "src_contracts": { + "Avalanche Mainnet": { + "on_ramp": "0x6aa72a998859eF93356c6521B72155D355D0Cfd2", + "deployed_at": 11111111 + }, + "Arbitrum Mainnet": { + "on_ramp": "0x2788b46BAcFF49BD89562e6bA5c5FBbbE5Fa92F7", + "deployed_at": 11111111 + }, + "Base Mainnet": { + "on_ramp": "0x70bC7f7a6D936b289bBF5c0E19ECE35B437E2e36", + "deployed_at": 11111111 + }, + "Ethereum Mainnet": { + "on_ramp": "0x0Bf40b034872D0b364f3DCec04C7434a4Da1C8d9", + "deployed_at": 11111111 + }, + "Optimism Mainnet": { + "on_ramp": "0x4FEB11A454C9E8038A8d0aDF599Fe7612ce114bA", + "deployed_at": 11111111 + }, + "Polygon Mainnet": { + "on_ramp": "0x6bD4754D86fc87FE5b463D368f26a3587a08347c", + "deployed_at": 11111111 + }, + "WeMix Mainnet": { + "on_ramp": "0x1467fF8f249f5bc604119Af26a47035886f856BE", + "deployed_at": 11111111 + } + }, + "dest_contracts": { + "Avalanche Mainnet": { + "off_ramp": "0x37a6fa55fe61061Ae97bF7314Ae270eCF71c5ED3", + "commit_store": "0x1f558F6dcf0224Ef1F78A24814FED548B9602c80", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Arbitrum Mainnet": { + "off_ramp": "0x3DA330fd8Ef10d93cFB7D4f8ecE7BC1F10811feC", + "commit_store": "0x86D55Ff492cfBBAf0c0D42D4EE615144E78b3D02", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Base Mainnet": { + "off_ramp": "0x574c697deab06B805D8780898B3F136a1F4892Dc", + "commit_store": "0x002B164b1dcf4E92F352DC625A01Be0E890EdEea", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Ethereum Mainnet": { + "off_ramp": "0x181Bb1E97b0bDD1D85E741ad0943552D3682cc35", + "commit_store": "0x3fF27A34fF0FA77921C3438e67f58da1a83e9Ce1", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Optimism Mainnet": { + "off_ramp": "0xE7E080C8d62d595a223C577C7C8d1f75d9A5E664", + "commit_store": "0xF4d53346bDb6d393C74B0B72Aa7D6689a3eAad79", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Polygon Mainnet": { + "off_ramp": "0x26af2046Da85d7f6712D5edCa81B9E3b2e7A60Ab", + "commit_store": "0x4C1dA405a789AC2853A69D8290B8B9b47a0374F8", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "WeMix Mainnet": { + "off_ramp": "0xC027C5AEb230008c243Be463A73571e581F94c13", + "commit_store": "0x2EB426C8C54D740d1FC856eB3Ff96feA03957978", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "Ethereum Mainnet": { + "is_native_fee_token": true, + "fee_token": "0x514910771AF9Ca656af840dff83E8264EcF986CA", + "bridge_tokens": ["0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"], + "bridge_tokens_pools": ["0x69c24c970B65e22Ac26864aF10b2295B7d78f93A"], + "arm": "0x8B63b3DE93431C0f756A493644d128134291fA1b", + "router": "0x80226fc0Ee2b096224EeAc085Bb9a8cba1146f7D", + "price_registry": "0x8c9b2Efb7c64C394119270bfecE7f54763b958Ad", + "wrapped_native": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "src_contracts": { + "Arbitrum Mainnet": { + "on_ramp": "0x925228D7B82d883Dde340A55Fe8e6dA56244A22C", + "deployed_at": 11111111 + }, + "Avalanche Mainnet": { + "on_ramp": "0x3df8dAe2d123081c4D5E946E655F7c109B9Dd630", + "deployed_at": 11111111 + }, + "Base Mainnet": { + "on_ramp": "0xe2c2AB221AA0b957805f229d2AA57fBE2f4dADf7", + "deployed_at": 11111111 + }, + "BSC Mainnet": { + "on_ramp": "0x91D25A56Db77aD5147437d8B83Eb563D46eBFa69", + "deployed_at": 11111111 + }, + "Optimism Mainnet": { + "on_ramp": "0x86B47d8411006874eEf8E4584BdFD7be8e5549d1", + "deployed_at": 11111111 + }, + "Polygon Mainnet": { + "on_ramp": "0x35F0ca9Be776E4B38659944c257bDd0ba75F1B8B", + "deployed_at": 11111111 + }, + "WeMix Mainnet": { + "on_ramp": "0xCbE7e5DA76dC99Ac317adF6d99137005FDA4E2C4", + "deployed_at": 11111111 + } + }, + "dest_contracts": { + "Arbitrum Mainnet": { + "off_ramp": "0xeFC4a18af59398FF23bfe7325F2401aD44286F4d", + "commit_store": "0x9B2EEd6A1e16cB50Ed4c876D2dD69468B21b7749", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Avalanche Mainnet": { + "off_ramp": "0x569940e02D4425eac61A7601632eC00d69f75c17", + "commit_store": "0x2aa101BF99CaeF7fc1355D4c493a1fe187A007cE", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Base Mainnet": { + "off_ramp": "0xdf85c8381954694E74abD07488f452b4c2Cddfb3", + "commit_store": "0x8DC27D621c41a32140e22E2a4dAf1259639BAe04", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "BSC Mainnet": { + "off_ramp": "0x7Afe7088aff57173565F4b034167643AA8b9171c", + "commit_store": "0x87c55D48DF6EF7B08153Ab079e76bFEcbb793D75", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Optimism Mainnet": { + "off_ramp": "0xB095900fB91db00E6abD247A5A5AD1cee3F20BF7", + "commit_store": "0x4af4B497c998007eF83ad130318eB2b925a79dc8", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Polygon Mainnet": { + "off_ramp": "0x0af338F0E314c7551bcE0EF516d46d855b0Ee395", + "commit_store": "0xD37a60E8C36E802D2E1a6321832Ee85556Beeb76", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "WeMix Mainnet": { + "off_ramp": "0x3a129e6C18b23d18BA9E6Aa14Dc2e79d1f91c6c5", + "commit_store": "0x31f6ab382DDeb9A316Ab61C3945a5292a50a89AB", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "Kroma Mainnet": { + "is_native_fee_token": true, + "fee_token": "0xC1F6f7622ad37C3f46cDF6F8AA0344ADE80BF450", + "bridge_tokens": [], + "bridge_tokens_pools": [], + "arm": "0xB59779d3364BC6d71168245f9ebb96469E5a5a98", + "router": "0xE93E8B0d1b1CEB44350C8758ed1E2799CCee31aB", + "price_registry": "0x8155B4710e7bbC90924E957104F94Afd4f95Eca2", + "wrapped_native": "0x4200000000000000000000000000000000000001", + "src_contracts": { + "WeMix Mainnet": { + "on_ramp": "0x3C5Ab46fA1dB1dECD854224654313a69bf9fcAD3", + "deployed_at": 11111111 + } + }, + "dest_contracts": { + "WeMix Mainnet": { + "off_ramp": "0x2B555774B3D1dcbcd76efb7751F3c5FbCFABC5C4", + "commit_store": "0x213124614aAf31eBCE7c612A12aac5f8aAD77DE4", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "Optimism Mainnet": { + "is_native_fee_token": true, + "fee_token": "0x350a791Bfc2C21F9Ed5d10980Dad2e2638ffa7f6", + "bridge_tokens": ["0x4200000000000000000000000000000000000006"], + "bridge_tokens_pools": ["0x86E715415D8C8435903d1e8204fA1e9784Aa7305"], + "arm": "0x8C7C2C3362a42308BB5c368677Ad321D11693b81", + "router": "0x3206695CaE29952f4b0c22a169725a865bc8Ce0f", + "price_registry": "0xb52545aECE8C73A97E52a146757EC15b90Ed8488", + "wrapped_native": "0x4200000000000000000000000000000000000006", + "src_contracts": { + "Arbitrum Mainnet": { + "on_ramp": "0x0C9BE7Cfd12c735E5aaE047C1dCB845d54E518C3", + "deployed_at": 11111111 + }, + "Avalanche Mainnet": { + "on_ramp": "0xD0D3E757bFBce7ae1881DDD7F6d798DDcE588445", + "deployed_at": 11111111 + }, + "Base Mainnet": { + "on_ramp": "0x0b1760A8112183303c5526C6b24569fd3A274f3B", + "deployed_at": 11111111 + }, + "BSC Mainnet": { + "on_ramp": "0xa3c9544B82846C45BE37593d5d9ACffbE61BF3A6", + "deployed_at": 11111111 + }, + "Ethereum Mainnet": { + "on_ramp": "0x55183Db1d2aE0b63e4c92A64bEF2CBfc2032B127", + "deployed_at": 11111111 + }, + "Polygon Mainnet": { + "on_ramp": "0x6B57145e322c877E7D91Ed8E31266eB5c02F7EfC", + "deployed_at": 11111111 + }, + "WeMix Mainnet": { + "on_ramp": "0x82e9f4C5ec4a84E310d60D462a12042E5cbA0954", + "deployed_at": 11111111 + } + }, + "dest_contracts": { + "Arbitrum Mainnet": { + "off_ramp": "0x0C9BE7Cfd12c735E5aaE047C1dCB845d54E518C3", + "commit_store": "0x55028780918330FD00a34a61D9a7Efd3f43ca845", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Avalanche Mainnet": { + "off_ramp": "0x8dc6490A6204dF846BaBE809cB695ba17Df1F9B1", + "commit_store": "0xA190660787B6B183Dd82B243eA10e609327c7308", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Base Mainnet": { + "off_ramp": "0xBAE6560eCa9B77Cb047158C783e36F7735C86037", + "commit_store": "0x6168aDF58e1Ad446BaD45c6275Bef60Ef4FFBAb8", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "BSC Mainnet": { + "off_ramp": "0xE14501F2838F2fA1Ceb52E78ABdA289EcE1705EA", + "commit_store": "0xa8DD25B29787527Df283211C24Ac72B17150A696", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Ethereum Mainnet": { + "off_ramp": "0xd2D98Be6a1C241e86C807e51cED6ABb51d044203", + "commit_store": "0x4d75A5cE454b264b187BeE9e189aF1564a68408D", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Polygon Mainnet": { + "off_ramp": "0x7c6221880A1D62506b1A08Dab3Bf695A49AcDD22", + "commit_store": "0x0684076EE3595221861C50cDb9Cb66402Ec11Cb9", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "WeMix Mainnet": { + "off_ramp": "0x3e5B3b7559D39563a74434157b31781322dA712D", + "commit_store": "0x7954372FF6f80908e5A2dC2a19d796A1005f91D2", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "Polygon Mainnet": { + "is_native_fee_token": true, + "fee_token": "0xb0897686c545045aFc77CF20eC7A532E3120E0F1", + "bridge_tokens": [], + "bridge_tokens_pools": [], + "arm": "0xD7AcF65dA1E1f34b663aB199a474F209bF2b0523", + "router": "0x849c5ED5a80F5B408Dd4969b78c2C8fdf0565Bfe", + "price_registry": "0x30D873664Ba766C983984C7AF9A921ccE36D34e1", + "wrapped_native": "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", + "src_contracts": { + "Arbitrum Mainnet": { + "on_ramp": "0xD16D025330Edb91259EEA8ed499daCd39087c295", + "deployed_at": 11111111 + }, + "Avalanche Mainnet": { + "on_ramp": "0x5FA30697e90eB30954895c45b028F7C0dDD39b12", + "deployed_at": 11111111 + }, + "Base Mainnet": { + "on_ramp": "0x20B028A2e0F6CCe3A11f3CE5F2B8986F932e89b4", + "deployed_at": 11111111 + }, + "BSC Mainnet": { + "on_ramp": "0xF5b5A2fC11BF46B1669C3B19d98B19C79109Dca9", + "deployed_at": 11111111 + }, + "Ethereum Mainnet": { + "on_ramp": "0xFd77c53AA4eF0E3C01f5Ac012BF7Cc7A3ECf5168", + "deployed_at": 11111111 + }, + "Optimism Mainnet": { + "on_ramp": "0x3111cfbF5e84B5D9BD952dd8e957f4Ca75f728Cf", + "deployed_at": 11111111 + }, + "WeMix Mainnet": { + "on_ramp": "0x5060eF647a1F66BE6eE27FAe3046faf8D53CeB2d", + "deployed_at": 11111111 + } + }, + "dest_contracts": { + "Arbitrum Mainnet": { + "off_ramp": "0xa8a9eDa2867c2E0CE0d5ECe273961F1EcC3CC25B", + "commit_store": "0xbD4480658dca8496a65046dfD1BDD44EF897Bdb5", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Avalanche Mainnet": { + "off_ramp": "0xB9e3680639c9F0C4e0b02FD81C445094426244Ae", + "commit_store": "0x8c63d4e67f7c4af6FEd2f56A34fB4e01CB807CFF", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Base Mainnet": { + "off_ramp": "0xD0FA7DE2D18A0c59D3fD7dfC7aB4e913C6Aa7b68", + "commit_store": "0xF88053B9DAC8Dd3039a4eFa8639159aaa3F2D4Cb", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "BSC Mainnet": { + "off_ramp": "0x592773924741F0Da889a0dfdab71171Dd11E054C", + "commit_store": "0xEC4d35E1A85f770f4D93BA43a462c9d87Ef7017e", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Ethereum Mainnet": { + "off_ramp": "0x45320085fF051361D301eC1044318213A5387A15", + "commit_store": "0x4Dc771B5ef21ef60c33e2987E092345f2b63aE08", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Optimism Mainnet": { + "off_ramp": "0xBa754ecd3CFA7E9093F688EAc3860cf9D07Fc0AC", + "commit_store": "0x04C0D5302E3D8Ca0A0019141a52a23B59cdb70e4", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "WeMix Mainnet": { + "off_ramp": "0xd7c877ea02310Cce9278D9A048Aa1Bb9aF72F00d", + "commit_store": "0x92A1C927E8E10Ab6A40E5A5154e2300D278d1a67", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "WeMix Mainnet": { + "is_native_fee_token": true, + "fee_token": "0x80f1FcdC96B55e459BF52b998aBBE2c364935d69", + "bridge_tokens": [], + "bridge_tokens_pools": [], + "arm": "0x07aaC8B69A62dB5bd3d244091916EbF2fac17b76", + "router": "0x7798b795Fde864f4Cd1b124a38Ba9619B7F8A442", + "price_registry": "0x252863688762aD86868D3d3076233Eacd80c7055", + "wrapped_native": "0x7D72b22a74A216Af4a002a1095C8C707d6eC1C5f", + "src_contracts": { + "Arbitrum Mainnet": { + "on_ramp": "0x9aBfd6f4C865610692AB6fb1Be862575809fFabf", + "deployed_at": 11111111 + }, + "Avalanche Mainnet": { + "on_ramp": "0xbE0Cfae74677F8dd16a246a3a5c8cbB1973118f4", + "deployed_at": 11111111 + }, + "BSC Mainnet": { + "on_ramp": "0x56657ec4D15C71f7F3C17ba2b21C853A24Dc5381", + "deployed_at": 11111111 + }, + "Optimism Mainnet": { + "on_ramp": "0x70f3b0FD7e6a4B9B623e9AB859604A9EE03e48BD", + "deployed_at": 11111111 + }, + "Polygon Mainnet": { + "on_ramp": "0x777058C1e1dcE4eB8001F38631a1cd9450816e5a", + "deployed_at": 11111111 + }, + "Ethereum Mainnet": { + "on_ramp": "0x190bcE84CF2d500B878966F4Cf98a50d78f2675E", + "deployed_at": 11111111 + }, + "Kroma Mainnet": { + "on_ramp": "0x47E9AE0A815C94836202E696748A5d5476aD8735", + "deployed_at": 11111111 + } + }, + "dest_contracts": { + "Arbitrum Mainnet": { + "off_ramp": "0x2ba68a395B72a6E3498D312efeD755ed2f3CF223", + "commit_store": "0xdAeC234DA83F68707Bb8AcB2ee6a01a5FD4c2391", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Avalanche Mainnet": { + "off_ramp": "0xFac907F9a1087B846Faa75A14C5d34A8639233d8", + "commit_store": "0xF2812063446c7deD2CA306c67A68364BdDcbEfc5", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "BSC Mainnet": { + "off_ramp": "0x6ec9ca4Cba62cA17c55F05ad2000B46192f02035", + "commit_store": "0x84534BE763366a69710E119c100832955795B34B", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Optimism Mainnet": { + "off_ramp": "0x87220D01DF0fF27149B47227897074653788fd23", + "commit_store": "0xF8dD2be2C6FA43e48A17146380CbEBBB4291807b", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Polygon Mainnet": { + "off_ramp": "0x8f0229804513A9Bc00c1308414AB279Dbc718ae1", + "commit_store": "0x3A85D1b8641d83a87957C6ECF1b62151213e0842", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Ethereum Mainnet": { + "off_ramp": "0xF92Fa796F5307b029c65CA26f322a6D86f211194", + "commit_store": "0xbeC110FF43D52be2066B06525304A9924E16b73b", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Kroma Mainnet": { + "off_ramp": "0xF886d8DC64E544af4835cbf91e5678A54D95B80e", + "commit_store": "0x8794C9534658fdCC44f2FF6645Bf31cf9F6d2d5D", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + } + } +} +""" + +[CCIP.Env] +TTL = '8h' + +[CCIP.Env.Network] +selected_networks = [ + 'ETHEREUM_MAINNET', + 'ARBITRUM_MAINNET', + 'OPTIMISM_MAINNET' + ] + +[CCIP.Groups.load] +NetworkPairs = [ + 'ETHEREUM_MAINNET,OPTIMISM_MAINNET', + 'ETHEREUM_MAINNET,ARBITRUM_MAINNET', + 'ARBITRUM_MAINNET,OPTIMISM_MAINNET' # added as batch 1 +] + +BiDirectionalLane = true +PhaseTimeout = '40m' +ExistingDeployment = true + +[CCIP.Groups.load.TokenConfig] +NoOfTokensPerChain = 1 +CCIPOwnerTokens = true + +[CCIP.Groups.load.LoadProfile] +RequestPerUnitTime = [1] +TimeUnit = '3h' +TestDuration = '24h' +TestRunName = 'mainnet-2.7-ccip1.2' +FailOnFirstErrorInLoad = true +SkipRequestIfAnotherRequestTriggeredWithin = '40m' + +[[CCIP.Groups.load.LoadProfile.MsgProfile.MsgDetails]] +MsgType = 'Data' +DestGasLimit = 0 +DataLength = 100 +NoOfTokens = 1 +AmountPerToken = 1 + +[CCIP.Groups.smoke] +# these are all the valid network pairs +NetworkPairs = [ + 'ETHEREUM_MAINNET,OPTIMISM_MAINNET', + 'ETHEREUM_MAINNET,ARBITRUM_MAINNET', + 'ARBITRUM_MAINNET,OPTIMISM_MAINNET' +] + +BiDirectionalLane = true +DestGasLimit = 0 +PhaseTimeout = '20m' +LocalCluster = false +ExistingDeployment = true +ReuseContracts = true + +[CCIP.Groups.smoke.TokenConfig] +NoOfTokensPerChain = 1 +CCIPOwnerTokens = true + +[CCIP.Groups.smoke.MsgDetails] +MsgType = 'Data' +DestGasLimit = 0 +DataLength = 100 +NoOfTokens = 1 +AmountPerToken = 1 \ No newline at end of file diff --git a/integration-tests/ccip-tests/testconfig/override/mainnet.toml b/integration-tests/ccip-tests/testconfig/override/mainnet.toml index 6fa0c3e0dd..60c0ba3f1c 100644 --- a/integration-tests/ccip-tests/testconfig/override/mainnet.toml +++ b/integration-tests/ccip-tests/testconfig/override/mainnet.toml @@ -1165,6 +1165,7 @@ TimeUnit = '1h' TestDuration = '5h' TestRunName = 'Soak_test_mainnet' FailOnFirstErrorInLoad = true +SkipRequestIfAnotherRequestTriggeredWithin = '40m' [[CCIP.Groups.load.LoadProfile.MsgProfile.MsgDetails]] @@ -1235,6 +1236,7 @@ ExistingDeployment = true ReuseContracts = true SkipRequestIfAnotherRequestTriggeredWithin = '24h' + [CCIP.Groups.smoke.TokenConfig] NoOfTokensPerChain = 1 CCIPOwnerTokens = true diff --git a/integration-tests/ccip-tests/testconfig/tomls/ccip-default.toml b/integration-tests/ccip-tests/testconfig/tomls/ccip-default.toml index 15bf443bca..84fdb0d769 100644 --- a/integration-tests/ccip-tests/testconfig/tomls/ccip-default.toml +++ b/integration-tests/ccip-tests/testconfig/tomls/ccip-default.toml @@ -27,7 +27,7 @@ ethereum_version = "eth1" # geth, besu, erigon or nethermind execution_layer = "geth" # eth2-only, if set to true environment startup will wait until at least 1 epoch has been finalised -wait_for_finalization = false +wait_for_finalization=false [CCIP.Env.PrivateEthereumNetworks.SIMULATED_1.EthereumChainConfig] # eth2-only, the lower the value the faster the block production (3 is minimum) @@ -276,7 +276,7 @@ TimeoutForPriceUpdate = '15m' # Duration to wait for the price update to time-ou # Now testing only with dynamic price getter (no pipeline). # Could be removed once the pipeline is completely removed. WithPipeline = false -NoOfTokensPerChain = 2 # number of bridge tokens to be deployed per network; if MsgType = 'Token'/'DataWithToken' +NoOfTokensPerChain = 2 # number of bridge tokens to be deployed per network; if MsgType = 'Token'/'DataWithToken' CCIPOwnerTokens = false # if true, the test will use deploy the tokens by the CCIPOwner, otherwise the tokens will be deployed by a non-owner account, only applicable for 1.5 pools and onwards #NoOfTokensWithDynamicPrice = 15 # number of tokens with dynamic price to be deployed @@ -357,7 +357,7 @@ TimeoutForPriceUpdate = '15m' # Duration to wait for the price update to time-ou # Now testing only with dynamic price getter (no pipeline). # Could be removed once the pipeline is completely removed. WithPipeline = false -NoOfTokensPerChain = 2 # number of bridge tokens to be deployed per network; if MsgType = 'Token'/'DataWithToken' +NoOfTokensPerChain = 2 # number of bridge tokens to be deployed per network; if MsgType = 'Token'/'DataWithToken' CCIPOwnerTokens = false # if true, the test will use deploy the tokens by the CCIPOwner, otherwise the tokens and pools will be deployed by a non-owner account, # only applicable for 1.5 pools and onwards, if you are running with pre-1.5 pools, then set this to true to deploy token pools by CCIPOwner, otherwise # the test will fail diff --git a/integration-tests/ccip-tests/testconfig/tomls/ccip1.4-stress/baseline.toml b/integration-tests/ccip-tests/testconfig/tomls/ccip1.4-stress/baseline.toml index c404d3a0c0..2c0ee640df 100644 --- a/integration-tests/ccip-tests/testconfig/tomls/ccip1.4-stress/baseline.toml +++ b/integration-tests/ccip-tests/testconfig/tomls/ccip1.4-stress/baseline.toml @@ -1,5 +1,5 @@ ## Baseline performance test on simulated environment (with chaos) -## 30 chains / 300 lanes +## 40 chains / 400 lanes ## historyDepth 200 / finalityDepth 200 ## block_time = 1s ## throughput 1msg / 5s @@ -29,7 +29,7 @@ CommitStore = '1.2.0' TTL = '10h' [CCIP.Env.Network] -selected_networks = ['PRIVATE-CHAIN-1', 'PRIVATE-CHAIN-2'] +selected_networks= ['PRIVATE-CHAIN-1', 'PRIVATE-CHAIN-2'] [CCIP.Env.Network.EVMNetworks.PRIVATE-CHAIN-1] evm_name = 'private-chain-1' @@ -148,9 +148,9 @@ KeepEnvAlive = true NoOfCommitNodes = 16 PhaseTimeout = '40m' NodeFunding = 1000.0 -NoOfRoutersPerPair = 1 -NoOfNetworks = 30 -MaxNoOfLanes = 300 +NoOfRoutersPerPair = 2 +NoOfNetworks = 40 +MaxNoOfLanes = 400 [CCIP.Groups.load.OffRampConfig] BatchGasLimit = 11000000 @@ -159,11 +159,11 @@ BatchGasLimit = 11000000 TimeoutForPriceUpdate = '15m' NoOfTokensPerChain = 10 NoOfTokensWithDynamicPrice = 10 -DynamicPriceUpdateInterval = '15s' +DynamicPriceUpdateInterval ='15s' CCIPOwnerTokens = true [CCIP.Groups.load.LoadProfile] -TestDuration = '6h' +TestDuration = '4h' TimeUnit = '5s' RequestPerUnitTime = [1] OptimizeSpace = true @@ -171,7 +171,7 @@ NetworkChaosDelay = '100ms' # to represent 20%, 60%, 15%, 5% of the total messages [CCIP.Groups.load.LoadProfile.MsgProfile] -Frequencies = [4, 12, 3, 1] +Frequencies = [4,12,3,1] [[CCIP.Groups.load.LoadProfile.MsgProfile.MsgDetails]] MsgType = 'Token' diff --git a/integration-tests/ccip-tests/testconfig/tomls/ccip1.4-stress/prod-testnet.toml b/integration-tests/ccip-tests/testconfig/tomls/ccip1.4-stress/prod-testnet.toml new file mode 100644 index 0000000000..f8321584c8 --- /dev/null +++ b/integration-tests/ccip-tests/testconfig/tomls/ccip1.4-stress/prod-testnet.toml @@ -0,0 +1,964 @@ +[CCIP] +[CCIP.ContractVersions] +PriceRegistry = '1.2.0' +OffRamp = '1.2.0' +OnRamp = '1.2.0' +TokenPool = '1.4.0' +CommitStore = '1.2.0' + + +[CCIP.Deployments] +Data = """ +{ + "lane_configs": { + "Arbitrum Sepolia": { + "is_native_fee_token": true, + "fee_token": "0xb1D4538B4571d411F07960EF2838Ce337FE1E80E", + "bridge_tokens": [ + ], + "bridge_tokens_pools": [ + ], + "price_aggregators": null, + "arm": "0x5EF7a726Fd21Fd9D77D34E3C56cfDD8691F7F0ac", + "router": "0x2a9C5afB0d0e4BAb2BCdaE109EC4b0c4Be15a165", + "price_registry": "0x89D5b13908b9063abCC6791dc724bF7B7c93634C", + "wrapped_native": "0xE591bf0A0CF924A0674d7792db046B23CEbF5f34", + "src_contracts": { + "Avalanche Fuji": { + "on_ramp": "0x1Cb56374296ED19E86F68fA437ee679FD7798DaA", + "deployed_at": 33999325 + }, + "Base Sepolia": { + "on_ramp": "0x7854E73C73e7F9bb5b0D5B4861E997f4C6E8dcC6", + "deployed_at": 9199926 + }, + "Gnosis Chiado": { + "on_ramp": "0x973CbE752258D32AE82b60CD1CB656Eebb588dF0", + "deployed_at": 42809650 + }, + "Optimism Sepolia": { + "on_ramp": "0x701Fe16916dd21EFE2f535CA59611D818B017877", + "deployed_at": 35180131 + }, + "Sepolia Testnet": { + "on_ramp": "0x4205E1Ca0202A248A5D42F5975A8FE56F3E302e9", + "deployed_at": 35180131 + }, + "WeMix Testnet": { + "on_ramp": "0xBD4106fBE4699FE212A34Cc21b10BFf22b02d959", + "deployed_at": 18816676 + } + }, + "dest_contracts": { + "Avalanche Fuji": { + "off_ramp": "0xcab0EF91Bee323d1A617c0a027eE753aFd6997E4", + "commit_store": "0x0d90b9b96cBFa0D01635ce12982ccE1b70827c7a", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Base Sepolia": { + "off_ramp": "0xc1982985720B959E66c19b64F783361Eb9B60F26", + "commit_store": "0x28F66bB336f6db713d6ad2a3bd1B7a531282A159", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Gnosis Chiado": { + "off_ramp": "0x935C26F9a9122E5F9a27f2d3803e74c75B94f5a3", + "commit_store": "0xEdb963Ec5c2E5AbdFdCF137eF44A445a7fa4787A", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Optimism Sepolia": { + "off_ramp": "0xfD404A89e1d195F0c65be1A9042C77745197659e", + "commit_store": "0x84B7B012c95f8A152B44Ab3e952f2dEE424fA8e1", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Sepolia Testnet": { + "off_ramp": "0x1c71f141b4630EBE52d6aF4894812960abE207eB", + "commit_store": "0xaB0c8Ba51E7Fa3E5693a4Fbb39473520FD85d173", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "WeMix Testnet": { + "off_ramp": "0x262e16C8D42aa07bE13e58F81e7D9F62F6DE2830", + "commit_store": "0xc132eFAf929299E5ee704Fa6D9796CFa23Bb8b2C", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "Avalanche Fuji": { + "fee_token": "0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846", + "bridge_tokens": [ + ], + "bridge_tokens_pools": [ + ], + "price_aggregators": null, + "arm": "0x0ea0D7B2b78DD3A926fC76d6875a287F0AEB158F", + "router": "0xF694E193200268f9a4868e4Aa017A0118C9a8177", + "price_registry": "0x19e157E5fb1DAec1aE4BaB113fdf077F980704AA", + "wrapped_native": "0xd00ae08403B9bbb9124bB305C09058E32C39A48c", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0x8bB16BEDbFd62D1f905ACe8DBBF2954c8EEB4f66", + "deployed_at": 31888860 + }, + "BSC Testnet": { + "on_ramp": "0xF25ECF1Aad9B2E43EDc2960cF66f325783245535", + "deployed_at": 33214865 + }, + "Base Sepolia": { + "on_ramp": "0x1A674645f3EB4147543FCA7d40C5719cbd997362", + "deployed_at": 31235262 + }, + "Gnosis Chiado": { + "on_ramp": "0x1532e5b204ee2b2244170c78E743CB9c168F4DF9", + "deployed_at": 32817266 + }, + "Optimism Sepolia": { + "on_ramp": "0xC334DE5b020e056d0fE766dE46e8d9f306Ffa1E2", + "deployed_at": 30396804 + }, + "Polygon Amoy": { + "on_ramp": "0x610F76A35E17DA4542518D85FfEa12645eF111Fc", + "deployed_at": 31982368 + }, + "Sepolia Testnet": { + "on_ramp": "0x5724B4Cc39a9690135F7273b44Dfd3BA6c0c69aD", + "deployed_at": 33214865 + }, + "WeMix Testnet": { + "on_ramp": "0x677B5ab5C8522d929166c064d5700F147b15fa33", + "deployed_at": 30436465 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0x90A74072e7B0c2d59e13aB4d8f93c8198c413194", + "commit_store": "0xf3458CFd2fdf4a6CF0Ce296d520DD21eB194828b", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "BSC Testnet": { + "off_ramp": "0x10b28009E5D776F1f5AAA73941CE8953B8f42d26", + "commit_store": "0xacDD582F271eCF22FAd6764cCDe1c4a534b732A8", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Base Sepolia": { + "off_ramp": "0xdBdE8510226d1E060A3bf982b67705C67f5697e2", + "commit_store": "0x8Ee73BC9492b4182D289E5C1e66e40CD876CC00F", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Gnosis Chiado": { + "off_ramp": "0x56dF55aF5F0A4689f3364230587a68eD6A314fAd", + "commit_store": "0xabA7ff98094c4cc7A075812EefF2CD21f6400235", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Optimism Sepolia": { + "off_ramp": "0x3d7CbC95DCC33257F14D6Eb780c88Bd56C6335BB", + "commit_store": "0x1fcDC02edDfb405f378ba53cF9E6104feBcB7542", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Polygon Amoy": { + "off_ramp": "0x3e33290B90fD0FF30a3FA138934DF028E4eCA348", + "commit_store": "0xCFe3556Aa42d40be09BD23aa80448a19443BE5B1", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Sepolia Testnet": { + "off_ramp": "0x9e5e4324F8608D54A50a317832d456a392E4F8C2", + "commit_store": "0x92A51eD3F041B39EbD1e464C1f7cb1e8f8A8c63f", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "WeMix Testnet": { + "off_ramp": "0xD0D338318bC6837b091FC7AB5F2a94B7783507d5", + "commit_store": "0xd9D479208235c7355848ff4aF26eB5aacfDC30c6", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "BSC Testnet": { + "is_native_fee_token": true, + "fee_token": "0x84b9B910527Ad5C03A9Ca831909E21e236EA7b06", + "bridge_tokens": [ + ], + "bridge_tokens_pools": [ + ], + "price_aggregators": null, + "arm": "0xF9a21B587111e7E8745Fb8b13750014f19DB0014", + "router": "0xE1053aE1857476f36A3C62580FF9b016E8EE8F6f", + "price_registry": "0xCCDf022c9d31DC26Ebab4FB92432724a5b79809a", + "wrapped_native": "0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd", + "src_contracts": { + "Avalanche Fuji": { + "on_ramp": "0xa2515683E99F50ADbE177519A46bb20FfdBaA5de", + "deployed_at": 40500000 + }, + "Base Sepolia": { + "on_ramp": "0x3E807220Ca84b997c0d1928162227b46C618e0c5", + "deployed_at": 37115558 + }, + "Gnosis Chiado": { + "on_ramp": "0x8735f991d41eA9cA9D2CC75cD201e4B7C866E63e", + "deployed_at": 40228352 + }, + "Polygon Amoy": { + "on_ramp": "0xf37CcbfC04adc1B56a46B36F811D52C744a1AF78", + "deployed_at": 39572254 + }, + "Sepolia Testnet": { + "on_ramp": "0xB1DE44B04C00eaFe9915a3C07a0CaeA4410537dF", + "deployed_at": 38150066 + }, + "WeMix Testnet": { + "on_ramp": "0x89268Afc1BEA0782a27ba84124E3F42b196af927", + "deployed_at": 38184995 + } + }, + "dest_contracts": { + "Avalanche Fuji": { + "off_ramp": "0x6e6fFCb6B4BED91ff0CC8C2e57EC029dA7DB80C2", + "commit_store": "0x38Bc38Bd824b6eE87571f9D3CFbe6D6E28E3Dc62", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Base Sepolia": { + "off_ramp": "0x2C61FD7E93Dc79422861282145c59B56dFbc3a8c", + "commit_store": "0x42fAe5B3605804CF6d08632d7A25864e24F792Ae", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Gnosis Chiado": { + "off_ramp": "0x71a44a60832B0F8B63232C9516e7E6aEc3A373Dc", + "commit_store": "0xAC24299a91b72d1Cb5B31147e3CF54964D896974", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Polygon Amoy": { + "off_ramp": "0x63440C7747d37bc6154b5538AE32b54FE0965AfA", + "commit_store": "0xAD22fA198CECfC534927aE1D480c460d5bB3460F", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Sepolia Testnet": { + "off_ramp": "0xf1c128Fe52Ea78CcAAB407509292E61ce38C1523", + "commit_store": "0x59dFD870dC4bd76A7B879A4f705Fdcd2595f85f9", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "WeMix Testnet": { + "off_ramp": "0xfd9B19c3725da5B517aA705B848ff3f21F98280e", + "commit_store": "0x3c1F1412563188aBc8FE3fd53E8F1Cb601CaB4f9", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "Base Sepolia": { + "is_native_fee_token": true, + "fee_token": "0xE4aB69C077896252FAFBD49EFD26B5D171A32410", + "bridge_tokens": [ + ], + "bridge_tokens_pools": [ + ], + "price_aggregators": null, + "arm": "0x5aA82cA372782d6CC33AA4C830Df2a91017A7e1b", + "router": "0xD3b06cEbF099CE7DA4AcCf578aaebFDBd6e88a93", + "price_registry": "0x4D20536e60832bE579Cd38E89Dc03d11E1741FbA", + "wrapped_native": "0x4200000000000000000000000000000000000006", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0x58622a80c6DdDc072F2b527a99BE1D0934eb2b50", + "deployed_at": 5146539 + }, + "Avalanche Fuji": { + "on_ramp": "0xAbA09a1b7b9f13E05A6241292a66793Ec7d43357", + "deployed_at": 7810235 + }, + "BSC Testnet": { + "on_ramp": "0xD806966beAB5A3C75E5B90CDA4a6922C6A9F0c9d", + "deployed_at": 5144127 + }, + "Gnosis Chiado": { + "on_ramp": "0x2Eff2d1BF5C557d6289D208a7a43608f5E3FeCc2", + "deployed_at": 9817141 + }, + "Optimism Sepolia": { + "on_ramp": "0x3b39Cd9599137f892Ad57A4f54158198D445D147", + "deployed_at": 5147649 + }, + "Sepolia Testnet": { + "on_ramp": "0x6486906bB2d85A6c0cCEf2A2831C11A2059ebfea", + "deployed_at": 7810235 + }, + "ethereum-testnet-sepolia-mode-1": { + "on_ramp": "0x3d0115386C01436870a2c47e6297962284E70BA6", + "deployed_at": 10409731 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0xd364C06ac99a82a00d3eFF9F2F78E4Abe4b9baAA", + "commit_store": "0xdE8d0f47a71eA3fDFBD3162271652f2847939097", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Avalanche Fuji": { + "off_ramp": "0xAd91214efFee446500940c764DF77AF18427294F", + "commit_store": "0x1242b6c5e0e349b8d4BCf0938f961C4B4f7EA3Fa", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "BSC Testnet": { + "off_ramp": "0xd5E9508921434e8758f4540D55c1c066b7cc1598", + "commit_store": "0x1a86b29364D1B3fA3386329A361aA98A104b2742", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Gnosis Chiado": { + "off_ramp": "0x9Bb7e398ef9Acfe9cA584C39B1E233Cba62BB9f7", + "commit_store": "0x1F4B82cDebaC5e3a0Dd53183D47e51808B4a64cB", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Optimism Sepolia": { + "off_ramp": "0x86a3910908eCaAA31Fcd9F0fC8841D8E98f1511d", + "commit_store": "0xE99a87C9b5ed4D2b6060195DEea5106ffF655736", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Sepolia Testnet": { + "off_ramp": "0x189F61D9B886Dd2975D5Abc893c8Cf5f5effda71", + "commit_store": "0xEE7e27346DCD1e711348D0F7f7ECB53a9a3a08a7", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "ethereum-testnet-sepolia-mode-1": { + "off_ramp": "0xB26647A23e8b4284375e5C74b77c9557aE709D03", + "commit_store": "0x4b4fEB401d3E613e1D6242E155C83A80BF9ac2C9", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "Gnosis Chiado": { + "is_native_fee_token": true, + "fee_token": "0xDCA67FD8324990792C0bfaE95903B8A64097754F", + "bridge_tokens": [ + ], + "bridge_tokens_pools": [ + ], + "price_aggregators": null, + "arm": "0xb6f1Fe2CDE891eFd5Efd2A563C4C2F2549163718", + "router": "0x19b1bac554111517831ACadc0FD119D23Bb14391", + "price_registry": "0x2F4ACd1f8986c6B1788159C4c9a5fC3fceCCE363", + "wrapped_native": "0x18c8a7ec7897177E4529065a7E7B0878358B3BfF", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0x473b49fb592B54a4BfCD55d40E048431982879C9", + "deployed_at": 9718588 + }, + "Avalanche Fuji": { + "on_ramp": "0x610F76A35E17DA4542518D85FfEa12645eF111Fc", + "deployed_at": 9718676 + }, + "BSC Testnet": { + "on_ramp": "0xE48E6AA1fc7D0411acEA95F8C6CaD972A37721D4", + "deployed_at": 9718302 + }, + "Base Sepolia": { + "on_ramp": "0x41b4A51cAfb699D9504E89d19D71F92E886028a8", + "deployed_at": 9718513 + }, + "Optimism Sepolia": { + "on_ramp": "0xAae733212981e06D9C978Eb5148F8af03F54b6EF", + "deployed_at": 9718420 + }, + "Polygon Amoy": { + "on_ramp": "0x01800fCDd892e37f7829937271840A6F041bE62E", + "deployed_at": 9718194 + }, + "Sepolia Testnet": { + "on_ramp": "0x4ac7FBEc2A7298AbDf0E0F4fDC45015836C4bAFe", + "deployed_at": 8487681 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0x9aA82DBB53bf02096B771D40e9432A323a78fB26", + "commit_store": "0x5CdbA91aBC0cD81FC56bc10Ad1835C9E5fB38e5F", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Avalanche Fuji": { + "off_ramp": "0x3e33290B90fD0FF30a3FA138934DF028E4eCA348", + "commit_store": "0xCFe3556Aa42d40be09BD23aa80448a19443BE5B1", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "BSC Testnet": { + "off_ramp": "0xbc4AD54e91b213D4279af92c0C5518c0b96cf62D", + "commit_store": "0xff84e8Dd4Fd17eaBb23b6AeA6e1981830e54389C", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Base Sepolia": { + "off_ramp": "0x4117953A5ceeF12f5B8C1E973b470ab83a8CebA6", + "commit_store": "0x94ad41296186E81f31e1ed0B1BcF5fa9e1721C27", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Optimism Sepolia": { + "off_ramp": "0x33d2898F8fb7714FD1661791766f40754982a343", + "commit_store": "0x55d6Df194472f02CD481e506A277c4A29D0D1bCc", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Polygon Amoy": { + "off_ramp": "0x450543b1d85ca79885851D7b74dc982981b78229", + "commit_store": "0x23B79d940A769FE31b4C867A8BAE80117f24Ca81", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Sepolia Testnet": { + "off_ramp": "0xbf9036529123DE264bFA0FC7362fE25B650D4B16", + "commit_store": "0x5f7F1abD5c5EdaF2636D58B980e85355AF0Ef80d", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "Kroma Sepolia": { + "is_native_fee_token": true, + "fee_token": "0xa75cCA5b404ec6F4BB6EC4853D177FE7057085c8", + "bridge_tokens": [ + ], + "bridge_tokens_pools": [ + ], + "price_aggregators": null, + "arm": "0x1E4e4e0d6f6631A45C616F71a1A5cF208DB9eCDe", + "router": "0xA8C0c11bf64AF62CDCA6f93D3769B88BdD7cb93D", + "price_registry": "0xa1ed3A3aA29166C9c8448654A8cA6b7916BC8379", + "wrapped_native": "0x4200000000000000000000000000000000000001", + "src_contracts": { + "WeMix Testnet": { + "on_ramp": "0x6ea155Fc77566D9dcE01B8aa5D7968665dc4f0C5", + "deployed_at": 10290904 + } + }, + "dest_contracts": { + "WeMix Testnet": { + "off_ramp": "0xB602B6E5Caf08ac0C920EAE585aed100a8cF6f3B", + "commit_store": "0x89D5b13908b9063abCC6791dc724bF7B7c93634C", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "Optimism Sepolia": { + "is_native_fee_token": true, + "fee_token": "0xE4aB69C077896252FAFBD49EFD26B5D171A32410", + "bridge_tokens": [ + ], + "bridge_tokens_pools": [ + ], + "price_aggregators": null, + "arm": "0xf06Ff5D2084295909119ca541E93635E7D582FFc", + "router": "0x114A20A10b43D4115e5aeef7345a1A71d2a60C57", + "price_registry": "0x782a7Ba95215f2F7c3dD4C153cbB2Ae3Ec2d3215", + "wrapped_native": "0x4200000000000000000000000000000000000006", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0x1a86b29364D1B3fA3386329A361aA98A104b2742", + "deployed_at": 10841494 + }, + "Avalanche Fuji": { + "on_ramp": "0x6b38CC6Fa938D5AB09Bdf0CFe580E226fDD793cE", + "deployed_at": 8677537 + }, + "Base Sepolia": { + "on_ramp": "0xe284D2315a28c4d62C419e8474dC457b219DB969", + "deployed_at": 7130524 + }, + "Gnosis Chiado": { + "on_ramp": "0x835a5b8e6CA17c2bB5A336c93a4E22478E6F1C8A", + "deployed_at": 11799783 + }, + "Polygon Amoy": { + "on_ramp": "0x2Cf26fb01E9ccDb831414B766287c0A9e4551089", + "deployed_at": 10813146 + }, + "Sepolia Testnet": { + "on_ramp": "0xC8b93b46BF682c39B3F65Aa1c135bC8A95A5E43a", + "deployed_at": 12165583 + }, + "WeMix Testnet": { + "on_ramp": "0xc7E53f6aB982af7A7C3e470c8cCa283d3399BDAd", + "deployed_at": 8733017 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0xDc2c7A3d8068C6F09F0F3648d24C84e372F6014d", + "commit_store": "0xb1aFb5cbE3c29b5Db71F21442BA9EfD450BC23C3", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Avalanche Fuji": { + "off_ramp": "0x1F350718e015EB20E5065C09F4A7a3f66888aEeD", + "commit_store": "0x98650A8EB59f75D93563aB34FcF603b1A30e4CBF", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Base Sepolia": { + "off_ramp": "0x0a750ca77369e03613d7640548F4b2b1c695c3Bb", + "commit_store": "0x8fEBC74C26129C8d7E60288C6dCCc75eb494aA3C", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Gnosis Chiado": { + "off_ramp": "0xCE2CE7F940B7c839384e5D7e079A6aE80e8AD6dB", + "commit_store": "0x1b9D78Ec1CEEC439F0b7eA6C428A1a607D9FA7e4", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Polygon Amoy": { + "off_ramp": "0xD667b5706592D0b040C78fEe5EE17D243b7dCB41", + "commit_store": "0x96101BA5250EE9295c193693C1e08A55bC593664", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Sepolia Testnet": { + "off_ramp": "0x260AF9b83e0d2Bb6C9015fC9f0BfF8858A0CCE68", + "commit_store": "0x7a0bB92Bc8663abe6296d0162A9b41a2Cb2E0358", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "WeMix Testnet": { + "off_ramp": "0x9C08B7712af0344188aa5087D9e6aD0f47191037", + "commit_store": "0x4BE6DB0B884169a6A207fe5cad01eB4C025a13dB", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "Polygon Amoy": { + "is_native_fee_token": true, + "fee_token": "0x0Fd9e8d3aF1aaee056EB9e802c3A762a667b1904", + "bridge_tokens": [ + ], + "bridge_tokens_pools": [ + ], + "price_aggregators": null, + "arm": "0x50b023c5b33AEe5Adef15C2E95C2fEC690a52fa1", + "router": "0x9C32fCB86BF0f4a1A8921a9Fe46de3198bb884B2", + "price_registry": "0xfb2f2A207dC428da81fbAFfDDe121761f8Be1194", + "wrapped_native": "0x360ad4f9a9A8EFe9A8DCB5f461c4Cc1047E1Dcf9", + "src_contracts": { + "Avalanche Fuji": { + "on_ramp": "0x8Fb98b3837578aceEA32b454f3221FE18D7Ce903", + "deployed_at": 6004551 + }, + "BSC Testnet": { + "on_ramp": "0xC6683ac4a0F62803Bec89a5355B36495ddF2C38b", + "deployed_at": 6005330 + }, + "Gnosis Chiado": { + "on_ramp": "0x2331F6D614C9Fd613Ff59a1aB727f1EDf6c37A68", + "deployed_at": 6897885 + }, + "Optimism Sepolia": { + "on_ramp": "0xA52cDAeb43803A80B3c0C2296f5cFe57e695BE11", + "deployed_at": 6004902 + }, + "Sepolia Testnet": { + "on_ramp": "0x35347A2fC1f2a4c5Eae03339040d0b83b09e6FDA", + "deployed_at": 6004056 + }, + "WeMix Testnet": { + "on_ramp": "0x26546096F64B5eF9A1DcDAe70Df6F4f8c2E10C61", + "deployed_at": 6005611 + } + }, + "dest_contracts": { + "Avalanche Fuji": { + "off_ramp": "0xa733ce82a84335b2E9D864312225B0F3D5d80600", + "commit_store": "0x09B0F93fC2111aE439e853884173AC5b2F809885", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "BSC Testnet": { + "off_ramp": "0x948dfaa4842fc23e0e362Fe8D4396AaE4E6DF7EA", + "commit_store": "0x7F4e739D40E58BBd59dAD388171d18e37B26326f", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Gnosis Chiado": { + "off_ramp": "0x17c542a28e08AEF5697251601C7b2B621d153D42", + "commit_store": "0x811250c20fAB9a1b7ca245453aC214ba637fBEB5", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Optimism Sepolia": { + "off_ramp": "0xfFdE9E8c34A27BEBeaCcAcB7b3044A0A364455C9", + "commit_store": "0x74ED442ad211050e9C05Dc9A267E037E3d74A03B", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Sepolia Testnet": { + "off_ramp": "0xFb04129aD1EEDB741CC705ebC1978a7aB63e51f6", + "commit_store": "0x63f875240149d29136053C954Ca164a9BfA81F77", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "WeMix Testnet": { + "off_ramp": "0xdE8451E952Eb43350614839cCAA84f7C8701a09C", + "commit_store": "0xaCdaBa07ECad81dc634458b98673931DD9d3Bc14", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "Sepolia Testnet": { + "is_native_fee_token": true, + "fee_token": "0x779877A7B0D9E8603169DdbD7836e478b4624789", + "bridge_tokens": [ + ], + "bridge_tokens_pools": [ + ], + "price_aggregators": null, + "arm": "0xB4d360459F32Dd641Ef5A6985fFbAC5c4e5521aA", + "router": "0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59", + "price_registry": "0x9EF7D57a4ea30b9e37794E55b0C75F2A70275dCc", + "wrapped_native": "0x097D90c9d3E0B50Ca60e1ae45F6A81010f9FB534", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0xe4Dd3B16E09c016402585a8aDFdB4A18f772a07e", + "deployed_at": 5737506 + }, + "Avalanche Fuji": { + "on_ramp": "0x0477cA0a35eE05D3f9f424d88bC0977ceCf339D4", + "deployed_at": 5944649 + }, + "BSC Testnet": { + "on_ramp": "0xD990f8aFA5BCB02f95eEd88ecB7C68f5998bD618", + "deployed_at": 5383500 + }, + "Base Sepolia": { + "on_ramp": "0x2B70a05320cB069e0fB55084D402343F832556E7", + "deployed_at": 5619657 + }, + "Gnosis Chiado": { + "on_ramp": "0x3E842E3A79A00AFdd03B52390B1caC6306Ea257E", + "deployed_at": 5386355 + }, + "Optimism Sepolia": { + "on_ramp": "0x69CaB5A0a08a12BaFD8f5B195989D709E396Ed4d", + "deployed_at": 5937506 + }, + "Polygon Amoy": { + "on_ramp": "0x9f656e0361Fb5Df2ac446102c8aB31855B591692", + "deployed_at": 5723315 + }, + "WeMix Testnet": { + "on_ramp": "0xedFc22336Eb0B9B11Ff37C07777db27BCcDe3C65", + "deployed_at": 5393931 + }, + "celo-testnet-alfajores": { + "on_ramp": "0x3C86d16F52C10B2ff6696a0e1b8E0BcfCC085948", + "deployed_at": 5704643 + }, + "ethereum-testnet-sepolia-blast-1": { + "on_ramp": "0xDB75E9D9ca7577CcBd7232741be954cf26194a66", + "deployed_at": 6040848 + }, + "ethereum-testnet-sepolia-metis-1": { + "on_ramp": "0x1C4640914cd57c5f02a68048A0fbb0E12d904223", + "deployed_at": 6002793 + }, + "ethereum-testnet-sepolia-mode-1": { + "on_ramp": "0xc630fbD4D0F6AEB00aD0793FB827b54fBB78e981", + "deployed_at": 5970819 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0xF18896AB20a09A29e64fdEbA99FDb8EC328f43b1", + "commit_store": "0x93Ff9Dd39Dc01eac1fc4d2c9211D95Ee458CAB94", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Avalanche Fuji": { + "off_ramp": "0x000b26f604eAadC3D874a4404bde6D64a97d95ca", + "commit_store": "0x2dD9273F8208B8393350508131270A6574A69784", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "BSC Testnet": { + "off_ramp": "0xdE2d8E126e08d675fCD7fFa5a6CE49925f3Dc692", + "commit_store": "0x0050ac355a82caB31194507f94174297bf0655A7", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Base Sepolia": { + "off_ramp": "0x31c0B81832B333419f0DfD36A69F502cF9094aed", + "commit_store": "0xDFcde9d698a2B32DB2537DC9B752Cadd1D846a52", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Gnosis Chiado": { + "off_ramp": "0x7db0115A0b3AAb01d30bf81123c5DD7B0C41Add5", + "commit_store": "0x6640723Ea801178c4383FA016b9781e7ef1016EF", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Optimism Sepolia": { + "off_ramp": "0xD50590D4438411EDe47029b0FD7901A7145E5Df6", + "commit_store": "0xe85EEE9Fd434A7b8a586Ee086E828abF41839479", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Polygon Amoy": { + "off_ramp": "0x5032cbC0C4aEeD25bb6E45D8B3fAF05DB0688C5d", + "commit_store": "0xe6201C9996Cc7B6E828E10CbE937E693d577D318", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "WeMix Testnet": { + "off_ramp": "0x46b639a3C1a4CBfD326b94a2dB7415c27157282f", + "commit_store": "0x7b74554678816b045c1e7409327E086bD436aa46", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "celo-testnet-alfajores": { + "off_ramp": "0xB435E0f73c18C5a12C324CA1d02F81F2C3e6e761", + "commit_store": "0xbc5d74957F171e75F92c8F0E1C317A25a56a416D", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "ethereum-testnet-sepolia-blast-1": { + "off_ramp": "0x4e897e5cF3aC307F0541B2151A88bCD781c153a3", + "commit_store": "0xB656652841F347178e193951C4663652aCe36B74", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "ethereum-testnet-sepolia-metis-1": { + "off_ramp": "0x4DB693A93E9d5196ECD42EC56CDEAe99dFC652ED", + "commit_store": "0xBfACd78F1412B6f93Ac23409bf456aFec1ABd845", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "ethereum-testnet-sepolia-mode-1": { + "off_ramp": "0xbEfd8D65F6643De54F0b1268A3bf4618ff85dcB4", + "commit_store": "0x0C161D3470b45Cc677661654C30ce4AdE6aCD288", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "WeMix Testnet": { + "is_native_fee_token": true, + "fee_token": "0x3580c7A817cCD41f7e02143BFa411D4EeAE78093", + "bridge_tokens": [ + ], + "bridge_tokens_pools": [ + ], + "price_aggregators": null, + "arm": "0x46fF31494651593973D9b38a872ED5B06f45A693", + "router": "0xA8C0c11bf64AF62CDCA6f93D3769B88BdD7cb93D", + "price_registry": "0x89D17571DB7C9540eeB36760E3c749C8fb984569", + "wrapped_native": "0xbE3686643c05f00eC46e73da594c78098F7a9Ae7", + "src_contracts": { + "Arbitrum Sepolia": { + "on_ramp": "0xA9DE3F7A617D67bC50c56baaCb9E0373C15EbfC6", + "deployed_at": 51216113 + }, + "Avalanche Fuji": { + "on_ramp": "0xC4aC84da458ba8e40210D2dF94C76E9a41f70069", + "deployed_at": 51214769 + }, + "BSC Testnet": { + "on_ramp": "0x5AD6eed6Be0ffaDCA4105050CF0E584D87E0c2F1", + "deployed_at": 51213771 + }, + "Kroma Sepolia": { + "on_ramp": "0x428C4dc89b6Bf908B82d77C9CBceA786ea8cc7D0", + "deployed_at": 51239062 + }, + "Optimism Sepolia": { + "on_ramp": "0x1961a7De751451F410391c251D4D4F98D71B767D", + "deployed_at": 51216748 + }, + "Polygon Amoy": { + "on_ramp": "0xd55148e841e76265B484d399eC71b7076ecB1216", + "deployed_at": 55378685 + }, + "Sepolia Testnet": { + "on_ramp": "0x4d57C6d8037C65fa66D6231844785a428310a735", + "deployed_at": 51239309 + } + }, + "dest_contracts": { + "Arbitrum Sepolia": { + "off_ramp": "0xeB1dFaB2464Bf0574D43e764E0c758f92e7ecAFb", + "commit_store": "0xcEaCa2B7890065c485f3E58657358a185Ad33791", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Avalanche Fuji": { + "off_ramp": "0x98e811Df9D2512f1aaf58D534607F583D6c54A4F", + "commit_store": "0x8e538351F6E5B2daF3c90C565C3738bca69a2716", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "BSC Testnet": { + "off_ramp": "0xB0e7f0fCcD3c961C473E7c44D939C1cDb4Cec1cB", + "commit_store": "0x4B56D8d53f1A6e0117B09700067De99581aA5542", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Kroma Sepolia": { + "off_ramp": "0xD685D2d224dd6D0Db2D56497db6270D77D9a7966", + "commit_store": "0x7e062D6880779a0347e7742058C1b1Ee4AA0B137", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Optimism Sepolia": { + "off_ramp": "0xA5f97Bc69Bf06e7C37B93265c5457420A92c5F4b", + "commit_store": "0xd48b9213583074f518D8f4336FDf35370D450132", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Polygon Amoy": { + "off_ramp": "0x6c8f5999B06FDE17B11E4e3C1062b761766F960f", + "commit_store": "0x957c3c2056192e58A8485eF31165fC490d474239", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Sepolia Testnet": { + "off_ramp": "0x8AB103843ED9D28D2C5DAf5FdB9c3e1CE2B6c876", + "commit_store": "0x7d5297c5506ee2A7Ef121Da9bE02b6a6AD30b392", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "celo-testnet-alfajores": { + "is_native_fee_token": true, + "fee_token": "0x32E08557B14FaD8908025619797221281D439071", + "bridge_tokens": [ + ], + "bridge_tokens_pools": [ + ], + "price_aggregators": null, + "arm": "0xbE8FD4b84ca8CC2cFAeeEf8dc1388E44860eeEeb", + "router": "0xb00E95b773528E2Ea724DB06B75113F239D15Dca", + "price_registry": "0x8F048206D11B2c69b8963E2EBd5968D141e022f4", + "wrapped_native": "0x99604d0e2EfE7ABFb58BdE565b5330Bb46Ab3Dca", + "src_contracts": { + "Sepolia Testnet": { + "on_ramp": "0x16a020c4bbdE363FaB8481262D30516AdbcfcFc8", + "deployed_at": 23561364 + } + }, + "dest_contracts": { + "Sepolia Testnet": { + "off_ramp": "0xa1b97F92D806BA040daf419AFC2765DC723683a4", + "commit_store": "0xcd92C0599Ac515e7588865cC45Eee21A74816aFc", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "ethereum-testnet-sepolia-blast-1": { + "is_native_fee_token": true, + "fee_token": "0x02c359ebf98fc8BF793F970F9B8302bb373BdF32", + "bridge_tokens": [ + ], + "bridge_tokens_pools": [ + ], + "price_aggregators": null, + "arm": "0x9C32fCB86BF0f4a1A8921a9Fe46de3198bb884B2", + "router": "0xfb2f2A207dC428da81fbAFfDDe121761f8Be1194", + "price_registry": "0xc8acE9dF450FaD007755C6C9AB4f0e9c8626E29C", + "wrapped_native": "0x4200000000000000000000000000000000000023", + "src_contracts": { + "Sepolia Testnet": { + "on_ramp": "0x85Ef19FC4C63c70744995DC38CAAEC185E0c619f", + "deployed_at": 6429339 + } + }, + "dest_contracts": { + "Sepolia Testnet": { + "off_ramp": "0x92cD24C278D34C726f377703E50875d8f9535dC2", + "commit_store": "0xcE1b4D50CeD56850182Bd58Ace91171cB249B873", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "ethereum-testnet-sepolia-metis-1": { + "is_native_fee_token": true, + "fee_token": "0x9870D6a0e05F867EAAe696e106741843F7fD116D", + "bridge_tokens": [ + ], + "bridge_tokens_pools": [ + ], + "price_aggregators": null, + "arm": "0x26546096F64B5eF9A1DcDAe70Df6F4f8c2E10C61", + "router": "0xaCdaBa07ECad81dc634458b98673931DD9d3Bc14", + "price_registry": "0x5DCE866b3ae6E0Ed153f0e149D7203A1B266cdF5", + "wrapped_native": "0x5c48e07062aC4E2Cf4b9A768a711Aef18e8fbdA0", + "src_contracts": { + "Sepolia Testnet": { + "on_ramp": "0x2Eff2d1BF5C557d6289D208a7a43608f5E3FeCc2", + "deployed_at": 858864 + } + }, + "dest_contracts": { + "Sepolia Testnet": { + "off_ramp": "0x9Bb7e398ef9Acfe9cA584C39B1E233Cba62BB9f7", + "commit_store": "0x1F4B82cDebaC5e3a0Dd53183D47e51808B4a64cB", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + }, + "ethereum-testnet-sepolia-mode-1": { + "is_native_fee_token": true, + "fee_token": "0x925a4bfE64AE2bFAC8a02b35F78e60C29743755d", + "bridge_tokens": [ + ], + "bridge_tokens_pools": [ + ], + "price_aggregators": null, + "arm": "0x11545812A8d64e4A3A0Ec36b6F70D87b42Ce4a01", + "router": "0xc49ec0eB4beb48B8Da4cceC51AA9A5bD0D0A4c43", + "price_registry": "0xa733ce82a84335b2E9D864312225B0F3D5d80600", + "wrapped_native": "0x4200000000000000000000000000000000000006", + "src_contracts": { + "Base Sepolia": { + "on_ramp": "0x73f7E074bd7291706a0C5412f51DB46441B1aDCB", + "deployed_at": 14359909 + }, + "Sepolia Testnet": { + "on_ramp": "0xfFdE9E8c34A27BEBeaCcAcB7b3044A0A364455C9", + "deployed_at": 14359680 + } + }, + "dest_contracts": { + "Base Sepolia": { + "off_ramp": "0x137a38c6b1Ad20101F93516aB2159Df525309168", + "commit_store": "0x8F43d867969F14619895d71E0A5b89E0bb20bF70", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + }, + "Sepolia Testnet": { + "off_ramp": "0xcD44cec849B6a8eBd5551D6DFeEcA452257Dfe4d", + "commit_store": "0xbA66f08733E6715D33edDfb5a5947676bb45d0e0", + "receiver_dapp": "0x1A2A69e3eB1382FE34Bc579AdD5Bae39e31d4A2c" + } + } + } + } +} +""" + +[CCIP.Env] +TTL = '8h' + +[CCIP.Env.Network] +selected_networks = [ + 'ARBITRUM_SEPOLIA', + 'AVALANCHE_FUJI', + 'OPTIMISM_SEPOLIA', + 'BASE_SEPOLIA', + 'BSC_TESTNET', + 'WEMIX_TESTNET', + 'SEPOLIA', + 'POLYGON_AMOY', + 'KROMA_SEPOLIA', + 'BLAST_SEPOLIA' +] + +[CCIP.Groups.load] +NetworkPairs = [ + 'AVALANCHE_FUJI,SEPOLIA', + 'OPTIMISM_SEPOLIA,BASE_SEPOLIA' +] + +BiDirectionalLane = true +PhaseTimeout = '45m' +ExistingDeployment = true + +NoOfTokensPerChain = 1 + +[CCIP.Groups.load.LoadProfile] +RequestPerUnitTime = [1] +TimeUnit = '5s' +TestDuration = '1h' +TestRunName = 'v2.12.0-ccip1.4.16-load' + +# to represent 20%, 60%, 15%, 5% of the total messages +[CCIP.Groups.load.LoadProfile.MsgProfile] +Frequencies = [4,12,3,1] + +[[CCIP.Groups.load.LoadProfile.MsgProfile.MsgDetails]] +MsgType = 'Token' +DestGasLimit = 0 +DataLength = 0 +NoOfTokens = 5 +AmountPerToken = 1 + +[[CCIP.Groups.load.LoadProfile.MsgProfile.MsgDetails]] +MsgType = 'DataWithToken' +DestGasLimit = 500000 +DataLength = 5000 +NoOfTokens = 5 +AmountPerToken = 1 + +[[CCIP.Groups.load.LoadProfile.MsgProfile.MsgDetails]] +MsgType = 'Data' +DestGasLimit = 800000 +DataLength = 10000 + +[[CCIP.Groups.load.LoadProfile.MsgProfile.MsgDetails]] +MsgType = 'Data' +DestGasLimit = 2500000 +DataLength = 10000 \ No newline at end of file diff --git a/integration-tests/ccip-tests/testreporters/ccip.go b/integration-tests/ccip-tests/testreporters/ccip.go index d9c2908304..b567f6a629 100644 --- a/integration-tests/ccip-tests/testreporters/ccip.go +++ b/integration-tests/ccip-tests/testreporters/ccip.go @@ -13,8 +13,8 @@ import ( "github.com/rs/zerolog" "github.com/slack-go/slack" - "github.com/smartcontractkit/chainlink-testing-framework/lib/k8s/config" - "github.com/smartcontractkit/chainlink-testing-framework/lib/testreporters" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/config" + "github.com/smartcontractkit/chainlink-testing-framework/testreporters" ) type Phase string diff --git a/integration-tests/chaos/ocr_chaos_test.go b/integration-tests/chaos/ocr_chaos_test.go index 89417d782d..200c97a795 100644 --- a/integration-tests/chaos/ocr_chaos_test.go +++ b/integration-tests/chaos/ocr_chaos_test.go @@ -9,19 +9,19 @@ import ( "github.com/onsi/gomega" "github.com/stretchr/testify/require" - ctfClient "github.com/smartcontractkit/chainlink-testing-framework/lib/client" - ctf_config "github.com/smartcontractkit/chainlink-testing-framework/lib/config" - "github.com/smartcontractkit/chainlink-testing-framework/lib/k8s/chaos" - "github.com/smartcontractkit/chainlink-testing-framework/lib/k8s/environment" - "github.com/smartcontractkit/chainlink-testing-framework/lib/k8s/pkg/helm/chainlink" - "github.com/smartcontractkit/chainlink-testing-framework/lib/k8s/pkg/helm/ethereum" - "github.com/smartcontractkit/chainlink-testing-framework/lib/k8s/pkg/helm/mockserver" - mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/lib/k8s/pkg/helm/mockserver-cfg" - "github.com/smartcontractkit/chainlink-testing-framework/lib/logging" - "github.com/smartcontractkit/chainlink-testing-framework/lib/networks" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/ptr" - seth_utils "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/seth" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" + ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" + ctf_config "github.com/smartcontractkit/chainlink-testing-framework/config" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/chaos" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver" + mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver-cfg" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/networks" + "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" + seth_utils "github.com/smartcontractkit/chainlink-testing-framework/utils/seth" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" @@ -84,7 +84,7 @@ func TestOCRChaos(t *testing.T) { // and chaos.NewNetworkPartition method (https://chaos-mesh.org/docs/simulate-network-chaos-on-kubernetes/) // in order to regenerate Go bindings if k8s version will be updated // you can pull new CRD spec from your current cluster and check README here - // https://github.com/smartcontractkit/chainlink-testing-framework/lib/k8s/blob/master/README.md + // https://github.com/smartcontractkit/chainlink-testing-framework/k8s/blob/master/README.md NetworkChaosFailMajorityNetwork: { ethereum.New(nil), chainlinkCfg, @@ -179,7 +179,6 @@ func TestOCRChaos(t *testing.T) { }) ms := ctfClient.ConnectMockServer(testEnvironment) - linkContract, err := contracts.DeployLinkTokenContract(l, seth) require.NoError(t, err, "Error deploying link token contract") diff --git a/integration-tests/citool/cmd/check_tests_cmd.go b/integration-tests/citool/cmd/check_tests_cmd.go deleted file mode 100644 index 3ef3712a57..0000000000 --- a/integration-tests/citool/cmd/check_tests_cmd.go +++ /dev/null @@ -1,166 +0,0 @@ -package cmd - -import ( - "fmt" - "os" - "path/filepath" - "regexp" - "strings" - - "github.com/spf13/cobra" - "gopkg.in/yaml.v3" -) - -type JobConfig struct { - Jobs map[string]struct { - Strategy struct { - Matrix struct { - Test []struct { - Path string `yaml:"path"` - TestOpts string `yaml:"testOpts"` - } `yaml:"test"` - } `yaml:"matrix"` - } `yaml:"strategy"` - } `yaml:"jobs"` -} - -var checkTestsCmd = &cobra.Command{ - Use: "check-tests [directory] [yaml file]", - Short: "Check if all tests in a directory are included in the test configurations YAML file", - Args: cobra.ExactArgs(2), - Run: func(_ *cobra.Command, args []string) { - directory := args[0] - yamlFile := args[1] - excludedDirs := []string{"../../citool"} - tests, err := extractTests(directory, excludedDirs) - if err != nil { - fmt.Println("Error extracting tests:", err) - os.Exit(1) - } - - checkTestsInPipeline(yamlFile, tests) - }, -} - -// extractTests scans the given directory and subdirectories (except the excluded ones) -// for Go test files, extracts test function names, and returns a slice of Test. -func extractTests(dir string, excludeDirs []string) ([]Test, error) { - var tests []Test - - // Resolve to absolute path - absDir, err := filepath.Abs(dir) - if err != nil { - return nil, err - } - - // filepath.WalkDir provides more control and is more efficient for skipping directories - err = filepath.WalkDir(absDir, func(path string, d os.DirEntry, err error) error { - if err != nil { - return err - } - - // Check if the current path is one of the excluded directories - for _, exclude := range excludeDirs { - absExclude, _ := filepath.Abs(exclude) - if strings.HasPrefix(path, absExclude) { - if d.IsDir() { - return filepath.SkipDir // Skip this directory - } - return nil // Skip this file - } - } - - if !d.IsDir() && strings.HasSuffix(d.Name(), "_test.go") { - content, err := os.ReadFile(path) - if err != nil { - return err - } - re := regexp.MustCompile(`func (Test\w+)`) - matches := re.FindAllSubmatch(content, -1) - for _, match := range matches { - funcName := string(match[1]) - if funcName == "TestMain" { // Skip "TestMain" - continue - } - tests = append(tests, Test{ - Name: funcName, - Path: mustExtractSubpath(path, "integration-tests"), - }) - } - } - return nil - }) - - return tests, err -} - -// ExtractSubpath extracts a specific subpath from a given full path. -// If the subpath is not found, it returns an error. -func mustExtractSubpath(fullPath, subPath string) string { - index := strings.Index(fullPath, subPath) - if index == -1 { - panic("subpath not found in the provided full path") - } - return fullPath[index:] -} - -func checkTestsInPipeline(yamlFile string, tests []Test) { - data, err := os.ReadFile(yamlFile) - if err != nil { - fmt.Printf("Error reading YAML file: %s\n", err) - return - } - - var config Config - err = yaml.Unmarshal(data, &config) - if err != nil { - fmt.Printf("Error parsing YAML: %s\n", err) - return - } - - missingTests := []string{} // Track missing tests - - for _, test := range tests { - found := false - for _, item := range config.Tests { - if item.Path == test.Path { - if strings.Contains(item.TestCmd, "-test.run") { - if matchTestNameInCmd(item.TestCmd, test.Name) { - found = true - break - } - } else { - found = true - break - } - } - } - if !found { - missingTests = append(missingTests, fmt.Sprintf("ERROR: Test '%s' in file '%s' does not have CI configuration in '%s'", test.Name, test.Path, yamlFile)) - } - } - - if len(missingTests) > 0 { - for _, missing := range missingTests { - fmt.Println(missing) - } - os.Exit(1) // Exit with a failure status - } -} - -// matchTestNameInCmd checks if the given test name matches the -test.run pattern in the command string. -func matchTestNameInCmd(cmd string, testName string) bool { - testRunRegex := regexp.MustCompile(`-test\.run ([^\s]+)`) - matches := testRunRegex.FindStringSubmatch(cmd) - if len(matches) > 1 { - // Extract the regex pattern used in the -test.run command - pattern := matches[1] - - // Escape regex metacharacters in the testName before matching - escapedTestName := regexp.QuoteMeta(testName) - - // Check if the escaped test name matches the extracted pattern - return regexp.MustCompile(pattern).MatchString(escapedTestName) - } - return false -} diff --git a/integration-tests/citool/cmd/check_tests_cmd_test.go b/integration-tests/citool/cmd/check_tests_cmd_test.go deleted file mode 100644 index 4b7f50e7f0..0000000000 --- a/integration-tests/citool/cmd/check_tests_cmd_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package cmd - -import ( - "testing" -) - -func TestMatchTestNameInCmd(t *testing.T) { - tests := []struct { - cmd string - testName string - expected bool - }{ - {"go test -test.run ^TestExample$", "TestExample", true}, - {"go test -test.run ^TestExample$", "TestAnother", false}, - {"go test -test.run ^TestExample$ -v", "TestExample", true}, - {"go test -test.run ^TestExamplePart$", "TestExample", false}, - {"go test -test.run ^TestWithNumbers123$", "TestWithNumbers123", true}, - {"go test -test.run ^Test_With_Underscores$", "Test_With_Underscores", true}, - {"go test -test.run ^Test-With-Dash$", "Test-With-Dash", true}, - {"go test -test.run ^TestWithSpace Space$", "TestWithSpace Space", true}, - {"go test -test.run ^TestWithNewline\nNewline$", "TestWithNewline\nNewline", true}, - {"go test -test.run ^TestOne$|^TestTwo$", "TestOne", true}, - {"go test -test.run ^TestOne$|^TestTwo$", "TestTwo", true}, - {"go test -test.run ^TestOne$|^TestTwo$", "TestThree", false}, - {"go test -test.run TestOne|TestTwo", "TestTwo", true}, - {"go test -test.run TestOne|TestTwo", "TestOne", true}, - {"go test -test.run TestOne|TestTwo|TestThree", "TestFour", false}, - {"go test -test.run ^TestOne$|TestTwo$", "TestTwo", true}, - {"go test -test.run ^TestOne$|TestTwo|TestThree$", "TestThree", true}, - {"go test -test.run TestOne|TestTwo|TestThree", "TestOne", true}, - {"go test -test.run TestOne|TestTwo|TestThree", "TestThree", true}, - {"go test -test.run ^TestA$|^TestB$|^TestC$", "TestA", true}, - {"go test -test.run ^TestA$|^TestB$|^TestC$", "TestB", true}, - {"go test -test.run ^TestA$|^TestB$|^TestC$", "TestD", false}, - {"go test -test.run TestA|^TestB$|TestC", "TestB", true}, - {"go test -test.run ^TestA|^TestB|TestC$", "TestA", true}, - {"go test -test.run ^TestA|^TestB|TestC$", "TestC", true}, - {"go test -test.run ^TestA|^TestB|TestC$", "TestD", false}, - } - - for _, tt := range tests { - result := matchTestNameInCmd(tt.cmd, tt.testName) - if result != tt.expected { - t.Errorf("matchTestNameInCmd(%s, %s) = %t; expected %t", tt.cmd, tt.testName, result, tt.expected) - } - } -} diff --git a/integration-tests/citool/cmd/create_test_config_cmd.go b/integration-tests/citool/cmd/create_test_config_cmd.go deleted file mode 100644 index 3a285a4e98..0000000000 --- a/integration-tests/citool/cmd/create_test_config_cmd.go +++ /dev/null @@ -1,179 +0,0 @@ -package cmd - -import ( - "fmt" - "os" - - "github.com/pelletier/go-toml/v2" - "github.com/spf13/cobra" - - ctfconfig "github.com/smartcontractkit/chainlink-testing-framework/lib/config" - ctfconfigtypes "github.com/smartcontractkit/chainlink-testing-framework/lib/config/types" -) - -var createTestConfigCmd = &cobra.Command{ - Use: "create", - Short: "Create a test config from the provided flags", - Run: func(cmd *cobra.Command, _ []string) { - var tc ctfconfig.TestConfig - - var version, postgresVersion *string - if cmd.Flags().Changed(ChainlinkVersionFlag) { - version = &oc.ChainlinkVersion - } - if cmd.Flags().Changed(ChainlinkPostgresVersionFlag) { - version = &oc.ChainlinkPostgresVersion - } - if version != nil || postgresVersion != nil { - tc.ChainlinkImage = &ctfconfig.ChainlinkImageConfig{ - Version: version, - PostgresVersion: postgresVersion, - } - } - - var upgradeVersion *string - if cmd.Flags().Changed(ChainlinkUpgradeVersionFlag) { - upgradeVersion = &oc.ChainlinkUpgradeVersion - } - if upgradeVersion != nil { - tc.ChainlinkUpgradeImage = &ctfconfig.ChainlinkImageConfig{ - Version: upgradeVersion, - } - } - - var selectedNetworks *[]string - if cmd.Flags().Changed(SelectedNetworksFlag) { - selectedNetworks = &oc.SelectedNetworks - } - if selectedNetworks != nil { - tc.Network = &ctfconfig.NetworkConfig{ - SelectedNetworks: oc.SelectedNetworks, - } - } - - var peryscopeEnabled *bool - var pyroscopeServerURL, pyroscopeEnvironment, pyroscopeKey *string - if cmd.Flags().Changed(PyroscopeEnabledFlag) { - peryscopeEnabled = &oc.PyroscopeEnabled - } - if cmd.Flags().Changed(PyroscopeServerURLFlag) { - pyroscopeServerURL = &oc.PyroscopeServerURL - } - if cmd.Flags().Changed(PyroscopeKeyFlag) { - pyroscopeKey = &oc.PyroscopeKey - } - if cmd.Flags().Changed(PyroscopeEnvironmentFlag) { - pyroscopeEnvironment = &oc.PyroscopeEnvironment - } - if peryscopeEnabled != nil { - tc.Pyroscope = &ctfconfig.PyroscopeConfig{ - Enabled: peryscopeEnabled, - ServerUrl: pyroscopeServerURL, - Environment: pyroscopeEnvironment, - Key: pyroscopeKey, - } - } - - var testLogCollect *bool - if cmd.Flags().Changed(LoggingTestLogCollectFlag) { - testLogCollect = &oc.LoggingTestLogCollect - } - var loggingRunID *string - if cmd.Flags().Changed(LoggingRunIDFlag) { - loggingRunID = &oc.LoggingRunID - } - var loggingLogTargets []string - if cmd.Flags().Changed(LoggingLogTargetsFlag) { - loggingLogTargets = oc.LoggingLogTargets - } - var loggingLokiTenantID *string - if cmd.Flags().Changed(LoggingLokiTenantIDFlag) { - loggingLokiTenantID = &oc.LoggingLokiTenantID - } - var loggingLokiBasicAuth *string - if cmd.Flags().Changed(LoggingLokiBasicAuthFlag) { - loggingLokiBasicAuth = &oc.LoggingLokiBasicAuth - } - var loggingLokiEndpoint *string - if cmd.Flags().Changed(LoggingLokiEndpointFlag) { - loggingLokiEndpoint = &oc.LoggingLokiEndpoint - } - var loggingGrafanaBaseURL *string - if cmd.Flags().Changed(LoggingGrafanaBaseURLFlag) { - loggingGrafanaBaseURL = &oc.LoggingGrafanaBaseURL - } - var loggingGrafanaDashboardURL *string - if cmd.Flags().Changed(LoggingGrafanaDashboardURLFlag) { - loggingGrafanaDashboardURL = &oc.LoggingGrafanaDashboardURL - } - var loggingGrafanaBearerToken *string - if cmd.Flags().Changed(LoggingGrafanaBearerTokenFlag) { - loggingGrafanaBearerToken = &oc.LoggingGrafanaBearerToken - } - - if testLogCollect != nil || loggingRunID != nil || loggingLogTargets != nil || loggingLokiEndpoint != nil || loggingLokiTenantID != nil || loggingLokiBasicAuth != nil || loggingGrafanaBaseURL != nil || loggingGrafanaDashboardURL != nil || loggingGrafanaBearerToken != nil { - tc.Logging = &ctfconfig.LoggingConfig{} - tc.Logging.TestLogCollect = testLogCollect - tc.Logging.RunId = loggingRunID - if loggingLogTargets != nil { - tc.Logging.LogStream = &ctfconfig.LogStreamConfig{ - LogTargets: loggingLogTargets, - } - } - if loggingLokiTenantID != nil || loggingLokiBasicAuth != nil || loggingLokiEndpoint != nil { - tc.Logging.Loki = &ctfconfig.LokiConfig{ - TenantId: loggingLokiTenantID, - BasicAuth: loggingLokiBasicAuth, - Endpoint: loggingLokiEndpoint, - } - } - if loggingGrafanaBaseURL != nil || loggingGrafanaDashboardURL != nil || loggingGrafanaBearerToken != nil { - tc.Logging.Grafana = &ctfconfig.GrafanaConfig{ - BaseUrl: loggingGrafanaBaseURL, - DashboardUrl: loggingGrafanaDashboardURL, - BearerToken: loggingGrafanaBearerToken, - } - } - } - - var privateEthereumNetworkExecutionLayer *string - if cmd.Flags().Changed(PrivateEthereumNetworkExecutionLayerFlag) { - privateEthereumNetworkExecutionLayer = &oc.PrivateEthereumNetworkExecutionLayer - } - var privateEthereumNetworkEthereumVersion *string - if cmd.Flags().Changed(PrivateEthereumNetworkEthereumVersionFlag) { - privateEthereumNetworkEthereumVersion = &oc.PrivateEthereumNetworkEthereumVersion - } - var privateEthereumNetworkCustomDockerImage *string - if cmd.Flags().Changed(PrivateEthereumNetworkCustomDockerImageFlag) { - privateEthereumNetworkCustomDockerImage = &oc.PrivateEthereumNetworkCustomDockerImages - } - if privateEthereumNetworkExecutionLayer != nil || privateEthereumNetworkEthereumVersion != nil || privateEthereumNetworkCustomDockerImage != nil { - var el ctfconfigtypes.ExecutionLayer - if privateEthereumNetworkExecutionLayer != nil { - el = ctfconfigtypes.ExecutionLayer(*privateEthereumNetworkExecutionLayer) - } - var ev ctfconfigtypes.EthereumVersion - if privateEthereumNetworkEthereumVersion != nil { - ev = ctfconfigtypes.EthereumVersion(*privateEthereumNetworkEthereumVersion) - } - var customImages map[ctfconfig.ContainerType]string - if privateEthereumNetworkCustomDockerImage != nil { - customImages = map[ctfconfig.ContainerType]string{"execution_layer": *privateEthereumNetworkCustomDockerImage} - } - tc.PrivateEthereumNetwork = &ctfconfig.EthereumNetworkConfig{ - ExecutionLayer: &el, - EthereumVersion: &ev, - CustomDockerImages: customImages, - } - } - - configToml, err := toml.Marshal(tc) - if err != nil { - fmt.Fprintf(os.Stderr, "Error marshalling TestConfig to TOML: %v\n", err) - os.Exit(1) - } - - fmt.Fprintln(cmd.OutOrStdout(), string(configToml)) - }, -} diff --git a/integration-tests/citool/cmd/csv_export_cmd.go b/integration-tests/citool/cmd/csv_export_cmd.go deleted file mode 100644 index 8fe13440c8..0000000000 --- a/integration-tests/citool/cmd/csv_export_cmd.go +++ /dev/null @@ -1,96 +0,0 @@ -package cmd - -import ( - "encoding/csv" - "fmt" - "os" - "strings" - - "github.com/spf13/cobra" - "gopkg.in/yaml.v3" -) - -var csvExportCmd = &cobra.Command{ - Use: "csvexport", - Short: "Export tests to CSV format", - Run: func(cmd *cobra.Command, _ []string) { - configFile, _ := cmd.Flags().GetString("file") - if err := exportConfigToCSV(configFile); err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) - os.Exit(1) - } - }, -} - -func init() { - csvExportCmd.Flags().StringP("file", "f", "", "Path to YML file") - err := csvExportCmd.MarkFlagRequired("file") - if err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) - os.Exit(1) - } -} - -func exportConfigToCSV(configFile string) error { - // Read the YAML file - bytes, err := os.ReadFile(configFile) - if err != nil { - return err - } - - // Unmarshal the YAML into the Config struct - var config Config - if err := yaml.Unmarshal(bytes, &config); err != nil { - return err - } - - // Create a CSV file - file, err := os.Create("output.csv") - if err != nil { - return err - } - defer file.Close() - - writer := csv.NewWriter(file) - defer writer.Flush() - - // Write CSV headers - headers := []string{"ID", "Test Path", "Test Env Type", "Runs On", "Test Cmd", "Test Config Override Required", "Test Secrets Required", "Remote Runner Memory", "Pyroscope Env", "Workflows", "Test Inputs"} - if err := writer.Write(headers); err != nil { - return err - } - - // Iterate over Tests and write data to CSV - for _, test := range config.Tests { - workflows := strings.Join(test.Workflows, ", ") // Combine workflows into a single CSV field - // Serialize TestInputs - testInputs := serializeMap(test.TestInputs) - - record := []string{ - test.ID, - test.Path, - test.TestEnvType, - test.RunsOn, - test.TestCmd, - fmt.Sprintf("%t", test.TestConfigOverrideRequired), - fmt.Sprintf("%t", test.TestSecretsRequired), - test.RemoteRunnerMemory, - test.PyroscopeEnv, - workflows, - testInputs, - } - if err := writer.Write(record); err != nil { - return err - } - } - - return nil -} - -func serializeMap(inputs map[string]string) string { - pairs := make([]string, 0, len(inputs)) - for key, value := range inputs { - pairs = append(pairs, fmt.Sprintf("%s=%s", key, value)) - } - return strings.Join(pairs, ", ") -} diff --git a/integration-tests/citool/cmd/filter_cmd.go b/integration-tests/citool/cmd/filter_cmd.go deleted file mode 100644 index 5d1d41cb9d..0000000000 --- a/integration-tests/citool/cmd/filter_cmd.go +++ /dev/null @@ -1,165 +0,0 @@ -package cmd - -import ( - "encoding/base64" - "encoding/json" - "fmt" - "log" - "os" - "regexp" - "strings" - - "github.com/spf13/cobra" - "gopkg.in/yaml.v2" - - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils" -) - -// Filter tests based on workflow, test type, and test IDs. -func filterTests(allTests []CITestConf, workflow, testType, ids string, envresolve bool) []CITestConf { - workflowFilter := workflow - typeFilter := testType - idFilter := strings.Split(ids, ",") - - var filteredTests []CITestConf - - for _, test := range allTests { - workflowMatch := workflow == "" || contains(test.Workflows, workflowFilter) - typeMatch := testType == "" || test.TestEnvType == typeFilter - idMatch := ids == "*" || ids == "" || contains(idFilter, test.ID) - - if workflowMatch && typeMatch && idMatch { - test.IDSanitized = sanitizeTestID(test.ID) - filteredTests = append(filteredTests, test) - } - if envresolve { - for k, v := range test.TestInputs { - test.TestInputs[k] = utils.MustResolveEnvPlaceholder(v) - } - } - } - - return filteredTests -} - -func filterAndMergeTests(allTests []CITestConf, workflow, testType, base64Tests string, envresolve bool) ([]CITestConf, error) { - decodedBytes, err := base64.StdEncoding.DecodeString(base64Tests) - if err != nil { - return nil, err - } - var decodedTests []CITestConf - err = yaml.Unmarshal(decodedBytes, &decodedTests) - if err != nil { - return nil, err - } - - idFilter := make(map[string]CITestConf) - for _, dt := range decodedTests { - idFilter[dt.ID] = dt - } - - var filteredTests []CITestConf - for _, test := range allTests { - workflowMatch := workflow == "" || contains(test.Workflows, workflow) - typeMatch := testType == "" || test.TestEnvType == testType - - if decodedTest, exists := idFilter[test.ID]; exists && workflowMatch && typeMatch { - // Override test inputs from the base64 encoded tests - for k, v := range decodedTest.TestInputs { - if test.TestInputs == nil { - test.TestInputs = make(map[string]string) - } - test.TestInputs[k] = v - } - test.IDSanitized = sanitizeTestID(test.ID) - filteredTests = append(filteredTests, test) - } - if envresolve { - for k, v := range test.TestInputs { - test.TestInputs[k] = utils.MustResolveEnvPlaceholder(v) - } - } - } - - return filteredTests, nil -} - -func sanitizeTestID(id string) string { - // Define a regular expression that matches any character not a letter, digit, hyphen - re := regexp.MustCompile(`[^a-zA-Z0-9-_]+`) - // Replace all occurrences of disallowed characters with "_" - return re.ReplaceAllString(id, "_") -} - -// Utility function to check if a slice contains a string. -func contains(slice []string, element string) bool { - for _, s := range slice { - if s == element { - return true - } - } - return false -} - -// filterCmd represents the filter command -var filterCmd = &cobra.Command{ - Use: "filter", - Short: "Filter test configurations based on specified criteria", - Long: `Filters tests from a YAML configuration based on name, workflow, test type, and test IDs. -Example usage: -./e2e_tests_tool filter --file .github/e2e-tests.yml --workflow "Run Nightly E2E Tests" --test-env-type "docker" --test-ids "test1,test2"`, - Run: func(cmd *cobra.Command, _ []string) { - yamlFile, _ := cmd.Flags().GetString("file") - workflow, _ := cmd.Flags().GetString("workflow") - testType, _ := cmd.Flags().GetString("test-env-type") - testIDs, _ := cmd.Flags().GetString("test-ids") - testMap, _ := cmd.Flags().GetString("test-list") - envresolve, _ := cmd.Flags().GetBool("envresolve") - - data, err := os.ReadFile(yamlFile) - if err != nil { - fmt.Fprintf(os.Stderr, "Error reading YAML file: %v\n", err) - os.Exit(1) - } - - var config Config - err = yaml.Unmarshal(data, &config) - if err != nil { - fmt.Fprintf(os.Stderr, "Error parsing YAML file %s data: %v\n", yamlFile, err) - os.Exit(1) - } - - var filteredTests []CITestConf - if testMap == "" { - filteredTests = filterTests(config.Tests, workflow, testType, testIDs, envresolve) - } else { - filteredTests, err = filterAndMergeTests(config.Tests, workflow, testType, testMap, envresolve) - if err != nil { - log.Fatalf("Error filtering and merging tests: %v", err) - } - } - matrix := map[string][]CITestConf{"tests": filteredTests} - matrixJSON, err := json.Marshal(matrix) - if err != nil { - fmt.Fprintf(os.Stderr, "Error marshaling matrix to JSON: %v\n", err) - os.Exit(1) - } - - fmt.Printf("%s", matrixJSON) - }, -} - -func init() { - filterCmd.Flags().StringP("file", "f", "", "Path to the YAML file") - filterCmd.Flags().String("test-list", "", "Base64 encoded list of tests (YML objects) to filter by. Can include test_inputs for each test.") - filterCmd.Flags().StringP("test-ids", "i", "*", "Comma-separated list of test IDs to filter by") - filterCmd.Flags().StringP("test-env-type", "y", "", "Type of test to filter by") - filterCmd.Flags().StringP("workflow", "t", "", "Workflow filter") - filterCmd.Flags().Bool("envresolve", false, "Resolve environment variables in test inputs") - - err := filterCmd.MarkFlagRequired("file") - if err != nil { - fmt.Fprintf(os.Stderr, "Error marking flag as required: %v\n", err) - os.Exit(1) - } -} diff --git a/integration-tests/citool/cmd/filter_cmd_test.go b/integration-tests/citool/cmd/filter_cmd_test.go deleted file mode 100644 index ff6e9c981d..0000000000 --- a/integration-tests/citool/cmd/filter_cmd_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package cmd - -import ( - "testing" -) - -func TestFilterTestsByID(t *testing.T) { - tests := []CITestConf{ - {ID: "run_all_in_ocr_tests_go", TestEnvType: "docker"}, - {ID: "run_all_in_ocr2_tests_go", TestEnvType: "docker"}, - {ID: "run_all_in_ocr3_tests_go", TestEnvType: "k8s_remote_runner"}, - } - - cases := []struct { - description string - inputIDs string - expectedLen int - }{ - {"Filter by single ID", "run_all_in_ocr_tests_go", 1}, - {"Filter by multiple IDs", "run_all_in_ocr_tests_go,run_all_in_ocr2_tests_go", 2}, - {"Wildcard to include all", "*", 3}, - {"Empty ID string to include all", "", 3}, - } - - for _, c := range cases { - t.Run(c.description, func(t *testing.T) { - filtered := filterTests(tests, "", "", c.inputIDs, false) - if len(filtered) != c.expectedLen { - t.Errorf("FilterTests(%s) returned %d tests, expected %d", c.description, len(filtered), c.expectedLen) - } - }) - } -} - -func TestFilterTestsIntegration(t *testing.T) { - tests := []CITestConf{ - {ID: "run_all_in_ocr_tests_go", TestEnvType: "docker", Workflows: []string{"Run Nightly E2E Tests"}}, - {ID: "run_all_in_ocr2_tests_go", TestEnvType: "docker", Workflows: []string{"Run PR E2E Tests"}}, - {ID: "run_all_in_ocr3_tests_go", TestEnvType: "k8s_remote_runner", Workflows: []string{"Run PR E2E Tests"}}, - } - - cases := []struct { - description string - inputNames string - inputWorkflow string - inputTestType string - inputIDs string - expectedLen int - }{ - {"Filter by test type and ID", "", "", "docker", "run_all_in_ocr2_tests_go", 1}, - {"Filter by trigger and test type", "", "Run PR E2E Tests", "docker", "*", 1}, - {"No filters applied", "", "", "", "*", 3}, - {"Filter mismatching all criteria", "", "Run Nightly E2E Tests", "", "", 1}, - } - - for _, c := range cases { - t.Run(c.description, func(t *testing.T) { - filtered := filterTests(tests, c.inputWorkflow, c.inputTestType, c.inputIDs, false) - if len(filtered) != c.expectedLen { - t.Errorf("FilterTests(%s) returned %d tests, expected %d", c.description, len(filtered), c.expectedLen) - } - }) - } -} diff --git a/integration-tests/citool/cmd/root_cmd.go b/integration-tests/citool/cmd/root_cmd.go deleted file mode 100644 index fdb4efd572..0000000000 --- a/integration-tests/citool/cmd/root_cmd.go +++ /dev/null @@ -1,32 +0,0 @@ -package cmd - -import ( - "os" - - "github.com/spf13/cobra" -) - -// rootCmd represents the base command when called without any subcommands -var rootCmd = &cobra.Command{ - Use: "citool", - Short: "A tool to manage E2E tests on Github CI", -} - -// Execute adds all child commands to the root command and sets flags appropriately. -// This is called by main.main(). It only needs to happen once to the rootCmd. -func Execute() { - err := rootCmd.Execute() - if err != nil { - os.Exit(1) - } -} - -func init() { - rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") - - rootCmd.AddCommand(checkTestsCmd) - rootCmd.AddCommand(filterCmd) - rootCmd.AddCommand(csvExportCmd) - rootCmd.AddCommand(testConfigCmd) - testConfigCmd.AddCommand(createTestConfigCmd) -} diff --git a/integration-tests/citool/cmd/test_config_cmd.go b/integration-tests/citool/cmd/test_config_cmd.go deleted file mode 100644 index 3d0766ca0c..0000000000 --- a/integration-tests/citool/cmd/test_config_cmd.go +++ /dev/null @@ -1,123 +0,0 @@ -package cmd - -import ( - "strings" - - "github.com/spf13/cobra" - - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils" -) - -var testConfigCmd = &cobra.Command{ - Use: "test-config", - Short: "Manage test config", -} - -// OverrideConfig holds the configuration data for overrides -type OverrideConfig struct { - ChainlinkImage string - ChainlinkVersion string - ChainlinkUpgradeImage string - ChainlinkUpgradeVersion string - ChainlinkPostgresVersion string - SelectedNetworks []string - PyroscopeEnabled bool - PyroscopeServerURL string - PyroscopeEnvironment string - PyroscopeKey string - LoggingTestLogCollect bool - LoggingRunID string - LoggingLogTargets []string - LoggingLokiTenantID string - LoggingLokiEndpoint string - LoggingLokiBasicAuth string - LoggingGrafanaBaseURL string - LoggingGrafanaDashboardURL string - LoggingGrafanaBearerToken string - PrivateEthereumNetworkExecutionLayer string - PrivateEthereumNetworkEthereumVersion string - PrivateEthereumNetworkCustomDockerImages string -} - -const ( - ChainlinkVersionFlag = "chainlink-version" - ChainlinkUpgradeVersionFlag = "chainlink-upgrade-version" - ChainlinkPostgresVersionFlag = "chainlink-postgres-version" - SelectedNetworksFlag = "selected-networks" - FromBase64ConfigFlag = "from-base64-config" - LoggingLokiBasicAuthFlag = "logging-loki-basic-auth" - LoggingLokiEndpointFlag = "logging-loki-endpoint" - LoggingRunIDFlag = "logging-run-id" - LoggingLokiTenantIDFlag = "logging-loki-tenant-id" - LoggingGrafanaBaseURLFlag = "logging-grafana-base-url" - LoggingGrafanaDashboardURLFlag = "logging-grafana-dashboard-url" - LoggingGrafanaBearerTokenFlag = "logging-grafana-bearer-token" - LoggingLogTargetsFlag = "logging-log-targets" - LoggingTestLogCollectFlag = "logging-test-log-collect" - PyroscopeEnabledFlag = "pyroscope-enabled" - PyroscopeServerURLFlag = "pyroscope-server-url" - PyroscopeKeyFlag = "pyroscope-key" - PyroscopeEnvironmentFlag = "pyroscope-environment" - PrivateEthereumNetworkExecutionLayerFlag = "private-ethereum-network-execution-layer" - PrivateEthereumNetworkEthereumVersionFlag = "private-ethereum-network-ethereum-version" - PrivateEthereumNetworkCustomDockerImageFlag = "private-ethereum-network-custom-docker-image" -) - -var oc OverrideConfig - -func init() { - cmds := []*cobra.Command{createTestConfigCmd} - for _, c := range cmds { - c.Flags().StringArrayVar(&oc.SelectedNetworks, SelectedNetworksFlag, nil, "Selected networks") - c.Flags().StringVar(&oc.ChainlinkVersion, ChainlinkVersionFlag, "", "Chainlink version") - c.Flags().StringVar(&oc.ChainlinkUpgradeVersion, ChainlinkUpgradeVersionFlag, "", "Chainlink upgrade version") - c.Flags().StringVar(&oc.ChainlinkPostgresVersion, ChainlinkPostgresVersionFlag, "", "Chainlink Postgres version") - c.Flags().BoolVar(&oc.PyroscopeEnabled, PyroscopeEnabledFlag, false, "Pyroscope enabled") - c.Flags().StringVar(&oc.PyroscopeServerURL, PyroscopeServerURLFlag, "", "Pyroscope server URL") - c.Flags().StringVar(&oc.PyroscopeKey, PyroscopeKeyFlag, "", "Pyroscope key") - c.Flags().StringVar(&oc.PyroscopeEnvironment, PyroscopeEnvironmentFlag, "", "Pyroscope environment") - c.Flags().BoolVar(&oc.LoggingTestLogCollect, LoggingTestLogCollectFlag, false, "Test log collect") - c.Flags().StringVar(&oc.LoggingRunID, LoggingRunIDFlag, "", "Run ID") - c.Flags().StringArrayVar(&oc.LoggingLogTargets, LoggingLogTargetsFlag, nil, "Logging.LogStream.LogTargets") - c.Flags().StringVar(&oc.LoggingLokiEndpoint, LoggingLokiEndpointFlag, "", "") - c.Flags().StringVar(&oc.LoggingLokiTenantID, LoggingLokiTenantIDFlag, "", "") - c.Flags().StringVar(&oc.LoggingLokiBasicAuth, LoggingLokiBasicAuthFlag, "", "") - c.Flags().StringVar(&oc.LoggingGrafanaBaseURL, LoggingGrafanaBaseURLFlag, "", "") - c.Flags().StringVar(&oc.LoggingGrafanaDashboardURL, LoggingGrafanaDashboardURLFlag, "", "") - c.Flags().StringVar(&oc.LoggingGrafanaBearerToken, LoggingGrafanaBearerTokenFlag, "", "") - c.Flags().StringVar(&oc.PrivateEthereumNetworkExecutionLayer, PrivateEthereumNetworkExecutionLayerFlag, "", "") - c.Flags().StringVar(&oc.PrivateEthereumNetworkEthereumVersion, PrivateEthereumNetworkEthereumVersionFlag, "", "") - c.Flags().StringVar(&oc.PrivateEthereumNetworkCustomDockerImages, PrivateEthereumNetworkCustomDockerImageFlag, "", "") - - c.PreRun = func(_ *cobra.Command, _ []string) { - // Resolve selected networks environment variable if set - if len(oc.SelectedNetworks) > 0 { - _, hasEnvVar := utils.LookupEnvVarName(oc.SelectedNetworks[0]) - if hasEnvVar { - selectedNetworks := utils.MustResolveEnvPlaceholder(oc.SelectedNetworks[0]) - oc.SelectedNetworks = strings.Split(selectedNetworks, ",") - } - } - - // Resolve all other environment variables - oc.ChainlinkImage = utils.MustResolveEnvPlaceholder(oc.ChainlinkImage) - oc.ChainlinkVersion = utils.MustResolveEnvPlaceholder(oc.ChainlinkVersion) - oc.ChainlinkUpgradeImage = utils.MustResolveEnvPlaceholder(oc.ChainlinkUpgradeImage) - oc.ChainlinkUpgradeVersion = utils.MustResolveEnvPlaceholder(oc.ChainlinkUpgradeVersion) - oc.ChainlinkPostgresVersion = utils.MustResolveEnvPlaceholder(oc.ChainlinkPostgresVersion) - oc.PyroscopeServerURL = utils.MustResolveEnvPlaceholder(oc.PyroscopeServerURL) - oc.PyroscopeKey = utils.MustResolveEnvPlaceholder(oc.PyroscopeKey) - oc.PyroscopeEnvironment = utils.MustResolveEnvPlaceholder(oc.PyroscopeEnvironment) - oc.LoggingRunID = utils.MustResolveEnvPlaceholder(oc.LoggingRunID) - oc.LoggingLokiTenantID = utils.MustResolveEnvPlaceholder(oc.LoggingLokiTenantID) - oc.LoggingLokiEndpoint = utils.MustResolveEnvPlaceholder(oc.LoggingLokiEndpoint) - oc.LoggingLokiBasicAuth = utils.MustResolveEnvPlaceholder(oc.LoggingLokiBasicAuth) - oc.LoggingGrafanaBaseURL = utils.MustResolveEnvPlaceholder(oc.LoggingGrafanaBaseURL) - oc.LoggingGrafanaDashboardURL = utils.MustResolveEnvPlaceholder(oc.LoggingGrafanaDashboardURL) - oc.LoggingGrafanaBearerToken = utils.MustResolveEnvPlaceholder(oc.LoggingGrafanaBearerToken) - oc.PrivateEthereumNetworkExecutionLayer = utils.MustResolveEnvPlaceholder(oc.PrivateEthereumNetworkExecutionLayer) - oc.PrivateEthereumNetworkEthereumVersion = utils.MustResolveEnvPlaceholder(oc.PrivateEthereumNetworkEthereumVersion) - oc.PrivateEthereumNetworkCustomDockerImages = utils.MustResolveEnvPlaceholder(oc.PrivateEthereumNetworkCustomDockerImages) - } - } -} diff --git a/integration-tests/citool/cmd/test_config_cmd_test.go b/integration-tests/citool/cmd/test_config_cmd_test.go deleted file mode 100644 index 4e710d06f6..0000000000 --- a/integration-tests/citool/cmd/test_config_cmd_test.go +++ /dev/null @@ -1,75 +0,0 @@ -package cmd - -import ( - "bytes" - "testing" - - "github.com/pelletier/go-toml/v2" - "github.com/spf13/cobra" - "github.com/stretchr/testify/assert" - - ctf_config "github.com/smartcontractkit/chainlink-testing-framework/lib/config" - ctfconfigtypes "github.com/smartcontractkit/chainlink-testing-framework/lib/config/types" -) - -func TestCreateTestConfigCmd(t *testing.T) { - tests := []struct { - name string - args []string - want interface{} - check func(t *testing.T, tc *ctf_config.TestConfig) - wantErr bool - }{ - { - name: "LoggingLogTargets", - args: []string{"create", "--logging-log-targets=target1", "--logging-log-targets=target2"}, - check: func(t *testing.T, tc *ctf_config.TestConfig) { - assert.NotNil(t, tc.Logging) - assert.NotNil(t, tc.Logging.LogStream) - assert.Equal(t, []string{"target1", "target2"}, tc.Logging.LogStream.LogTargets) - }, - }, - { - name: "PrivateEthereumNetworkExecutionLayerFlag", - args: []string{"create", "--private-ethereum-network-execution-layer=geth", "--private-ethereum-network-ethereum-version=1.10.0"}, - check: func(t *testing.T, tc *ctf_config.TestConfig) { - assert.NotNil(t, tc.PrivateEthereumNetwork) - assert.NotNil(t, tc.PrivateEthereumNetwork.ExecutionLayer) - assert.Equal(t, ctfconfigtypes.ExecutionLayer("geth"), *tc.PrivateEthereumNetwork.ExecutionLayer) - assert.Equal(t, ctfconfigtypes.EthereumVersion("1.10.0"), *tc.PrivateEthereumNetwork.EthereumVersion) - }, - }, - { - name: "PrivateEthereumNetworkCustomDockerImageFlag", - args: []string{"create", "--private-ethereum-network-execution-layer=geth", "--private-ethereum-network-ethereum-version=1.10.0", "--private-ethereum-network-custom-docker-image=custom-image:v1.0"}, - check: func(t *testing.T, tc *ctf_config.TestConfig) { - assert.NotNil(t, tc.PrivateEthereumNetwork) - assert.NotNil(t, tc.PrivateEthereumNetwork.ExecutionLayer) - assert.Equal(t, map[ctf_config.ContainerType]string{"execution_layer": "custom-image:v1.0"}, tc.PrivateEthereumNetwork.CustomDockerImages) - }, - }, - } - - rootCmd := &cobra.Command{} - rootCmd.AddCommand(createTestConfigCmd) - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - rootCmd.SetArgs(tt.args) - var out bytes.Buffer - rootCmd.SetOutput(&out) - err := rootCmd.Execute() - if (err != nil) != tt.wantErr { - t.Fatalf("Execute() error = %v, wantErr %v", err, tt.wantErr) - } - var tc ctf_config.TestConfig - err = toml.Unmarshal(out.Bytes(), &tc) - if err != nil { - t.Fatalf("Failed to unmarshal output: %v", err) - } - if tt.check != nil { - tt.check(t, &tc) - } - }) - } -} diff --git a/integration-tests/citool/cmd/types.go b/integration-tests/citool/cmd/types.go deleted file mode 100644 index 3c347e9406..0000000000 --- a/integration-tests/citool/cmd/types.go +++ /dev/null @@ -1,26 +0,0 @@ -package cmd - -type Test struct { - Name string - Path string -} - -// CITestConf defines the configuration for running a test in a CI environment, specifying details like test ID, path, type, runner settings, command, and associated workflows. -type CITestConf struct { - ID string `yaml:"id" json:"id"` - IDSanitized string `json:"id_sanitized"` - Path string `yaml:"path" json:"path"` - TestEnvType string `yaml:"test_env_type" json:"test_env_type"` - RunsOn string `yaml:"runs_on" json:"runs_on"` - TestCmd string `yaml:"test_cmd" json:"test_cmd"` - TestConfigOverrideRequired bool `yaml:"test_config_override_required" json:"testConfigOverrideRequired"` - TestSecretsRequired bool `yaml:"test_secrets_required" json:"testSecretsRequired"` - TestInputs map[string]string `yaml:"test_inputs" json:"test_inputs"` - RemoteRunnerMemory string `yaml:"remote_runner_memory" json:"remoteRunnerMemory"` - PyroscopeEnv string `yaml:"pyroscope_env" json:"pyroscopeEnv"` - Workflows []string `yaml:"workflows" json:"workflows"` -} - -type Config struct { - Tests []CITestConf `yaml:"runner-test-matrix"` -} diff --git a/integration-tests/citool/main.go b/integration-tests/citool/main.go deleted file mode 100644 index 4fa6cac56e..0000000000 --- a/integration-tests/citool/main.go +++ /dev/null @@ -1,9 +0,0 @@ -package main - -import ( - "github.com/smartcontractkit/chainlink/integration-tests/citool/cmd" -) - -func main() { - cmd.Execute() -} diff --git a/integration-tests/client/chainlink.go b/integration-tests/client/chainlink.go index 5d9b6a3d08..fbee59e4ce 100644 --- a/integration-tests/client/chainlink.go +++ b/integration-tests/client/chainlink.go @@ -2,6 +2,7 @@ package client import ( + "crypto/tls" "fmt" "math/big" "net/http" @@ -45,14 +46,10 @@ type ChainlinkClient struct { // NewChainlinkClient creates a new Chainlink model using a provided config func NewChainlinkClient(c *ChainlinkConfig, logger zerolog.Logger) (*ChainlinkClient, error) { - rc, err := initRestyClient(c.URL, c.Email, c.Password, c.HTTPTimeout) + rc, err := initRestyClient(c.URL, c.Email, c.Password, c.Headers, c.HTTPTimeout) if err != nil { return nil, err } - _, isSet := os.LookupEnv("CL_CLIENT_DEBUG") - if isSet { - rc.SetDebug(true) - } return &ChainlinkClient{ Config: c, APIClient: rc, @@ -61,8 +58,11 @@ func NewChainlinkClient(c *ChainlinkConfig, logger zerolog.Logger) (*ChainlinkCl }, nil } -func initRestyClient(url string, email string, password string, timeout *time.Duration) (*resty.Client, error) { - rc := resty.New().SetBaseURL(url) +func initRestyClient(url string, email string, password string, headers map[string]string, timeout *time.Duration) (*resty.Client, error) { + isDebug := os.Getenv("RESTY_DEBUG") == "true" + // G402 - TODO: certificates + //nolint + rc := resty.New().SetBaseURL(url).SetHeaders(headers).SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}).SetDebug(isDebug) if timeout != nil { rc.SetTimeout(*timeout) } @@ -74,7 +74,7 @@ func initRestyClient(url string, email string, password string, timeout *time.Du for i := 0; i < retryCount; i++ { resp, err = rc.R().SetBody(session).Post("/sessions") if err != nil { - log.Debug().Err(err).Str("URL", url).Interface("Session Details", session).Msg("Error connecting to Chainlink node, retrying") + log.Warn().Err(err).Str("URL", url).Interface("Session Details", session).Msg("Error connecting to Chainlink node, retrying") time.Sleep(5 * time.Second) } else { break diff --git a/integration-tests/client/chainlink_k8s.go b/integration-tests/client/chainlink_k8s.go index 969e060ca1..4cdfa96c53 100644 --- a/integration-tests/client/chainlink_k8s.go +++ b/integration-tests/client/chainlink_k8s.go @@ -2,7 +2,6 @@ package client import ( - "os" "regexp" "github.com/rs/zerolog/log" @@ -23,14 +22,10 @@ type ChainlinkK8sClient struct { // NewChainlink creates a new Chainlink model using a provided config func NewChainlinkK8sClient(c *ChainlinkConfig, podName, chartName string) (*ChainlinkK8sClient, error) { - rc, err := initRestyClient(c.URL, c.Email, c.Password, c.HTTPTimeout) + rc, err := initRestyClient(c.URL, c.Email, c.Password, c.Headers, c.HTTPTimeout) if err != nil { return nil, err } - _, isSet := os.LookupEnv("CL_CLIENT_DEBUG") - if isSet { - rc.SetDebug(true) - } return &ChainlinkK8sClient{ ChainlinkClient: &ChainlinkClient{ APIClient: rc, diff --git a/integration-tests/client/chainlink_models.go b/integration-tests/client/chainlink_models.go index a0435d5336..86e9f75902 100644 --- a/integration-tests/client/chainlink_models.go +++ b/integration-tests/client/chainlink_models.go @@ -20,11 +20,12 @@ type EIServiceConfig struct { // ChainlinkConfig represents the variables needed to connect to a Chainlink node type ChainlinkConfig struct { - URL string `toml:",omitempty"` - Email string `toml:",omitempty"` - Password string `toml:",omitempty"` - InternalIP string `toml:",omitempty"` - HTTPTimeout *time.Duration `toml:"-"` + URL string `toml:",omitempty"` + Email string `toml:",omitempty"` + Password string `toml:",omitempty"` + InternalIP string `toml:",omitempty"` + Headers map[string]string `toml:",omitempty"` + HTTPTimeout *time.Duration `toml:"-"` } // ResponseSlice is the generic model that can be used for all Chainlink API responses that are an slice diff --git a/integration-tests/contracts/contract_models.go b/integration-tests/contracts/contract_models.go index 853ce8e86b..01dcbb553b 100644 --- a/integration-tests/contracts/contract_models.go +++ b/integration-tests/contracts/contract_models.go @@ -226,7 +226,7 @@ type JobByInstance struct { Instance string } -type MockETHLINKFeed interface { +type MockLINKETHFeed interface { Address() string LatestRoundData() (*big.Int, error) LatestRoundDataUpdatedAt() (*big.Int, error) diff --git a/integration-tests/contracts/contract_vrf_models.go b/integration-tests/contracts/contract_vrf_models.go index 3aca32a66f..db8b427b07 100644 --- a/integration-tests/contracts/contract_vrf_models.go +++ b/integration-tests/contracts/contract_vrf_models.go @@ -76,6 +76,8 @@ type VRFCoordinatorV2 interface { WaitForConfigSetEvent(timeout time.Duration) (*CoordinatorConfigSet, error) OracleWithdraw(recipient common.Address, amount *big.Int) error GetBlockHashStoreAddress(ctx context.Context) (common.Address, error) + GetLinkAddress(ctx context.Context) (common.Address, error) + GetLinkNativeFeed(ctx context.Context) (common.Address, error) } type VRFCoordinatorV2_5 interface { @@ -121,6 +123,8 @@ type VRFCoordinatorV2_5 interface { ParseRandomWordsFulfilled(log types.Log) (*CoordinatorRandomWordsFulfilled, error) WaitForConfigSetEvent(timeout time.Duration) (*CoordinatorConfigSet, error) GetBlockHashStoreAddress(ctx context.Context) (common.Address, error) + GetLinkAddress(ctx context.Context) (common.Address, error) + GetLinkNativeFeed(ctx context.Context) (common.Address, error) } type VRFCoordinatorV2PlusUpgradedVersion interface { diff --git a/integration-tests/contracts/ethereum_contracts.go b/integration-tests/contracts/ethereum_contracts.go index 9ce118b556..c77b5e9bd8 100644 --- a/integration-tests/contracts/ethereum_contracts.go +++ b/integration-tests/contracts/ethereum_contracts.go @@ -14,6 +14,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/rs/zerolog" "github.com/rs/zerolog/log" + "github.com/smartcontractkit/libocr/gethwrappers/offchainaggregator" "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" ocrConfigHelper "github.com/smartcontractkit/libocr/offchainreporting/confighelper" @@ -771,12 +772,12 @@ func DeployLinkTokenContract(l zerolog.Logger, client *seth.Client) (*EthereumLi } func LoadLinkTokenContract(l zerolog.Logger, client *seth.Client, address common.Address) (*EthereumLinkToken, error) { - abi, err := link_token_interface.LinkTokenMetaData.GetAbi() + linkABI, err := link_token_interface.LinkTokenMetaData.GetAbi() if err != nil { return &EthereumLinkToken{}, fmt.Errorf("failed to get LinkToken ABI: %w", err) } - client.ContractStore.AddABI("LinkToken", *abi) + client.ContractStore.AddABI("LinkToken", *linkABI) client.ContractStore.AddBIN("LinkToken", common.FromHex(link_token_interface.LinkTokenMetaData.Bin)) linkToken, err := link_token_interface.NewLinkToken(address, wrappers.MustNewWrappedContractBackend(nil, client)) @@ -1196,19 +1197,19 @@ func (v *EthereumMockETHLINKFeed) LatestRoundDataUpdatedAt() (*big.Int, error) { return data.UpdatedAt, nil } -func DeployMockETHLINKFeed(client *seth.Client, answer *big.Int) (MockETHLINKFeed, error) { +func DeployMockLINKETHFeed(client *seth.Client, answer *big.Int) (MockLINKETHFeed, error) { abi, err := mock_ethlink_aggregator_wrapper.MockETHLINKAggregatorMetaData.GetAbi() if err != nil { - return &EthereumMockETHLINKFeed{}, fmt.Errorf("failed to get MockETHLINKFeed ABI: %w", err) + return &EthereumMockETHLINKFeed{}, fmt.Errorf("failed to get MockLINKETHFeed ABI: %w", err) } - data, err := client.DeployContract(client.NewTXOpts(), "MockETHLINKFeed", *abi, common.FromHex(mock_ethlink_aggregator_wrapper.MockETHLINKAggregatorMetaData.Bin), answer) + data, err := client.DeployContract(client.NewTXOpts(), "MockLINKETHFeed", *abi, common.FromHex(mock_ethlink_aggregator_wrapper.MockETHLINKAggregatorMetaData.Bin), answer) if err != nil { - return &EthereumMockETHLINKFeed{}, fmt.Errorf("MockETHLINKFeed instance deployment have failed: %w", err) + return &EthereumMockETHLINKFeed{}, fmt.Errorf("MockLINKETHFeed instance deployment have failed: %w", err) } instance, err := mock_ethlink_aggregator_wrapper.NewMockETHLINKAggregator(data.Address, wrappers.MustNewWrappedContractBackend(nil, client)) if err != nil { - return &EthereumMockETHLINKFeed{}, fmt.Errorf("failed to instantiate MockETHLINKFeed instance: %w", err) + return &EthereumMockETHLINKFeed{}, fmt.Errorf("failed to instantiate MockLINKETHFeed instance: %w", err) } return &EthereumMockETHLINKFeed{ @@ -1218,17 +1219,17 @@ func DeployMockETHLINKFeed(client *seth.Client, answer *big.Int) (MockETHLINKFee }, nil } -func LoadMockETHLINKFeed(client *seth.Client, address common.Address) (MockETHLINKFeed, error) { +func LoadMockLINKETHFeed(client *seth.Client, address common.Address) (MockLINKETHFeed, error) { abi, err := mock_ethlink_aggregator_wrapper.MockETHLINKAggregatorMetaData.GetAbi() if err != nil { - return &EthereumMockETHLINKFeed{}, fmt.Errorf("failed to get MockETHLINKFeed ABI: %w", err) + return &EthereumMockETHLINKFeed{}, fmt.Errorf("failed to get MockLINKETHFeed ABI: %w", err) } - client.ContractStore.AddABI("MockETHLINKFeed", *abi) - client.ContractStore.AddBIN("MockETHLINKFeed", common.FromHex(mock_ethlink_aggregator_wrapper.MockETHLINKAggregatorMetaData.Bin)) + client.ContractStore.AddABI("MockLINKETHFeed", *abi) + client.ContractStore.AddBIN("MockLINKETHFeed", common.FromHex(mock_ethlink_aggregator_wrapper.MockETHLINKAggregatorMetaData.Bin)) instance, err := mock_ethlink_aggregator_wrapper.NewMockETHLINKAggregator(address, wrappers.MustNewWrappedContractBackend(nil, client)) if err != nil { - return &EthereumMockETHLINKFeed{}, fmt.Errorf("failed to instantiate MockETHLINKFeed instance: %w", err) + return &EthereumMockETHLINKFeed{}, fmt.Errorf("failed to instantiate MockLINKETHFeed instance: %w", err) } return &EthereumMockETHLINKFeed{ diff --git a/integration-tests/contracts/ethereum_contracts_automation.go b/integration-tests/contracts/ethereum_contracts_automation.go index 02132b1a12..d9d4a730c3 100644 --- a/integration-tests/contracts/ethereum_contracts_automation.go +++ b/integration-tests/contracts/ethereum_contracts_automation.go @@ -148,7 +148,7 @@ type EthereumKeeperRegistry struct { func (v *EthereumKeeperRegistry) ReorgProtectionEnabled() bool { chainId := v.client.ChainID // 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) + return v.version < ethereum.RegistryVersion_2_2 || (chainId != 1101 && chainId != 1442 && chainId != 2442 && chainId != 534352 && chainId != 534351) } func (v *EthereumKeeperRegistry) ChainModuleAddress() common.Address { @@ -1369,12 +1369,7 @@ func deployRegistry22(client *seth.Client, opts *KeeperRegistryOpts) (KeeperRegi return nil, err } - var allowedReadOnlyAddress common.Address - if chainId == networks.PolygonZkEvmMainnet.ChainID || chainId == networks.PolygonZkEvmCardona.ChainID { - allowedReadOnlyAddress = common.HexToAddress("0x1111111111111111111111111111111111111111") - } else { - allowedReadOnlyAddress = common.HexToAddress("0x0000000000000000000000000000000000000000") - } + allowedReadOnlyAddress := common.HexToAddress("0x0000000000000000000000000000000000000000") logicBAbi, err := registrylogicb22.AutomationRegistryLogicBMetaData.GetAbi() if err != nil { @@ -1457,12 +1452,7 @@ func deployRegistry23(client *seth.Client, opts *KeeperRegistryOpts) (KeeperRegi return nil, err } - var allowedReadOnlyAddress common.Address - if chainId == networks.PolygonZkEvmMainnet.ChainID || chainId == networks.PolygonZkEvmCardona.ChainID { - allowedReadOnlyAddress = common.HexToAddress("0x1111111111111111111111111111111111111111") - } else { - allowedReadOnlyAddress = common.HexToAddress("0x0000000000000000000000000000000000000000") - } + allowedReadOnlyAddress := common.HexToAddress("0x0000000000000000000000000000000000000000") logicCAbi, err := registrylogicc23.AutomationRegistryLogicCMetaData.GetAbi() if err != nil { diff --git a/integration-tests/contracts/ethereum_keeper_contracts.go b/integration-tests/contracts/ethereum_keeper_contracts.go index 28fdd15b13..38aa5c58f0 100644 --- a/integration-tests/contracts/ethereum_keeper_contracts.go +++ b/integration-tests/contracts/ethereum_keeper_contracts.go @@ -150,6 +150,7 @@ type KeeperRegistrySettings struct { MaxPerformGas uint32 // max gas allowed for an upkeep within perform FallbackGasPrice *big.Int // gas price used if the gas price feed is stale FallbackLinkPrice *big.Int // LINK price used if the LINK price feed is stale + FallbackNativePrice *big.Int // Native price used if the Native price feed is stale MaxCheckDataSize uint32 MaxPerformDataSize uint32 MaxRevertDataSize uint32 diff --git a/integration-tests/contracts/ethereum_vrf_contracts.go b/integration-tests/contracts/ethereum_vrf_contracts.go index 71da9baa36..c47f8bec00 100644 --- a/integration-tests/contracts/ethereum_vrf_contracts.go +++ b/integration-tests/contracts/ethereum_vrf_contracts.go @@ -13,6 +13,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2_5" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_load_test_with_metrics" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2plus_wrapper" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2plus_wrapper_load_test_consumer" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2plus_wrapper_optimism" "github.com/smartcontractkit/chainlink-testing-framework/seth" @@ -546,3 +547,22 @@ func LoadVRFV2PlusWrapperOptimism(seth *seth.Client, addr string) (*EthereumVRFV wrapper: contract, }, nil } + +func LoadVRFV2WrapperLoadTestConsumer(seth *seth.Client, addr string) (*EthereumVRFV2PlusWrapperLoadTestConsumer, error) { + address := common.HexToAddress(addr) + abi, err := vrfv2plus_wrapper_load_test_consumer.VRFV2PlusWrapperLoadTestConsumerMetaData.GetAbi() + if err != nil { + return nil, fmt.Errorf("failed to get VRFV2PlusWrapperLoadTestConsumer ABI: %w", err) + } + seth.ContractStore.AddABI("VRFV2PlusWrapperLoadTestConsumer", *abi) + seth.ContractStore.AddBIN("VRFV2PlusWrapperLoadTestConsumer", common.FromHex(vrfv2plus_wrapper_load_test_consumer.VRFV2PlusWrapperLoadTestConsumerMetaData.Bin)) + contract, err := vrfv2plus_wrapper_load_test_consumer.NewVRFV2PlusWrapperLoadTestConsumer(address, wrappers.MustNewWrappedContractBackend(nil, seth)) + if err != nil { + return nil, fmt.Errorf("failed to instantiate VRFV2PlusWrapperLoadTestConsumer instance: %w", err) + } + return &EthereumVRFV2PlusWrapperLoadTestConsumer{ + client: seth, + address: address, + consumer: contract, + }, nil +} diff --git a/integration-tests/contracts/ethereum_vrfv2_contracts.go b/integration-tests/contracts/ethereum_vrfv2_contracts.go index aa9237ea7c..708fdb211e 100644 --- a/integration-tests/contracts/ethereum_vrfv2_contracts.go +++ b/integration-tests/contracts/ethereum_vrfv2_contracts.go @@ -594,6 +594,30 @@ func (v *EthereumVRFCoordinatorV2) ParseLog(log types.Log) (generated.AbigenLog, return v.coordinator.ParseLog(log) } +func (v *EthereumVRFCoordinatorV2) GetLinkAddress(ctx context.Context) (common.Address, error) { + opts := &bind.CallOpts{ + From: v.client.MustGetRootKeyAddress(), + Context: ctx, + } + address, err := v.coordinator.LINK(opts) + if err != nil { + return common.Address{}, err + } + return address, nil +} + +func (v *EthereumVRFCoordinatorV2) GetLinkNativeFeed(ctx context.Context) (common.Address, error) { + opts := &bind.CallOpts{ + From: v.client.MustGetRootKeyAddress(), + Context: ctx, + } + address, err := v.coordinator.LINKETHFEED(opts) + if err != nil { + return common.Address{}, err + } + return address, nil +} + // CancelSubscription cancels subscription by Sub owner, // return funds to specified address, // checks if pending requests for a sub exist diff --git a/integration-tests/contracts/ethereum_vrfv2plus_contracts.go b/integration-tests/contracts/ethereum_vrfv2plus_contracts.go index 58044b36c6..2741209114 100644 --- a/integration-tests/contracts/ethereum_vrfv2plus_contracts.go +++ b/integration-tests/contracts/ethereum_vrfv2plus_contracts.go @@ -16,11 +16,14 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/wrappers" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_blockhash_store" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_vrf_coordinator_v2plus" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_test_v2_5" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2_5" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2_5_arbitrum" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2_5_optimism" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_load_test_with_metrics" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_upgraded_version" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2plus_wrapper" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2plus_wrapper_arbitrum" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2plus_wrapper_load_test_consumer" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2plus_wrapper_optimism" ) @@ -37,6 +40,18 @@ type EthereumVRFCoordinatorV2_5_Optimism struct { coordinator vrf_coordinator_v2_5_optimism.VRFCoordinatorV25Optimism } +type EthereumVRFCoordinatorV2_5_Arbitrum struct { + Address common.Address + client *seth.Client + coordinator vrf_coordinator_v2_5_arbitrum.VRFCoordinatorV25Arbitrum +} + +type EthereumVRFCoordinatorTestV2_5 struct { + Address common.Address + client *seth.Client + coordinator vrf_coordinator_test_v2_5.VRFCoordinatorTestV25 +} + type EthereumBatchVRFCoordinatorV2Plus struct { address common.Address client *seth.Client @@ -74,6 +89,12 @@ type EthereumVRFV2PlusWrapperOptimism struct { wrapper *vrfv2plus_wrapper_optimism.VRFV2PlusWrapperOptimism } +type EthereumVRFV2PlusWrapperArbitrum struct { + Address common.Address + client *seth.Client + wrapper *vrfv2plus_wrapper_arbitrum.VRFV2PlusWrapperArbitrum +} + func (v *EthereumVRFV2PlusWrapper) Address() string { return v.address.Hex() } @@ -178,6 +199,56 @@ func DeployVRFCoordinatorV2_5_Optimism(seth *seth.Client, bhsAddr string) (*Ethe }, err } +func DeployVRFCoordinatorV2_5_Arbitrum(seth *seth.Client, bhsAddr string) (*EthereumVRFCoordinatorV2_5_Arbitrum, error) { + abi, err := vrf_coordinator_v2_5_arbitrum.VRFCoordinatorV25ArbitrumMetaData.GetAbi() + if err != nil { + return nil, fmt.Errorf("failed to get VRFCoordinatorV2_5_Arbitrum ABI: %w", err) + } + coordinatorDeploymentData, err := seth.DeployContract( + seth.NewTXOpts(), + "VRFCoordinatorV2_5_Arbitrum", + *abi, + common.FromHex(vrf_coordinator_v2_5_arbitrum.VRFCoordinatorV25ArbitrumMetaData.Bin), + common.HexToAddress(bhsAddr)) + if err != nil { + return nil, fmt.Errorf("VRFCoordinatorV2_5_Arbitrum instance deployment have failed: %w", err) + } + contract, err := vrf_coordinator_v2_5_arbitrum.NewVRFCoordinatorV25Arbitrum(coordinatorDeploymentData.Address, wrappers.MustNewWrappedContractBackend(nil, seth)) + if err != nil { + return nil, fmt.Errorf("failed to instantiate VRFCoordinatorV2_5_Arbitrum instance: %w", err) + } + return &EthereumVRFCoordinatorV2_5_Arbitrum{ + client: seth, + coordinator: *contract, + Address: coordinatorDeploymentData.Address, + }, err +} + +func DeployVRFCoordinatorTestV2_5(seth *seth.Client, bhsAddr string) (*EthereumVRFCoordinatorTestV2_5, error) { + abi, err := vrf_coordinator_test_v2_5.VRFCoordinatorTestV25MetaData.GetAbi() + if err != nil { + return nil, fmt.Errorf("failed to get VRFCoordinatorTestV2_5 ABI: %w", err) + } + coordinatorDeploymentData, err := seth.DeployContract( + seth.NewTXOpts(), + "VRFCoordinatorTestV2_5", + *abi, + common.FromHex(vrf_coordinator_test_v2_5.VRFCoordinatorTestV25MetaData.Bin), + common.HexToAddress(bhsAddr)) + if err != nil { + return nil, fmt.Errorf("VRFCoordinatorTestV2_5 instance deployment have failed: %w", err) + } + contract, err := vrf_coordinator_test_v2_5.NewVRFCoordinatorTestV25(coordinatorDeploymentData.Address, wrappers.MustNewWrappedContractBackend(nil, seth)) + if err != nil { + return nil, fmt.Errorf("failed to instantiate VRFCoordinatorTestV2_5 instance: %w", err) + } + return &EthereumVRFCoordinatorTestV2_5{ + client: seth, + coordinator: *contract, + Address: coordinatorDeploymentData.Address, + }, err +} + func DeployBatchVRFCoordinatorV2Plus(seth *seth.Client, coordinatorAddress string) (BatchVRFCoordinatorV2Plus, error) { abi, err := batch_vrf_coordinator_v2plus.BatchVRFCoordinatorV2PlusMetaData.GetAbi() if err != nil { @@ -340,6 +411,30 @@ func (v *EthereumVRFCoordinatorV2_5) GetBlockHashStoreAddress(ctx context.Contex return blockHashStoreAddress, nil } +func (v *EthereumVRFCoordinatorV2_5) GetLinkAddress(ctx context.Context) (common.Address, error) { + opts := &bind.CallOpts{ + From: v.client.MustGetRootKeyAddress(), + Context: ctx, + } + address, err := v.coordinator.LINK(opts) + if err != nil { + return common.Address{}, err + } + return address, nil +} + +func (v *EthereumVRFCoordinatorV2_5) GetLinkNativeFeed(ctx context.Context) (common.Address, error) { + opts := &bind.CallOpts{ + From: v.client.MustGetRootKeyAddress(), + Context: ctx, + } + address, err := v.coordinator.LINKNATIVEFEED(opts) + if err != nil { + return common.Address{}, err + } + return address, nil +} + // OwnerCancelSubscription cancels subscription by Coordinator owner // return funds to sub owner, // does not check if pending requests for a sub exist @@ -1161,24 +1256,50 @@ func DeployVRFV2PlusWrapper(seth *seth.Client, linkAddr string, linkEthFeedAddr }, err } +func DeployVRFV2PlusWrapperArbitrum(seth *seth.Client, linkAddr string, linkEthFeedAddr string, coordinatorAddr string, subId *big.Int) (*EthereumVRFV2PlusWrapperArbitrum, error) { + abi, err := vrfv2plus_wrapper_arbitrum.VRFV2PlusWrapperArbitrumMetaData.GetAbi() + if err != nil { + return nil, fmt.Errorf("failed to get VRFV2PlusWrapper_Arbitrum ABI: %w", err) + } + data, err := seth.DeployContract( + seth.NewTXOpts(), + "VRFV2PlusWrapper_Arbitrum", + *abi, + common.FromHex(vrfv2plus_wrapper_arbitrum.VRFV2PlusWrapperArbitrumMetaData.Bin), + common.HexToAddress(linkAddr), common.HexToAddress(linkEthFeedAddr), + common.HexToAddress(coordinatorAddr), subId) + if err != nil { + return nil, fmt.Errorf("VRFV2PlusWrapper_Arbitrum instance deployment have failed: %w", err) + } + contract, err := vrfv2plus_wrapper_arbitrum.NewVRFV2PlusWrapperArbitrum(data.Address, wrappers.MustNewWrappedContractBackend(nil, seth)) + if err != nil { + return nil, fmt.Errorf("failed to instantiate VRFV2PlusWrapper_Arbitrum instance: %w", err) + } + return &EthereumVRFV2PlusWrapperArbitrum{ + client: seth, + wrapper: contract, + Address: data.Address, + }, err +} + func DeployVRFV2PlusWrapperOptimism(seth *seth.Client, linkAddr string, linkEthFeedAddr string, coordinatorAddr string, subId *big.Int) (*EthereumVRFV2PlusWrapperOptimism, error) { abi, err := vrfv2plus_wrapper_optimism.VRFV2PlusWrapperOptimismMetaData.GetAbi() if err != nil { - return nil, fmt.Errorf("failed to get VRFV2PlusWrapperOptimism ABI: %w", err) + return nil, fmt.Errorf("failed to get VRFV2PlusWrapper_Optimism ABI: %w", err) } data, err := seth.DeployContract( seth.NewTXOpts(), - "VRFV2PlusWrapperOptimism", + "VRFV2PlusWrapper_Optimism", *abi, common.FromHex(vrfv2plus_wrapper_optimism.VRFV2PlusWrapperOptimismMetaData.Bin), common.HexToAddress(linkAddr), common.HexToAddress(linkEthFeedAddr), common.HexToAddress(coordinatorAddr), subId) if err != nil { - return nil, fmt.Errorf("VRFV2PlusWrapperOptimism instance deployment have failed: %w", err) + return nil, fmt.Errorf("VRFV2PlusWrapper_Optimism instance deployment have failed: %w", err) } contract, err := vrfv2plus_wrapper_optimism.NewVRFV2PlusWrapperOptimism(data.Address, wrappers.MustNewWrappedContractBackend(nil, seth)) if err != nil { - return nil, fmt.Errorf("failed to instantiate VRFV2PlusWrapperOptimism instance: %w", err) + return nil, fmt.Errorf("failed to instantiate VRFV2PlusWrapper_Optimism instance: %w", err) } return &EthereumVRFV2PlusWrapperOptimism{ client: seth, diff --git a/integration-tests/crib/README.md b/integration-tests/crib/README.md index ecf393f780..e895cca676 100644 --- a/integration-tests/crib/README.md +++ b/integration-tests/crib/README.md @@ -1,4 +1,4 @@ -### CRIB Health Check Test +### Example e2e product test using CRIB ## Setup CRIB This is a simple smoke + chaos test for CRIB deployment. @@ -12,8 +12,15 @@ devspace deploy --debug --profile local-dev-simulated-core-ocr1 ## Run the tests ```shell -CRIB_NAMESPACE=crib-oh-my-crib -CRIB_NETWORK=geth # only "geth" is supported for now -CRIB_NODES=5 # min 5 nodes +export CRIB_NAMESPACE=crib-oh-my-crib +export CRIB_NETWORK=geth # only "geth" is supported for now +export CRIB_NODES=5 # min 5 nodes +#export SETH_LOG_LEVEL=debug # these two can be enabled to debug connection issues +#export RESTY_DEBUG=true +#export TEST_PERSISTENCE=true # to run the chaos test +export GAP_URL=https://localhost:8080/primary # only applicable in CI, unset the var to connect locally go test -v -run TestCRIB -``` \ No newline at end of file +``` + +## Configuring CI workflow +We are using GAP and GATI to access the infrastructure, please follow [configuration guide](https://smartcontract-it.atlassian.net/wiki/spaces/CRIB/pages/909967436/CRIB+CI+Integration) \ No newline at end of file diff --git a/integration-tests/crib/chaos.go b/integration-tests/crib/chaos.go index 985ca2dc2d..745237e67b 100644 --- a/integration-tests/crib/chaos.go +++ b/integration-tests/crib/chaos.go @@ -7,6 +7,8 @@ import ( "github.com/chaos-mesh/chaos-mesh/api/v1alpha1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/smartcontractkit/chainlink-testing-framework/havoc" ) func rebootCLNamespace(delay time.Duration, namespace string) (*havoc.Chaos, error) { diff --git a/integration-tests/crib/connect.go b/integration-tests/crib/connect.go index 3289ed52c4..ae62d31a0e 100644 --- a/integration-tests/crib/connect.go +++ b/integration-tests/crib/connect.go @@ -1,35 +1,25 @@ package crib import ( - "fmt" - "os" - "strconv" + "net/http" "time" + "github.com/smartcontractkit/chainlink-testing-framework/crib" + "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-testing-framework/seth" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/ptr" - seth_utils "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/seth" + "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" + seth_utils "github.com/smartcontractkit/chainlink-testing-framework/utils/seth" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" - "github.com/smartcontractkit/chainlink-testing-framework/lib/blockchain" - msClient "github.com/smartcontractkit/chainlink-testing-framework/lib/client" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + msClient "github.com/smartcontractkit/chainlink-testing-framework/client" "github.com/smartcontractkit/chainlink/integration-tests/client" ) -const ( - // these are constants for simulated CRIB that should never change - // CRIB: https://github.com/smartcontractkit/crib/tree/main/core - // Core Chart: https://github.com/smartcontractkit/infra-charts/tree/main/chainlink-cluster - mockserverCRIBTemplate = "https://%s-mockserver%s" - internalNodeDNSTemplate = "app-node%d" - ingressNetworkWSURLTemplate = "wss://%s-geth-1337-ws%s" - ingressNetworkHTTPURLTemplate = "https://%s-geth-1337-http%s" -) - -func setSethConfig(cfg tc.TestConfig, netWSURL string, netHTTPURL string) { +func setSethConfig(cfg tc.TestConfig, netWSURL string, netHTTPURL string, headers http.Header) { netName := "CRIB_SIMULATED" cfg.Network.SelectedNetworks = []string{netName} cfg.Network.RpcHttpUrls = map[string][]string{} @@ -37,6 +27,7 @@ func setSethConfig(cfg tc.TestConfig, netWSURL string, netHTTPURL string) { cfg.Network.RpcWsUrls = map[string][]string{} cfg.Network.RpcWsUrls[netName] = []string{netWSURL} cfg.Seth.EphemeralAddrs = ptr.Ptr(int64(0)) + cfg.Seth.RPCHeaders = headers } // ConnectRemote connects to a local environment, see https://github.com/smartcontractkit/crib/tree/main/core @@ -48,52 +39,33 @@ func ConnectRemote() ( []*client.ChainlinkK8sClient, error, ) { - ingressSuffix := os.Getenv("K8S_STAGING_INGRESS_SUFFIX") - if ingressSuffix == "" { - return nil, nil, nil, nil, errors.New("K8S_STAGING_INGRESS_SUFFIX must be set to connect to k8s ingresses") - } - cribNamespace := os.Getenv("CRIB_NAMESPACE") - if cribNamespace == "" { - return nil, nil, nil, nil, errors.New("CRIB_NAMESPACE must be set to connect") - } - cribNetwork := os.Getenv("CRIB_NETWORK") - if cribNetwork == "" { - return nil, nil, nil, nil, errors.New("CRIB_NETWORK must be set to connect, only 'geth' is supported for now") - } - cribNodes := os.Getenv("CRIB_NODES") - nodes, err := strconv.Atoi(cribNodes) + vars, err := crib.CoreDONSimulatedConnection() if err != nil { - return nil, nil, nil, nil, errors.New("CRIB_NODES must be a number, 5-19 nodes") + return nil, nil, nil, nil, err } + // TODO: move all the parts of ConnectRemote() to CTF when Seth config refactor is finalized config, err := tc.GetConfig([]string{"CRIB"}, tc.OCR) if err != nil { return nil, nil, nil, nil, err } - if nodes < 2 { - return nil, nil, nil, nil, fmt.Errorf("not enough chainlink nodes, need at least 2, TOML key: [CRIB.nodes]") - } - mockserverURL := fmt.Sprintf(mockserverCRIBTemplate, cribNamespace, ingressSuffix) var sethClient *seth.Client - switch cribNetwork { + switch vars.Network { case "geth": - netWSURL := fmt.Sprintf(ingressNetworkWSURLTemplate, cribNamespace, ingressSuffix) - netHTTPURL := fmt.Sprintf(ingressNetworkHTTPURLTemplate, cribNamespace, ingressSuffix) - setSethConfig(config, netWSURL, netHTTPURL) + setSethConfig(config, vars.NetworkWSURL, vars.NetworkHTTPURL, vars.BlockchainNodeHeaders) net := blockchain.EVMNetwork{ - Name: cribNetwork, - Simulated: true, - SupportsEIP1559: true, - ClientImplementation: blockchain.EthereumClientImplementation, - ChainID: 1337, - PrivateKeys: []string{ - "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", - }, - URLs: []string{netWSURL}, - HTTPURLs: []string{netHTTPURL}, + Name: vars.Network, + Simulated: true, + SupportsEIP1559: true, + ClientImplementation: blockchain.EthereumClientImplementation, + ChainID: vars.ChainID, + PrivateKeys: vars.PrivateKeys, + URLs: []string{vars.NetworkWSURL}, + HTTPURLs: []string{vars.NetworkHTTPURL}, ChainlinkTransactionLimit: 500000, Timeout: blockchain.StrDuration{Duration: 2 * time.Minute}, MinimumConfirmations: 1, GasEstimationBuffer: 10000, + Headers: vars.BlockchainNodeHeaders, } sethClient, err = seth_utils.GetChainClient(config, net) if err != nil { @@ -105,31 +77,34 @@ func ConnectRemote() ( // bootstrap node clClients := make([]*client.ChainlinkK8sClient, 0) c, err := client.NewChainlinkK8sClient(&client.ChainlinkConfig{ - URL: fmt.Sprintf("https://%s-node%d%s", cribNamespace, 1, ingressSuffix), Email: client.CLNodeTestEmail, - InternalIP: fmt.Sprintf(internalNodeDNSTemplate, 1), Password: client.CLNodeTestPassword, - }, fmt.Sprintf(internalNodeDNSTemplate, 1), cribNamespace) + URL: vars.NodeURLs[0], + InternalIP: vars.NodeInternalDNS[0], + Headers: vars.NodeHeaders[0], + }, vars.NodeInternalDNS[0], vars.Namespace) if err != nil { return nil, nil, nil, nil, err } clClients = append(clClients, c) // all the other nodes, indices of nodes in CRIB starts with 1 - for i := 2; i <= nodes; i++ { + for i := 1; i < vars.Nodes; i++ { cl, err := client.NewChainlinkK8sClient(&client.ChainlinkConfig{ - URL: fmt.Sprintf("https://%s-node%d%s", cribNamespace, i, ingressSuffix), Email: client.CLNodeTestEmail, - InternalIP: fmt.Sprintf(internalNodeDNSTemplate, i), Password: client.CLNodeTestPassword, - }, fmt.Sprintf(internalNodeDNSTemplate, i), cribNamespace) + URL: vars.NodeURLs[i], + InternalIP: vars.NodeInternalDNS[i], + Headers: vars.NodeHeaders[i], + }, vars.NodeInternalDNS[i], vars.Namespace) if err != nil { return nil, nil, nil, nil, err } clClients = append(clClients, cl) } mockServerClient := msClient.NewMockserverClient(&msClient.MockserverConfig{ - LocalURL: mockserverURL, - ClusterURL: mockserverURL, + LocalURL: vars.MockserverURL, + ClusterURL: "http://mockserver:1080", + Headers: vars.MockserverHeaders, }) //nolint:gosec // G602 - false positive https://github.com/securego/gosec/issues/1005 diff --git a/integration-tests/crib/ocr_test.go b/integration-tests/crib/ocr_test.go index 327283c150..0b7c38597c 100644 --- a/integration-tests/crib/ocr_test.go +++ b/integration-tests/crib/ocr_test.go @@ -6,14 +6,14 @@ import ( "testing" "time" - "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-testing-framework/havoc" + "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/contracts" - "github.com/smartcontractkit/chainlink-testing-framework/lib/logging" + "github.com/smartcontractkit/chainlink-testing-framework/logging" ) func TestCRIB(t *testing.T) { @@ -34,22 +34,24 @@ func TestCRIB(t *testing.T) { err = actions.WatchNewOCRRound(l, sethClient, 1, contracts.V1OffChainAgrregatorToOffChainAggregatorWithRounds(ocrInstances), 5*time.Minute) require.NoError(t, err, "Error watching for new OCR round") - ch, err := rebootCLNamespace( - 1*time.Second, - os.Getenv("CRIB_NAMESPACE"), - ) - ch.Create(context.Background()) - ch.AddListener(havoc.NewChaosLogger(l)) - t.Cleanup(func() { - err := ch.Delete(context.Background()) - require.NoError(t, err) - }) - require.Eventually(t, func() bool { - err = actions.WatchNewOCRRound(l, sethClient, 3, contracts.V1OffChainAgrregatorToOffChainAggregatorWithRounds(ocrInstances), 5*time.Minute) - if err != nil { - l.Info().Err(err).Msg("OCR round is not there yet") - return false - } - return true - }, 3*time.Minute, 5*time.Second) + if os.Getenv("TEST_PERSISTENCE") != "" { + ch, err := rebootCLNamespace( + 1*time.Second, + os.Getenv("CRIB_NAMESPACE"), + ) + ch.Create(context.Background()) + ch.AddListener(havoc.NewChaosLogger(l)) + t.Cleanup(func() { + err := ch.Delete(context.Background()) + require.NoError(t, err) + }) + require.Eventually(t, func() bool { + err = actions.WatchNewOCRRound(l, sethClient, 3, contracts.V1OffChainAgrregatorToOffChainAggregatorWithRounds(ocrInstances), 5*time.Minute) + if err != nil { + l.Info().Err(err).Msg("OCR round is not there yet") + return false + } + return true + }, 20*time.Minute, 5*time.Second) + } } diff --git a/integration-tests/docker/test_env/test_env_builder.go b/integration-tests/docker/test_env/test_env_builder.go index 249bb8c39a..3540e0e877 100644 --- a/integration-tests/docker/test_env/test_env_builder.go +++ b/integration-tests/docker/test_env/test_env_builder.go @@ -3,26 +3,29 @@ package test_env import ( "fmt" "os" + "path/filepath" "slices" "strings" "testing" + "time" "github.com/rs/zerolog" "github.com/rs/zerolog/log" - "go.uber.org/zap/zapcore" - "github.com/smartcontractkit/chainlink-testing-framework/lib/blockchain" - ctf_config "github.com/smartcontractkit/chainlink-testing-framework/lib/config" - "github.com/smartcontractkit/chainlink-testing-framework/lib/docker/test_env" - "github.com/smartcontractkit/chainlink-testing-framework/lib/logging" - "github.com/smartcontractkit/chainlink-testing-framework/lib/logstream" - "github.com/smartcontractkit/chainlink-testing-framework/lib/networks" - "github.com/smartcontractkit/chainlink-testing-framework/lib/testreporters" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/osutil" - "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - + "github.com/smartcontractkit/chainlink-testing-framework/seth" + + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + ctf_config "github.com/smartcontractkit/chainlink-testing-framework/config" + "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/logstream" + "github.com/smartcontractkit/chainlink-testing-framework/networks" + "github.com/smartcontractkit/chainlink-testing-framework/testreporters" + "github.com/smartcontractkit/chainlink-testing-framework/testsummary" + "github.com/smartcontractkit/chainlink-testing-framework/utils/osutil" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" + "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" ) type CleanUpType string @@ -230,7 +233,6 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { return nil, err } } - b.te.TestConfig = b.testConfig b.te.TestConfig = b.testConfig @@ -256,7 +258,7 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { b.t.Cleanup(func() { b.l.Info().Msg("Shutting down LogStream") logPath, err := osutil.GetAbsoluteFolderPath("logs") - if err != nil { + if err == nil { b.l.Info().Str("Absolute path", logPath).Msg("LogStream logs folder location") } @@ -281,7 +283,8 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { // new logs can be added to the log stream, so parallel processing would get stuck on waiting for it to be unlocked LogScanningLoop: for i := 0; i < b.clNodesCount; i++ { - if b == nil || b.te == nil || b.te.ClCluster == nil || b.te.ClCluster.Nodes == nil || b.te.ClCluster.Nodes[i] == nil || len(b.te.ClCluster.Nodes)-1 < i { + // if something went wrong during environment setup we might not have all nodes, and we don't want an NPE + if b == nil || b.te == nil || b.te.ClCluster == nil || b.te.ClCluster.Nodes == nil || len(b.te.ClCluster.Nodes)-1 < i || b.te.ClCluster.Nodes[i] == nil { continue } // ignore count return, because we are only interested in the error @@ -308,6 +311,47 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { b.te.LogStream.SaveLogLocationInTestSummary() } b.l.Info().Msg("Finished shutting down LogStream") + + if b.t.Failed() || *b.testConfig.GetLoggingConfig().TestLogCollect { + b.l.Info().Msg("Dump state of all Postgres DBs used by Chainlink Nodes") + + dbDumpFolder := "db_dumps" + dbDumpPath := fmt.Sprintf("%s/%s-%s", dbDumpFolder, b.t.Name(), time.Now().Format("2006-01-02T15-04-05")) + if err := os.MkdirAll(dbDumpPath, os.ModePerm); err != nil { + b.l.Error().Err(err).Msg("Error creating folder for Postgres DB dump") + return + } + + absDbDumpPath, err := osutil.GetAbsoluteFolderPath(dbDumpFolder) + if err == nil { + b.l.Info().Str("Absolute path", absDbDumpPath).Msg("PostgresDB dump folder location") + } + + for i := 0; i < b.clNodesCount; i++ { + // if something went wrong during environment setup we might not have all nodes, and we don't want an NPE + if b == nil || b.te == nil || b.te.ClCluster == nil || b.te.ClCluster.Nodes == nil || len(b.te.ClCluster.Nodes)-1 < i || b.te.ClCluster.Nodes[i] == nil || b.te.ClCluster.Nodes[i].PostgresDb == nil { + continue + } + + filePath := filepath.Join(dbDumpPath, fmt.Sprintf("postgres_db_dump_%s.sql", b.te.ClCluster.Nodes[i].ContainerName)) + localDbDumpFile, err := os.Create(filePath) + if err != nil { + b.l.Error().Err(err).Msg("Error creating localDbDumpFile for Postgres DB dump") + _ = localDbDumpFile.Close() + continue + } + + if err := b.te.ClCluster.Nodes[i].PostgresDb.ExecPgDumpFromContainer(localDbDumpFile); err != nil { + b.l.Error().Err(err).Msg("Error dumping Postgres DB") + } + _ = localDbDumpFile.Close() + } + b.l.Info().Msg("Finished dumping state of all Postgres DBs used by Chainlink Nodes") + } + + if b.testConfig.GetSethConfig() != nil && ((b.t.Failed() && slices.Contains(b.testConfig.GetSethConfig().TraceOutputs, seth.TraceOutput_DOT) && b.testConfig.GetSethConfig().TracingLevel != seth.TracingLevel_None) || (!b.t.Failed() && slices.Contains(b.testConfig.GetSethConfig().TraceOutputs, seth.TraceOutput_DOT) && b.testConfig.GetSethConfig().TracingLevel == seth.TracingLevel_All)) { + _ = testsummary.AddEntry(b.t.Name(), "dot_graphs", "true") + } }) } else { b.l.Warn().Msg("LogStream won't be cleaned up, because either test instance is not set or cleanup type is set to none") diff --git a/integration-tests/go.mod b/integration-tests/go.mod index f11f609258..25bea041d0 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -34,7 +34,8 @@ require ( github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chain-selectors v1.0.24 github.com/smartcontractkit/chainlink-automation v1.0.4 - github.com/smartcontractkit/chainlink-common v0.2.2-0.20240723123524-e407ecd120b1 + github.com/smartcontractkit/chainlink-common v0.2.2-0.20240828121637-da5837469949 + github.com/smartcontractkit/chainlink-testing-framework v1.34.10-0.20240828122712-9ea5d6ac33fe github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.0 github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.5 github.com/smartcontractkit/chainlink-testing-framework/lib/grafana v1.50.0 @@ -52,12 +53,10 @@ require ( go.uber.org/multierr v1.11.0 go.uber.org/zap v1.27.0 golang.org/x/crypto v0.27.0 - golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 + golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa golang.org/x/sync v0.8.0 golang.org/x/text v0.18.0 gopkg.in/guregu/null.v4 v4.0.0 - gopkg.in/yaml.v2 v2.4.0 - gopkg.in/yaml.v3 v3.0.1 k8s.io/apimachinery v0.31.0 ) @@ -268,7 +267,7 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/gtank/merlin v0.1.1 // indirect github.com/gtank/ristretto255 v0.1.2 // indirect @@ -401,9 +400,9 @@ require ( github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240710121324-3ed288aa9b45 // indirect - github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240718160222-2dc0c8136bfa // indirect + github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240820130645-cf4b159fbba2 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20240710170203-5b41615da827 // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240712132946-267a37c5ac6e // indirect + github.com/smartcontractkit/chainlink-solana v1.1.1-0.20240821170223-a2f5c39f457f // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240709043547-03612098f799 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 // indirect @@ -468,24 +467,26 @@ require ( go.uber.org/ratelimit v0.3.0 // indirect go4.org/netipx v0.0.0-20230125063823-8449b0a6169f // indirect golang.org/x/arch v0.8.0 // indirect - golang.org/x/mod v0.19.0 // indirect + golang.org/x/mod v0.20.0 // indirect golang.org/x/net v0.29.0 // indirect - golang.org/x/oauth2 v0.21.0 // indirect + golang.org/x/oauth2 v0.22.0 // indirect golang.org/x/sys v0.25.0 // indirect golang.org/x/term v0.24.0 // indirect golang.org/x/time v0.6.0 // indirect - golang.org/x/tools v0.23.0 // indirect + golang.org/x/tools v0.24.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect gonum.org/v1/gonum v0.15.0 // indirect google.golang.org/genproto v0.0.0-20240711142825-46eb208f015d // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd // indirect google.golang.org/grpc v1.65.0 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.31.0 // indirect k8s.io/apiextensions-apiserver v0.31.0 // indirect k8s.io/cli-runtime v0.31.0 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 91d2f9cdf1..c14dbfee3d 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -851,8 +851,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= @@ -1417,18 +1417,20 @@ github.com/smartcontractkit/chain-selectors v1.0.24 h1:day+2l0vBErgVIs2Y9ifSz+T7 github.com/smartcontractkit/chain-selectors v1.0.24/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.4 h1:iyW181JjKHLNMnDleI8umfIfVVlwC7+n5izbLSFgjw8= github.com/smartcontractkit/chainlink-automation v1.0.4/go.mod h1:u4NbPZKJ5XiayfKHD/v3z3iflQWqvtdhj13jVZXj/cM= -github.com/smartcontractkit/chainlink-common v0.2.2-0.20240723123524-e407ecd120b1 h1:pdEpjgbZ5w/Sd5lzg/XiuC5gVyrmSovOo+3nUD46SP8= -github.com/smartcontractkit/chainlink-common v0.2.2-0.20240723123524-e407ecd120b1/go.mod h1:Jg1sCTsbxg76YByI8ifpFby3FvVqISStHT8ypy9ocmY= +github.com/smartcontractkit/chainlink-common v0.2.2-0.20240828121637-da5837469949 h1:9YHYswxhxMAgdJhb+APrf57ZEPsxML8H71oxxvU36eU= +github.com/smartcontractkit/chainlink-common v0.2.2-0.20240828121637-da5837469949/go.mod h1:bE6E7KwB8dkFUWKxJTTTtrNAl9xFPGlurKpDVhRz1tk= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240710121324-3ed288aa9b45 h1:NBQLtqk8zsyY4qTJs+NElI3aDFTcAo83JHvqD04EvB0= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240710121324-3ed288aa9b45/go.mod h1:LV0h7QBQUpoC2UUi6TcUvcIFm1xjP/DtEcqV8+qeLUs= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240718160222-2dc0c8136bfa h1:g75H8oh2ws52s8BekwvGQ9XvBVu3E7WM1rfiA0PN0zk= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240718160222-2dc0c8136bfa/go.mod h1:wZvLHX/Sd9hskN51016cTFcT3G62KXVa6xbVDS7tRjc= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240820130645-cf4b159fbba2 h1:KH6tpCw5hu8u6UTtgll7a8mE4sIbHCbmtzHJdKuRwBw= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240820130645-cf4b159fbba2/go.mod h1:V/86loaFSH0dqqUEHqyXVbyNqDRSjvcf9BRomWFTljU= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240710170203-5b41615da827 h1:BCHu4pNP6arrcHLEWx61XjLaonOd2coQNyL0NTUcaMc= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240710170203-5b41615da827/go.mod h1:OPX+wC2TWQsyLNpR7daMt2vMpmsNcoBxbZyGTHr6tiA= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240712132946-267a37c5ac6e h1:PzwzlHNv1YbJ6ZIdl/pIFRoOuOS4V4WLvjZvFUnZFL4= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240712132946-267a37c5ac6e/go.mod h1:hsFhop+SlQHKD+DEFjZrMJmbauT1A/wvtZIeeo4PxFU= +github.com/smartcontractkit/chainlink-solana v1.1.1-0.20240821170223-a2f5c39f457f h1:b0Ifwk7eK3fwJ0R69Ovhv5XvZ1/TUAfHkU5Jp7wbNZ0= +github.com/smartcontractkit/chainlink-solana v1.1.1-0.20240821170223-a2f5c39f457f/go.mod h1:Ml88TJTwZCj6yHDkAEN/EhxVutzSlk+kDZgfibRIqF0= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240709043547-03612098f799 h1:HyLTySm7BR+oNfZqDTkVJ25wnmcTtxBBD31UkFL+kEM= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240709043547-03612098f799/go.mod h1:UVFRacRkP7O7TQAzFmR52v5mUlxf+G1ovMlCQAB/cHU= +github.com/smartcontractkit/chainlink-testing-framework v1.34.10-0.20240828122712-9ea5d6ac33fe h1:5EaoT0jYlmubsDawLVLgPxgEWG7IPxjuxJP3cJ1wRzw= +github.com/smartcontractkit/chainlink-testing-framework v1.34.10-0.20240828122712-9ea5d6ac33fe/go.mod h1:ekYJbRAxXcs/YgOjHsY9/tlvDvXzv3lxcZK2eFUZduc= github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.0 h1:mgjBQIEy+3V3G6K8e+6by3xndgsXdYYsdy+7kzQZwSk= github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.0/go.mod h1:pdIxrooP5CFGmC0p5NTOBiZAFtMw+5pTT4de5GY3ywA= github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.5 h1:Owb1MQZn0NZHwtZAnPZE6TVoTx6xLrfPaUdeOYswE9M= @@ -1744,8 +1746,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI= +golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1770,8 +1772,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= -golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= +golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1836,8 +1838,8 @@ golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= -golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= -golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= +golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2038,8 +2040,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= -golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= +golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -2116,10 +2118,10 @@ google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210401141331-865547bb08e2/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20240711142825-46eb208f015d h1:/hmn0Ku5kWij/kjGsrcJeC1T/MrJi2iNWwgAqrihFwc= google.golang.org/genproto v0.0.0-20240711142825-46eb208f015d/go.mod h1:FfBgJBJg9GcpPvKIuHSZ/aE1g2ecGL74upMzGZjiGEY= -google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d h1:kHjw/5UfflP/L5EbledDrcG4C2597RtymmGRZvHiCuY= -google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d/go.mod h1:mw8MG/Qz5wfgYr6VqVCiZcHe/GJEfI+oGGDCohaVgB0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d h1:JU0iKnSg02Gmb5ZdV8nYsKEKsP6o/FGVWTrw4i1DA9A= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd h1:BBOTEWLuuEGQy9n1y9MhVJ9Qt0BDu21X8qZs71/uPZo= +google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:fO8wJzT2zbQbAjbIoos1285VfEIYKDDY+Dt+WpTkh6g= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd h1:6TEm2ZxXoQmFWFlt1vNxvVOa1Q0dXFQD1m/rYjXmS0E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= diff --git a/integration-tests/load/automationv2_1/helpers.go b/integration-tests/load/automationv2_1/helpers.go index 41fa9a0ce1..f770ba7573 100644 --- a/integration-tests/load/automationv2_1/helpers.go +++ b/integration-tests/load/automationv2_1/helpers.go @@ -11,8 +11,8 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/seth" - ctf_concurrency "github.com/smartcontractkit/chainlink-testing-framework/lib/concurrency" - reportModel "github.com/smartcontractkit/chainlink-testing-framework/lib/testreporters" + ctf_concurrency "github.com/smartcontractkit/chainlink-testing-framework/concurrency" + reportModel "github.com/smartcontractkit/chainlink-testing-framework/testreporters" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/contracts" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" diff --git a/integration-tests/load/functions/onchain_monitoring.go b/integration-tests/load/functions/onchain_monitoring.go index 39c4bdcf94..942103a78b 100644 --- a/integration-tests/load/functions/onchain_monitoring.go +++ b/integration-tests/load/functions/onchain_monitoring.go @@ -8,7 +8,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/wasp" - ctf_config "github.com/smartcontractkit/chainlink-testing-framework/lib/config" + ctf_config "github.com/smartcontractkit/chainlink-testing-framework/config" ) /* Monitors on-chain stats of LoadConsumer and pushes them to Loki every second */ diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index c46ec64093..593e98c355 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -16,7 +16,8 @@ require ( github.com/rs/zerolog v1.33.0 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.4 - github.com/smartcontractkit/chainlink-common v0.2.2-0.20240723123524-e407ecd120b1 + github.com/smartcontractkit/chainlink-common v0.2.2-0.20240828121637-da5837469949 + github.com/smartcontractkit/chainlink-testing-framework v1.34.10-0.20240828122712-9ea5d6ac33fe github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.5 github.com/smartcontractkit/chainlink-testing-framework/seth v1.50.2 github.com/smartcontractkit/chainlink-testing-framework/wasp v1.50.0 @@ -53,15 +54,16 @@ require ( github.com/blang/semver/v4 v4.0.0 // indirect github.com/containerd/errdefs v0.1.0 // indirect github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect + github.com/linxGnu/grocksdb v1.7.16 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect - github.com/sergi/go-diff v1.3.1 // indirect + github.com/smartcontractkit/chainlink-ccip v0.0.0-20240806144315-04ac101e9c95 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240710121324-3ed288aa9b45 // indirect github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.0 // indirect github.com/smartcontractkit/chainlink-testing-framework/lib/grafana v1.50.0 // indirect - github.com/testcontainers/testcontainers-go v0.28.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect k8s.io/apimachinery v0.31.0 // indirect ) @@ -319,7 +321,6 @@ require ( github.com/lib/pq v1.10.9 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect - github.com/linxGnu/grocksdb v1.7.16 // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/magiconair/properties v1.8.7 // indirect @@ -394,15 +395,14 @@ require ( github.com/shopspring/decimal v1.4.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/chain-selectors v1.0.24 // indirect - github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240718160222-2dc0c8136bfa // indirect + github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240820130645-cf4b159fbba2 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20240710170203-5b41615da827 // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240712132946-267a37c5ac6e // indirect + github.com/smartcontractkit/chainlink-solana v1.1.1-0.20240821170223-a2f5c39f457f // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240709043547-03612098f799 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/wsrpc v0.8.1 // indirect github.com/soheilhy/cmux v0.1.5 // indirect github.com/sony/gobreaker v0.5.0 // indirect - github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect github.com/spf13/cobra v1.8.1 // indirect @@ -417,6 +417,7 @@ require ( github.com/tendermint/go-amino v0.16.0 // indirect github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125 // indirect github.com/test-go/testify v1.1.4 // indirect + github.com/testcontainers/testcontainers-go v0.28.0 // indirect github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a // indirect github.com/tidwall/btree v1.6.0 // indirect github.com/tidwall/gjson v1.17.0 // indirect @@ -565,4 +566,4 @@ replace ( sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.16.2 ) -exclude github.com/sourcegraph/sourcegraph/lib v0.0.0-20221216004406-749998a2ac74 +replace github.com/sourcegraph/sourcegraph/lib => github.com/sourcegraph/sourcegraph-public-snapshot/lib v0.0.0-20240822153003-c864f15af264 diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index 09384ccb12..0441d34e0d 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1389,18 +1389,22 @@ github.com/smartcontractkit/chain-selectors v1.0.24 h1:day+2l0vBErgVIs2Y9ifSz+T7 github.com/smartcontractkit/chain-selectors v1.0.24/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE= github.com/smartcontractkit/chainlink-automation v1.0.4 h1:iyW181JjKHLNMnDleI8umfIfVVlwC7+n5izbLSFgjw8= github.com/smartcontractkit/chainlink-automation v1.0.4/go.mod h1:u4NbPZKJ5XiayfKHD/v3z3iflQWqvtdhj13jVZXj/cM= -github.com/smartcontractkit/chainlink-common v0.2.2-0.20240723123524-e407ecd120b1 h1:pdEpjgbZ5w/Sd5lzg/XiuC5gVyrmSovOo+3nUD46SP8= -github.com/smartcontractkit/chainlink-common v0.2.2-0.20240723123524-e407ecd120b1/go.mod h1:Jg1sCTsbxg76YByI8ifpFby3FvVqISStHT8ypy9ocmY= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20240806144315-04ac101e9c95 h1:LAgJTg9Yr/uCo2g7Krp88Dco2U45Y6sbJVl8uKoLkys= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20240806144315-04ac101e9c95/go.mod h1:/ZWraCBaDDgaIN1prixYcbVvIk/6HeED9+8zbWQ+TMo= +github.com/smartcontractkit/chainlink-common v0.2.2-0.20240828121637-da5837469949 h1:9YHYswxhxMAgdJhb+APrf57ZEPsxML8H71oxxvU36eU= +github.com/smartcontractkit/chainlink-common v0.2.2-0.20240828121637-da5837469949/go.mod h1:bE6E7KwB8dkFUWKxJTTTtrNAl9xFPGlurKpDVhRz1tk= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240710121324-3ed288aa9b45 h1:NBQLtqk8zsyY4qTJs+NElI3aDFTcAo83JHvqD04EvB0= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240710121324-3ed288aa9b45/go.mod h1:LV0h7QBQUpoC2UUi6TcUvcIFm1xjP/DtEcqV8+qeLUs= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240718160222-2dc0c8136bfa h1:g75H8oh2ws52s8BekwvGQ9XvBVu3E7WM1rfiA0PN0zk= -github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240718160222-2dc0c8136bfa/go.mod h1:wZvLHX/Sd9hskN51016cTFcT3G62KXVa6xbVDS7tRjc= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240820130645-cf4b159fbba2 h1:KH6tpCw5hu8u6UTtgll7a8mE4sIbHCbmtzHJdKuRwBw= +github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240820130645-cf4b159fbba2/go.mod h1:V/86loaFSH0dqqUEHqyXVbyNqDRSjvcf9BRomWFTljU= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240710170203-5b41615da827 h1:BCHu4pNP6arrcHLEWx61XjLaonOd2coQNyL0NTUcaMc= github.com/smartcontractkit/chainlink-feeds v0.0.0-20240710170203-5b41615da827/go.mod h1:OPX+wC2TWQsyLNpR7daMt2vMpmsNcoBxbZyGTHr6tiA= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240712132946-267a37c5ac6e h1:PzwzlHNv1YbJ6ZIdl/pIFRoOuOS4V4WLvjZvFUnZFL4= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20240712132946-267a37c5ac6e/go.mod h1:hsFhop+SlQHKD+DEFjZrMJmbauT1A/wvtZIeeo4PxFU= +github.com/smartcontractkit/chainlink-solana v1.1.1-0.20240821170223-a2f5c39f457f h1:b0Ifwk7eK3fwJ0R69Ovhv5XvZ1/TUAfHkU5Jp7wbNZ0= +github.com/smartcontractkit/chainlink-solana v1.1.1-0.20240821170223-a2f5c39f457f/go.mod h1:Ml88TJTwZCj6yHDkAEN/EhxVutzSlk+kDZgfibRIqF0= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240709043547-03612098f799 h1:HyLTySm7BR+oNfZqDTkVJ25wnmcTtxBBD31UkFL+kEM= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20240709043547-03612098f799/go.mod h1:UVFRacRkP7O7TQAzFmR52v5mUlxf+G1ovMlCQAB/cHU= +github.com/smartcontractkit/chainlink-testing-framework v1.34.10-0.20240828122712-9ea5d6ac33fe h1:5EaoT0jYlmubsDawLVLgPxgEWG7IPxjuxJP3cJ1wRzw= +github.com/smartcontractkit/chainlink-testing-framework v1.34.10-0.20240828122712-9ea5d6ac33fe/go.mod h1:ekYJbRAxXcs/YgOjHsY9/tlvDvXzv3lxcZK2eFUZduc= github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.0 h1:mgjBQIEy+3V3G6K8e+6by3xndgsXdYYsdy+7kzQZwSk= github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.0/go.mod h1:pdIxrooP5CFGmC0p5NTOBiZAFtMw+5pTT4de5GY3ywA= github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.5 h1:Owb1MQZn0NZHwtZAnPZE6TVoTx6xLrfPaUdeOYswE9M= diff --git a/integration-tests/load/ocr/vu.go b/integration-tests/load/ocr/vu.go index aece9cb74b..8e15344610 100644 --- a/integration-tests/load/ocr/vu.go +++ b/integration-tests/load/ocr/vu.go @@ -15,7 +15,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/wasp" - client2 "github.com/smartcontractkit/chainlink-testing-framework/lib/client" + client2 "github.com/smartcontractkit/chainlink-testing-framework/client" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" diff --git a/integration-tests/load/vrfv2/gun.go b/integration-tests/load/vrfv2/gun.go index 20a20b4083..e9e7de9bef 100644 --- a/integration-tests/load/vrfv2/gun.go +++ b/integration-tests/load/vrfv2/gun.go @@ -9,7 +9,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/seth" - seth_utils "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/seth" + seth_utils "github.com/smartcontractkit/chainlink-testing-framework/utils/seth" vrfcommon "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/common" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/vrfv2" diff --git a/integration-tests/load/vrfv2/vrfv2_test.go b/integration-tests/load/vrfv2/vrfv2_test.go index 49bd2dd9c0..8e2a42fd21 100644 --- a/integration-tests/load/vrfv2/vrfv2_test.go +++ b/integration-tests/load/vrfv2/vrfv2_test.go @@ -13,11 +13,10 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/wasp" - "github.com/smartcontractkit/chainlink-testing-framework/lib/logging" - "github.com/smartcontractkit/chainlink-testing-framework/lib/networks" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/ptr" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" - + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/networks" + "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" vrfcommon "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/common" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/vrfv2" @@ -30,10 +29,12 @@ import ( tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) -var labels = map[string]string{ - "branch": "vrfv2_healthcheck", - "commit": "vrfv2_healthcheck", -} +var ( + labels = map[string]string{ + "branch": "vrfv2_healthcheck", + "commit": "vrfv2_healthcheck", + } +) func TestVRFV2Performance(t *testing.T) { var ( @@ -72,9 +73,9 @@ func TestVRFV2Performance(t *testing.T) { Uint16("RandomnessRequestCountPerRequestDeviation", *vrfv2Config.General.RandomnessRequestCountPerRequestDeviation). Bool("UseExistingEnv", *vrfv2Config.General.UseExistingEnv). Msg("Performance Test Configuration") + cleanupFn := func() { teardown(t, vrfContracts.VRFV2Consumers[0], lc, updatedLabels, testReporter, testType, &testConfig) - require.NoError(t, err, "Getting Seth client shouldn't fail") if sethClient.Cfg.IsSimulatedNetwork() { l.Info(). @@ -82,12 +83,14 @@ func TestVRFV2Performance(t *testing.T) { Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") } else { if *vrfv2Config.General.CancelSubsAfterTestRun { + // wait for all txs to be mined in order to avoid nonce issues + time.Sleep(10 * time.Second) //cancel subs and return funds to sub owner vrfv2.CancelSubsAndReturnFunds(testcontext.Get(t), vrfContracts, sethClient.MustGetRootKeyAddress().Hex(), subIDsForCancellingAfterTest, l) } } if !*vrfv2Config.General.UseExistingEnv { - if err := testEnv.Cleanup(test_env.CleanupOpts{TestName: t.Name()}); err != nil { + if err := testEnv.Cleanup(test_env.CleanupOpts{}); err != nil { l.Error().Err(err).Msg("Error cleaning up test environment") } } @@ -164,7 +167,7 @@ func TestVRFV2Performance(t *testing.T) { var wg sync.WaitGroup wg.Add(1) - // todo - timeout should be configurable depending on the perf test type + //todo - timeout should be configurable depending on the perf test type requestCount, fulfilmentCount, err := vrfcommon.WaitForRequestCountEqualToFulfilmentCount(testcontext.Get(t), vrfContracts.VRFV2Consumers[0], 2*time.Minute, &wg) require.NoError(t, err) wg.Wait() @@ -228,7 +231,7 @@ func TestVRFV2BHSPerformance(t *testing.T) { } } if !*vrfv2Config.General.UseExistingEnv { - if err := testEnv.Cleanup(test_env.CleanupOpts{TestName: t.Name()}); err != nil { + if err := testEnv.Cleanup(test_env.CleanupOpts{}); err != nil { l.Error().Err(err).Msg("Error cleaning up test environment") } } @@ -250,7 +253,7 @@ func TestVRFV2BHSPerformance(t *testing.T) { t.Run("vrfv2 and bhs performance test", func(t *testing.T) { configCopy := testConfig.MustCopy().(tc.TestConfig) - // Underfund Subscription + //Underfund Subscription configCopy.VRFv2.General.SubscriptionFundingAmountLink = ptr.Ptr(float64(0)) underfundedSubIDs, consumers, err := vrfv2.SetupSubsAndConsumersForExistingEnv( @@ -307,7 +310,7 @@ func TestVRFV2BHSPerformance(t *testing.T) { wgBlockNumberTobe.Add(1) //Wait at least 256 blocks latestBlockNumber, err := sethClient.Client.BlockNumber(testcontext.Get(t)) - require.NoError(t, err) + require.NoError(t, err, "error getting latest block number") _, err = actions.WaitForBlockNumberToBe( testcontext.Get(t), latestBlockNumber+uint64(257), @@ -350,7 +353,6 @@ func TestVRFV2BHSPerformance(t *testing.T) { Msg("Final Request/Fulfilment Stats") }) } - func teardown( t *testing.T, consumer contracts.VRFv2LoadTestConsumer, @@ -360,10 +362,10 @@ func teardown( testType string, testConfig *tc.TestConfig, ) { - // send final results to Loki + //send final results to Loki metrics := GetLoadTestMetrics(testcontext.Get(t), consumer) SendMetricsToLoki(metrics, lc, updatedLabels) - // set report data for Slack notification + //set report data for Slack notification testReporter.SetReportData( testType, testreporters.VRFLoadTestMetrics{ diff --git a/integration-tests/load/vrfv2plus/gun.go b/integration-tests/load/vrfv2plus/gun.go index 4aac392751..3c16bbafdc 100644 --- a/integration-tests/load/vrfv2plus/gun.go +++ b/integration-tests/load/vrfv2plus/gun.go @@ -10,7 +10,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/seth" - seth_utils "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/seth" + seth_utils "github.com/smartcontractkit/chainlink-testing-framework/utils/seth" "github.com/smartcontractkit/chainlink/integration-tests/actions" vrfcommon "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/common" diff --git a/integration-tests/load/vrfv2plus/vrfv2plus_test.go b/integration-tests/load/vrfv2plus/vrfv2plus_test.go index bcefe510a6..30d33aa3c6 100644 --- a/integration-tests/load/vrfv2plus/vrfv2plus_test.go +++ b/integration-tests/load/vrfv2plus/vrfv2plus_test.go @@ -13,10 +13,10 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/wasp" - "github.com/smartcontractkit/chainlink-testing-framework/lib/logging" - "github.com/smartcontractkit/chainlink-testing-framework/lib/networks" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/ptr" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/networks" + "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/vrfv2plus" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/testreporters" @@ -28,10 +28,12 @@ import ( tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" ) -var labels = map[string]string{ - "branch": "vrfv2Plus_healthcheck", - "commit": "vrfv2Plus_healthcheck", -} +var ( + labels = map[string]string{ + "branch": "vrfv2Plus_healthcheck", + "commit": "vrfv2Plus_healthcheck", + } +) func TestVRFV2PlusPerformance(t *testing.T) { var ( @@ -80,12 +82,14 @@ func TestVRFV2PlusPerformance(t *testing.T) { Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") } else { if *testConfig.VRFv2Plus.General.CancelSubsAfterTestRun { + // wait for all txs to be mined in order to avoid nonce issues + time.Sleep(10 * time.Second) //cancel subs and return funds to sub owner vrfv2plus.CancelSubsAndReturnFunds(testcontext.Get(t), vrfContracts, sethClient.MustGetRootKeyAddress().Hex(), subIDsForCancellingAfterTest, l) } } if !*testConfig.VRFv2Plus.General.UseExistingEnv { - if err := testEnv.Cleanup(test_env.CleanupOpts{TestName: t.Name()}); err != nil { + if err := testEnv.Cleanup(test_env.CleanupOpts{}); err != nil { l.Error().Err(err).Msg("Error cleaning up test environment") } } @@ -163,7 +167,7 @@ func TestVRFV2PlusPerformance(t *testing.T) { var wg sync.WaitGroup wg.Add(1) - // todo - timeout should be configurable depending on the perf test type + //todo - timeout should be configurable depending on the perf test type requestCount, fulfilmentCount, err := vrfcommon.WaitForRequestCountEqualToFulfilmentCount(testcontext.Get(t), consumer, 2*time.Minute, &wg) require.NoError(t, err) wg.Wait() @@ -227,7 +231,7 @@ func TestVRFV2PlusBHSPerformance(t *testing.T) { } } if !*testConfig.VRFv2Plus.General.UseExistingEnv { - if err := testEnv.Cleanup(test_env.CleanupOpts{TestName: t.Name()}); err != nil { + if err := testEnv.Cleanup(test_env.CleanupOpts{}); err != nil { l.Error().Err(err).Msg("Error cleaning up test environment") } } @@ -248,7 +252,7 @@ func TestVRFV2PlusBHSPerformance(t *testing.T) { t.Run("vrfv2plus and bhs performance test", func(t *testing.T) { configCopy := testConfig.MustCopy().(tc.TestConfig) - // Underfund Subscription + //Underfund Subscription configCopy.VRFv2Plus.General.SubscriptionFundingAmountLink = ptr.Ptr(float64(0)) configCopy.VRFv2Plus.General.SubscriptionFundingAmountNative = ptr.Ptr(float64(0)) @@ -368,10 +372,10 @@ func teardown( testType string, testConfig *tc.TestConfig, ) { - // send final results to Loki + //send final results to Loki metrics := GetLoadTestMetrics(testcontext.Get(t), consumer) SendMetricsToLoki(metrics, lc, updatedLabels) - // set report data for Slack notification + //set report data for Slack notification testReporter.SetReportData( testType, testreporters.VRFLoadTestMetrics{ diff --git a/integration-tests/reorg/automation_reorg_test.go b/integration-tests/reorg/automation_reorg_test.go index 22cfaa7ca6..15b3b74582 100644 --- a/integration-tests/reorg/automation_reorg_test.go +++ b/integration-tests/reorg/automation_reorg_test.go @@ -43,7 +43,7 @@ var ( ) var logScannerSettings = test_env.GetDefaultChainlinkNodeLogScannerSettingsWithExtraAllowedMessages(testreporters.NewAllowedLogMessage( - "Got very old block with number", + "Got very old block.", "It is expected, because we are causing reorgs", zapcore.DPanicLevel, testreporters.WarnAboutAllowedMsgs_No, diff --git a/integration-tests/smoke/README.md b/integration-tests/smoke/README.md index 266720c7bc..c4aa2b91a1 100644 --- a/integration-tests/smoke/README.md +++ b/integration-tests/smoke/README.md @@ -75,9 +75,3 @@ Then execute: go test -v -run ${TestName} ``` - - -### Debugging CL client API calls -```bash -export CL_CLIENT_DEBUG=true -``` \ No newline at end of file diff --git a/integration-tests/smoke/automation_test.go b/integration-tests/smoke/automation_test.go index 92cbc95a9d..39a9f75492 100644 --- a/integration-tests/smoke/automation_test.go +++ b/integration-tests/smoke/automation_test.go @@ -12,7 +12,7 @@ import ( "testing" "time" - seth_utils "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/seth" + "github.com/smartcontractkit/chainlink/integration-tests/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -23,9 +23,9 @@ import ( ocr3 "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" ocr2keepers30config "github.com/smartcontractkit/chainlink-automation/pkg/v3/config" - ctfTestEnv "github.com/smartcontractkit/chainlink-testing-framework/lib/docker/test_env" - "github.com/smartcontractkit/chainlink-testing-framework/lib/logging" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" + ctfTestEnv "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions/automationv2" "github.com/smartcontractkit/chainlink/integration-tests/contracts" @@ -1427,7 +1427,7 @@ func setupAutomationTestDocker( evmNetwork, err := env.GetFirstEvmNetwork() require.NoError(t, err, "Error getting first evm network") - sethClient, err := seth_utils.GetChainClient(automationTestConfig, *evmNetwork) + sethClient, err := utils.TestAwareSethClient(t, automationTestConfig, evmNetwork) require.NoError(t, err, "Error getting seth client") err = actions.FundChainlinkNodesFromRootAddress(l, sethClient, contracts.ChainlinkClientToChainlinkNodeWithKeysAndAddress(env.ClCluster.NodeAPIs()), big.NewFloat(*automationTestConfig.GetCommonConfig().ChainlinkNodeFunding)) diff --git a/integration-tests/smoke/flux_test.go b/integration-tests/smoke/flux_test.go index 6f007166b1..d66cdbd284 100644 --- a/integration-tests/smoke/flux_test.go +++ b/integration-tests/smoke/flux_test.go @@ -8,13 +8,14 @@ import ( "testing" "time" + "github.com/smartcontractkit/chainlink/integration-tests/utils" + "github.com/ethereum/go-ethereum/common" "github.com/google/uuid" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-testing-framework/lib/logging" - seth_utils "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/seth" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" @@ -49,7 +50,7 @@ func TestFluxBasic(t *testing.T) { evmNetwork, err := env.GetFirstEvmNetwork() require.NoError(t, err, "Error getting first evm network") - sethClient, err := seth_utils.GetChainClient(config, *evmNetwork) + sethClient, err := utils.TestAwareSethClient(t, config, evmNetwork) require.NoError(t, err, "Error getting seth client") adapterUUID := uuid.NewString() diff --git a/integration-tests/smoke/forwarder_ocr_test.go b/integration-tests/smoke/forwarder_ocr_test.go index e7c550e1d3..1eff96cb7a 100644 --- a/integration-tests/smoke/forwarder_ocr_test.go +++ b/integration-tests/smoke/forwarder_ocr_test.go @@ -6,13 +6,13 @@ import ( "testing" "time" - seth_utils "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/seth" + "github.com/smartcontractkit/chainlink/integration-tests/utils" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-testing-framework/lib/logging" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/contracts" @@ -49,7 +49,7 @@ func TestForwarderOCRBasic(t *testing.T) { evmNetwork, err := env.GetFirstEvmNetwork() require.NoError(t, err, "Error getting first evm network") - sethClient, err := seth_utils.GetChainClient(config, *evmNetwork) + sethClient, err := utils.TestAwareSethClient(t, config, evmNetwork) require.NoError(t, err, "Error getting seth client") err = actions.FundChainlinkNodesFromRootAddress(l, sethClient, contracts.ChainlinkClientToChainlinkNodeWithKeysAndAddress(env.ClCluster.NodeAPIs()), big.NewFloat(*config.Common.ChainlinkNodeFunding)) diff --git a/integration-tests/smoke/forwarders_ocr2_test.go b/integration-tests/smoke/forwarders_ocr2_test.go index 58de6d7b11..e3cced94fd 100644 --- a/integration-tests/smoke/forwarders_ocr2_test.go +++ b/integration-tests/smoke/forwarders_ocr2_test.go @@ -7,13 +7,13 @@ import ( "testing" "time" - seth_utils "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/seth" + "github.com/smartcontractkit/chainlink/integration-tests/utils" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-testing-framework/lib/logging" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/contracts" @@ -50,7 +50,7 @@ func TestForwarderOCR2Basic(t *testing.T) { evmNetwork, err := env.GetFirstEvmNetwork() require.NoError(t, err, "Error getting first evm network") - sethClient, err := seth_utils.GetChainClient(config, *evmNetwork) + sethClient, err := utils.TestAwareSethClient(t, config, evmNetwork) require.NoError(t, err, "Error getting seth client") err = actions.FundChainlinkNodesFromRootAddress(l, sethClient, contracts.ChainlinkClientToChainlinkNodeWithKeysAndAddress(env.ClCluster.NodeAPIs()), big.NewFloat(*config.Common.ChainlinkNodeFunding)) diff --git a/integration-tests/smoke/keeper_test.go b/integration-tests/smoke/keeper_test.go index 21eaf23122..29602da34f 100644 --- a/integration-tests/smoke/keeper_test.go +++ b/integration-tests/smoke/keeper_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - seth_utils "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/seth" + "github.com/smartcontractkit/chainlink/integration-tests/utils" "github.com/ethereum/go-ethereum/common" "github.com/onsi/gomega" @@ -17,8 +17,8 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/seth" - "github.com/smartcontractkit/chainlink-testing-framework/lib/logging" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" @@ -1244,7 +1244,7 @@ func setupKeeperTest(l zerolog.Logger, t *testing.T, config *tc.TestConfig) ( evmNetwork, err := env.GetFirstEvmNetwork() require.NoError(t, err, "Error getting first evm network") - sethClient, err := seth_utils.GetChainClient(config, *evmNetwork) + sethClient, err := utils.TestAwareSethClient(t, config, evmNetwork) require.NoError(t, err, "Error getting seth client") err = actions.FundChainlinkNodesFromRootAddress(l, sethClient, contracts.ChainlinkClientToChainlinkNodeWithKeysAndAddress(env.ClCluster.NodeAPIs()), big.NewFloat(*config.Common.ChainlinkNodeFunding)) diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index adcc99ae09..03c0d81c46 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - seth_utils "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/seth" + "github.com/smartcontractkit/chainlink/integration-tests/utils" "github.com/ethereum/go-ethereum/common" "github.com/rs/zerolog" @@ -16,9 +16,9 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/seth" - "github.com/smartcontractkit/chainlink-testing-framework/lib/logging" - "github.com/smartcontractkit/chainlink-testing-framework/lib/logstream" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/logstream" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/v2/core/config/env" @@ -164,7 +164,7 @@ func prepareORCv2SmokeTestEnv(t *testing.T, testData ocr2test, l zerolog.Logger, evmNetwork, err := testEnv.GetFirstEvmNetwork() require.NoError(t, err, "Error getting first evm network") - sethClient, err := seth_utils.GetChainClient(config, *evmNetwork) + sethClient, err := utils.TestAwareSethClient(t, config, evmNetwork) require.NoError(t, err, "Error getting seth client") nodeClients := testEnv.ClCluster.NodeAPIs() diff --git a/integration-tests/smoke/ocr_test.go b/integration-tests/smoke/ocr_test.go index cd6954ac2c..41cc1fb641 100644 --- a/integration-tests/smoke/ocr_test.go +++ b/integration-tests/smoke/ocr_test.go @@ -5,21 +5,20 @@ import ( "testing" "time" - seth_utils "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/seth" - "github.com/ethereum/go-ethereum/common" "github.com/rs/zerolog" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/seth" - "github.com/smartcontractkit/chainlink-testing-framework/lib/logging" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" + "github.com/smartcontractkit/chainlink/integration-tests/utils" ) const ( @@ -100,7 +99,7 @@ func prepareORCv1SmokeTestEnv(t *testing.T, l zerolog.Logger, firstRoundResult i evmNetwork, err := env.GetFirstEvmNetwork() require.NoError(t, err, "Error getting first evm network") - sethClient, err := seth_utils.GetChainClient(config, *evmNetwork) + sethClient, err := utils.TestAwareSethClient(t, config, evmNetwork) require.NoError(t, err, "Error getting seth client") nodeClients := env.ClCluster.NodeAPIs() diff --git a/integration-tests/smoke/runlog_test.go b/integration-tests/smoke/runlog_test.go index 922cc9c372..1558b44732 100644 --- a/integration-tests/smoke/runlog_test.go +++ b/integration-tests/smoke/runlog_test.go @@ -7,14 +7,14 @@ import ( "strings" "testing" - seth_utils "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/seth" + "github.com/smartcontractkit/chainlink/integration-tests/utils" "github.com/google/uuid" "github.com/onsi/gomega" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-testing-framework/lib/logging" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" @@ -47,7 +47,7 @@ func TestRunLogBasic(t *testing.T) { evmNetwork, err := env.GetFirstEvmNetwork() require.NoError(t, err, "Error getting first evm network") - sethClient, err := seth_utils.GetChainClient(config, *evmNetwork) + sethClient, err := utils.TestAwareSethClient(t, config, evmNetwork) require.NoError(t, err, "Error getting seth client") err = actions.FundChainlinkNodesFromRootAddress(l, sethClient, contracts.ChainlinkClientToChainlinkNodeWithKeysAndAddress(env.ClCluster.NodeAPIs()), big.NewFloat(*config.Common.ChainlinkNodeFunding)) diff --git a/integration-tests/smoke/vrf_test.go b/integration-tests/smoke/vrf_test.go index afef09841b..76a4a06c6f 100644 --- a/integration-tests/smoke/vrf_test.go +++ b/integration-tests/smoke/vrf_test.go @@ -11,11 +11,9 @@ import ( "github.com/rs/zerolog" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/seth" - - "github.com/smartcontractkit/chainlink-testing-framework/lib/logging" - seth_utils "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/seth" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrf/vrfv1" @@ -24,6 +22,7 @@ import ( ethcontracts "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" + "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func TestVRFBasic(t *testing.T) { @@ -205,7 +204,7 @@ func prepareVRFtestEnv(t *testing.T, l zerolog.Logger) (*test_env.CLClusterTestE evmNetwork, err := env.GetFirstEvmNetwork() require.NoError(t, err, "Error getting first evm network") - sethClient, err := seth_utils.GetChainClient(config, *evmNetwork) + sethClient, err := utils.TestAwareSethClient(t, config, evmNetwork) require.NoError(t, err, "Error getting seth client") err = actions.FundChainlinkNodesFromRootAddress(l, sethClient, contracts.ChainlinkClientToChainlinkNodeWithKeysAndAddress(env.ClCluster.NodeAPIs()), big.NewFloat(*config.Common.ChainlinkNodeFunding)) diff --git a/integration-tests/smoke/vrfv2_test.go b/integration-tests/smoke/vrfv2_test.go index 4ae2553fa0..f14f212ae4 100644 --- a/integration-tests/smoke/vrfv2_test.go +++ b/integration-tests/smoke/vrfv2_test.go @@ -1064,7 +1064,7 @@ func TestVRFV2NodeReorg(t *testing.T) { chainlinkNodeLogScannerSettings := test_env.GetDefaultChainlinkNodeLogScannerSettingsWithExtraAllowedMessages( testreporters.NewAllowedLogMessage( - "This is a problem and either means a very deep re-org occurred", + "Got very old block.", "Test is expecting a reorg to occur", zapcore.DPanicLevel, testreporters.WarnAboutAllowedMsgs_No), diff --git a/integration-tests/smoke/vrfv2plus_test.go b/integration-tests/smoke/vrfv2plus_test.go index 36c61ee91b..da2740ad67 100644 --- a/integration-tests/smoke/vrfv2plus_test.go +++ b/integration-tests/smoke/vrfv2plus_test.go @@ -290,16 +290,14 @@ func TestVRFv2Plus(t *testing.T) { t.Run("Direct Funding", func(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) - wrapperContracts, wrapperSubID, err := vrfv2plus.SetupVRFV2PlusWrapperEnvironment( + wrapperContracts, wrapperSubID, err := vrfv2plus.SetupVRFV2PlusWrapperUniverse( testcontext.Get(t), - l, sethClient, + vrfContracts, &configCopy, - vrfContracts.LinkToken, - vrfContracts.MockETHLINKFeed, - vrfContracts.CoordinatorV2Plus, vrfKey.KeyHash, 1, + l, ) require.NoError(t, err) @@ -308,7 +306,7 @@ func TestVRFv2Plus(t *testing.T) { testConfig := configCopy.VRFv2Plus.General isNativeBilling := false - wrapperConsumerJuelsBalanceBeforeRequest, err := vrfContracts.LinkToken.BalanceOf(testcontext.Get(t), wrapperContracts.LoadTestConsumers[0].Address()) + wrapperConsumerJuelsBalanceBeforeRequest, err := vrfContracts.LinkToken.BalanceOf(testcontext.Get(t), wrapperContracts.WrapperConsumers[0].Address()) require.NoError(t, err, "error getting wrapper consumer balance") wrapperSubscription, err := vrfContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), wrapperSubID) @@ -316,7 +314,7 @@ func TestVRFv2Plus(t *testing.T) { subBalanceBeforeRequest := wrapperSubscription.Balance randomWordsFulfilledEvent, err := vrfv2plus.DirectFundingRequestRandomnessAndWaitForFulfillment( - wrapperContracts.LoadTestConsumers[0], + wrapperContracts.WrapperConsumers[0], vrfContracts.CoordinatorV2Plus, vrfKey, wrapperSubID, @@ -332,13 +330,13 @@ func TestVRFv2Plus(t *testing.T) { subBalanceAfterRequest := wrapperSubscription.Balance require.Equal(t, expectedSubBalanceJuels, subBalanceAfterRequest) - consumerStatus, err := wrapperContracts.LoadTestConsumers[0].GetRequestStatus(testcontext.Get(t), randomWordsFulfilledEvent.RequestId) + consumerStatus, err := wrapperContracts.WrapperConsumers[0].GetRequestStatus(testcontext.Get(t), randomWordsFulfilledEvent.RequestId) require.NoError(t, err, "error getting rand request status") require.True(t, consumerStatus.Fulfilled) expectedWrapperConsumerJuelsBalance := new(big.Int).Sub(wrapperConsumerJuelsBalanceBeforeRequest, consumerStatus.Paid) - wrapperConsumerJuelsBalanceAfterRequest, err := vrfContracts.LinkToken.BalanceOf(testcontext.Get(t), wrapperContracts.LoadTestConsumers[0].Address()) + wrapperConsumerJuelsBalanceAfterRequest, err := vrfContracts.LinkToken.BalanceOf(testcontext.Get(t), wrapperContracts.WrapperConsumers[0].Address()) require.NoError(t, err, "error getting wrapper consumer balance") require.Equal(t, expectedWrapperConsumerJuelsBalance, wrapperConsumerJuelsBalanceAfterRequest) @@ -357,7 +355,7 @@ func TestVRFv2Plus(t *testing.T) { testConfig := configCopy.VRFv2Plus.General isNativeBilling := true - wrapperConsumerBalanceBeforeRequestWei, err := sethClient.Client.BalanceAt(testcontext.Get(t), common.HexToAddress(wrapperContracts.LoadTestConsumers[0].Address()), nil) + wrapperConsumerBalanceBeforeRequestWei, err := sethClient.Client.BalanceAt(testcontext.Get(t), common.HexToAddress(wrapperContracts.WrapperConsumers[0].Address()), nil) require.NoError(t, err, "error getting wrapper consumer balance") wrapperSubscription, err := vrfContracts.CoordinatorV2Plus.GetSubscription(testcontext.Get(t), wrapperSubID) @@ -365,7 +363,7 @@ func TestVRFv2Plus(t *testing.T) { subBalanceBeforeRequest := wrapperSubscription.NativeBalance randomWordsFulfilledEvent, err := vrfv2plus.DirectFundingRequestRandomnessAndWaitForFulfillment( - wrapperContracts.LoadTestConsumers[0], + wrapperContracts.WrapperConsumers[0], vrfContracts.CoordinatorV2Plus, vrfKey, wrapperSubID, @@ -381,13 +379,13 @@ func TestVRFv2Plus(t *testing.T) { subBalanceAfterRequest := wrapperSubscription.NativeBalance require.Equal(t, expectedSubBalanceWei, subBalanceAfterRequest) - consumerStatus, err := wrapperContracts.LoadTestConsumers[0].GetRequestStatus(testcontext.Get(t), randomWordsFulfilledEvent.RequestId) + consumerStatus, err := wrapperContracts.WrapperConsumers[0].GetRequestStatus(testcontext.Get(t), randomWordsFulfilledEvent.RequestId) require.NoError(t, err, "error getting rand request status") require.True(t, consumerStatus.Fulfilled) expectedWrapperConsumerWeiBalance := new(big.Int).Sub(wrapperConsumerBalanceBeforeRequestWei, consumerStatus.Paid) - wrapperConsumerBalanceAfterRequestWei, err := sethClient.Client.BalanceAt(testcontext.Get(t), common.HexToAddress(wrapperContracts.LoadTestConsumers[0].Address()), nil) + wrapperConsumerBalanceAfterRequestWei, err := sethClient.Client.BalanceAt(testcontext.Get(t), common.HexToAddress(wrapperContracts.WrapperConsumers[0].Address()), nil) require.NoError(t, err, "error getting wrapper consumer balance") require.Equal(t, expectedWrapperConsumerWeiBalance, wrapperConsumerBalanceAfterRequestWei) @@ -1063,16 +1061,14 @@ func TestVRFv2PlusMigration(t *testing.T) { t.Run("Test migration of direct billing using VRFV2PlusWrapper subID", func(t *testing.T) { configCopy := config.MustCopy().(tc.TestConfig) - wrapperContracts, wrapperSubID, err := vrfv2plus.SetupVRFV2PlusWrapperEnvironment( + wrapperContracts, wrapperSubID, err := vrfv2plus.SetupVRFV2PlusWrapperUniverse( testcontext.Get(t), - l, sethClient, + vrfContracts, &configCopy, - vrfContracts.LinkToken, - vrfContracts.MockETHLINKFeed, - vrfContracts.CoordinatorV2Plus, vrfKey.KeyHash, 1, + l, ) require.NoError(t, err) subID := wrapperSubID @@ -1203,7 +1199,7 @@ func TestVRFv2PlusMigration(t *testing.T) { // Verify rand requests fulfills with Link Token billing isNativeBilling := false randomWordsFulfilledEvent, err := vrfv2plus.DirectFundingRequestRandomnessAndWaitForFulfillment( - wrapperContracts.LoadTestConsumers[0], + wrapperContracts.WrapperConsumers[0], newCoordinator, vrfKey, subID, @@ -1212,14 +1208,14 @@ func TestVRFv2PlusMigration(t *testing.T) { l, ) require.NoError(t, err, "error requesting randomness and waiting for fulfilment") - consumerStatus, err := wrapperContracts.LoadTestConsumers[0].GetRequestStatus(testcontext.Get(t), randomWordsFulfilledEvent.RequestId) + consumerStatus, err := wrapperContracts.WrapperConsumers[0].GetRequestStatus(testcontext.Get(t), randomWordsFulfilledEvent.RequestId) require.NoError(t, err, "error getting rand request status") require.True(t, consumerStatus.Fulfilled) // Verify rand requests fulfills with Native Token billing isNativeBilling = true randomWordsFulfilledEvent, err = vrfv2plus.DirectFundingRequestRandomnessAndWaitForFulfillment( - wrapperContracts.LoadTestConsumers[0], + wrapperContracts.WrapperConsumers[0], newCoordinator, vrfKey, subID, @@ -1228,7 +1224,7 @@ func TestVRFv2PlusMigration(t *testing.T) { l, ) require.NoError(t, err, "error requesting randomness and waiting for fulfilment") - consumerStatus, err = wrapperContracts.LoadTestConsumers[0].GetRequestStatus(testcontext.Get(t), randomWordsFulfilledEvent.RequestId) + consumerStatus, err = wrapperContracts.WrapperConsumers[0].GetRequestStatus(testcontext.Get(t), randomWordsFulfilledEvent.RequestId) require.NoError(t, err, "error getting rand request status") require.True(t, consumerStatus.Fulfilled) }) @@ -1347,11 +1343,10 @@ func TestVRFV2PlusWithBHS(t *testing.T) { }() if *configCopy.VRFv2Plus.General.GenerateTXsOnChain { + wg.Add(1) go func() { - _, err := actions.ContinuouslyGenerateTXsOnChain(sethClient, desiredBlockNumberReached, l) + _, err := actions.ContinuouslyGenerateTXsOnChain(sethClient, desiredBlockNumberReached, &wg, l) require.NoError(t, err) - // Wait to let the transactions be mined and avoid nonce issues - time.Sleep(time.Second * 5) }() } wg.Wait() @@ -1959,7 +1954,7 @@ func TestVRFv2PlusNodeReorg(t *testing.T) { } chainlinkNodeLogScannerSettings := test_env.GetDefaultChainlinkNodeLogScannerSettingsWithExtraAllowedMessages( testreporters.NewAllowedLogMessage( - "This is a problem and either means a very deep re-org occurred", + "Got very old block.", "Test is expecting a reorg to occur", zapcore.DPanicLevel, testreporters.WarnAboutAllowedMsgs_No), diff --git a/integration-tests/soak/forwarder_ocr_test.go b/integration-tests/soak/forwarder_ocr_test.go index 3017a1cef2..292693d509 100644 --- a/integration-tests/soak/forwarder_ocr_test.go +++ b/integration-tests/soak/forwarder_ocr_test.go @@ -42,6 +42,11 @@ func executeForwarderOCRSoakTest(t *testing.T, config *tc.TestConfig) { t.Cleanup(func() { if err := actions.TeardownRemoteSuite(ocrSoakTest.TearDownVals(t)); err != nil { l.Error().Err(err).Msg("Error tearing down environment") + } else { + err := ocrSoakTest.Environment().Client.RemoveNamespace(ocrSoakTest.Environment().Cfg.Namespace) + if err != nil { + l.Error().Err(err).Msg("Error removing namespace") + } } }) ocrSoakTest.Setup(config) diff --git a/integration-tests/soak/ocr_test.go b/integration-tests/soak/ocr_test.go index 5e53c7d098..59368c0eeb 100644 --- a/integration-tests/soak/ocr_test.go +++ b/integration-tests/soak/ocr_test.go @@ -18,8 +18,10 @@ import ( "github.com/google/uuid" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/smartcontractkit/chainlink-testing-framework/lib/networks" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/ptr" + "github.com/smartcontractkit/chainlink-testing-framework/havoc" + + "github.com/smartcontractkit/chainlink-testing-framework/networks" + "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" "github.com/smartcontractkit/chainlink/integration-tests/actions" tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" "github.com/smartcontractkit/chainlink/integration-tests/testsetups" @@ -161,6 +163,11 @@ func executeOCRSoakTest(t *testing.T, test *testsetups.OCRSoakTest, config *tc.T t.Cleanup(func() { if err := actions.TeardownRemoteSuite(test.TearDownVals(t)); err != nil { l.Error().Err(err).Msg("Error tearing down environment") + } else { + err := test.Environment().Client.RemoveNamespace(test.Environment().Cfg.Namespace) + if err != nil { + l.Error().Err(err).Msg("Error removing namespace") + } } }) if test.Interrupted() { diff --git a/integration-tests/test.Dockerfile b/integration-tests/test.Dockerfile index fc6eefd650..2e928ab29f 100644 --- a/integration-tests/test.Dockerfile +++ b/integration-tests/test.Dockerfile @@ -1,10 +1,18 @@ ARG BASE_IMAGE ARG IMAGE_VERSION=latest -FROM ${BASE_IMAGE}:${IMAGE_VERSION} +FROM ${BASE_IMAGE}:${IMAGE_VERSION} AS build-env ARG SUITES=chaos migration performance reorg smoke soak benchmark COPY . testdir/ WORKDIR /go/testdir RUN /go/testdir/integration-tests/scripts/buildTests "${SUITES}" + +FROM ${BASE_IMAGE}:${IMAGE_VERSION} + +RUN mkdir -p /go/testdir/integration-tests/scripts +COPY --from=build-env /go/pkg /go/pkg +COPY --from=build-env /go/testdir/integration-tests/*.test /go/testdir/integration-tests/ +COPY --from=build-env /go/testdir/integration-tests/scripts /go/testdir/integration-tests/scripts/ + ENTRYPOINT ["/go/testdir/integration-tests/scripts/entrypoint"] diff --git a/integration-tests/testconfig/README.md b/integration-tests/testconfig/README.md index 7ff6cedd24..878b36bc75 100644 --- a/integration-tests/testconfig/README.md +++ b/integration-tests/testconfig/README.md @@ -137,7 +137,9 @@ DefaultTransactionQueueDepth = 0 """ ``` Note that you cannot override individual values in BaseConfigTOML. You must provide the entire configuration. +This corresponds to [Config struct](../../core/services/chainlink/config.go) in Chainlink Node that excludes all chain-specific configuration, which is built based on selected_networks and either Chainlink Node's defaults for each network, or `ChainConfigTOMLByChainID` (if an entry with matching chain id is defined) or `CommonChainConfigTOML` (if no entry with matching chain id is defined). +If BaseConfigTOML is empty, then default base config provided by the Chainlink Node is used. If tracing is enabled unique id will be generated and shared between all Chainlink nodes in the same test. To set base config for EVM chains use `NodeConfig.CommonChainConfigTOML`. Example: ```toml @@ -153,12 +155,12 @@ FeeCapDefault = '200 gwei' """ ``` -This is the default configuration used for all EVM chains unless ChainConfigTOMLByChainID is specified. +This is the default configuration used for all EVM chains unless `ChainConfigTOMLByChainID` is specified. Do remember that if either `ChainConfigTOMLByChainID` or `CommonChainConfigTOML` is defined, it will override any defaults that Chainlink Node might have for the given network. Part of the configuration that defines blockchain node URLs is always dynamically generated based on the EVMNetwork configuration. To set custom per-chain config use `[NodeConfig.ChainConfigTOMLByChainID]`. Example: ```toml [NodeConfig.ChainConfigTOMLByChainID] -# applicable for arbitrum-goerli chain +# applicable only to arbitrum-goerli chain 421613 = """ [GasEstimator] PriceMax = '400 gwei' @@ -170,7 +172,8 @@ BumpMin = '100 gwei' """ ``` -For more examples see `example.toml` in product TOML configs like `testconfig/automation/example.toml`. +For more examples see `example.toml` in product TOML configs like `testconfig/automation/example.toml`. If either ChainConfigTOMLByChainID or CommonChainConfigTOML is defined, it will override any defaults that Chainlink Node might have for the given network. Part of the configuration that defines blockchain node URLs is always dynamically generated based on the EVMNetwork configuration. +Currently, all networks are treated as EVM networks. There's no way to provide Solana, Starknet, Cosmos or Aptos configuration yet. ### Setting env vars for Chainlink Node diff --git a/integration-tests/testconfig/common/vrf/common.go b/integration-tests/testconfig/common/vrf/common.go index 120bd5c864..49cb7c4f25 100644 --- a/integration-tests/testconfig/common/vrf/common.go +++ b/integration-tests/testconfig/common/vrf/common.go @@ -71,10 +71,13 @@ func (c *PerformanceConfig) Validate() error { type ExistingEnvConfig struct { CoordinatorAddress *string `toml:"coordinator_address"` + UseExistingWrapper *bool `toml:"use_existing_wrapper"` + WrapperAddress *string `toml:"wrapper_address"` ConsumerAddress *string `toml:"consumer_address"` - LinkAddress *string `toml:"link_address"` + WrapperConsumerAddress *string `toml:"wrapper_consumer_address"` KeyHash *string `toml:"key_hash"` CreateFundSubsAndAddConsumers *bool `toml:"create_fund_subs_and_add_consumers"` + CreateFundAddWrapperConsumers *bool `toml:"create_fund_add_wrapper_consumers"` NodeSendingKeys []string `toml:"node_sending_keys"` Funding } @@ -83,23 +86,33 @@ func (c *ExistingEnvConfig) Validate() error { if c.CreateFundSubsAndAddConsumers == nil { return errors.New("create_fund_subs_and_add_consumers must be set ") } + if c.CreateFundAddWrapperConsumers == nil { + return errors.New("create_fund_add_wrapper_consumers must be set ") + } if c.CoordinatorAddress == nil { return errors.New("coordinator_address must be set when using existing environment") } if !common.IsHexAddress(*c.CoordinatorAddress) { return errors.New("coordinator_address must be a valid hex address") } + if c.UseExistingWrapper == nil { + return errors.New("use_existing_wrapper must be set ") + } + if *c.UseExistingWrapper { + if c.WrapperAddress == nil { + return errors.New("wrapper_address must be set when using `use_existing_wrapper=true`") + } + if !common.IsHexAddress(*c.WrapperAddress) { + return errors.New("wrapper_address must be a valid hex address") + } + } if c.KeyHash == nil { return errors.New("key_hash must be set when using existing environment") } if *c.KeyHash == "" { return errors.New("key_hash must be a non-empty string") } - if *c.CreateFundSubsAndAddConsumers { - if err := c.Funding.Validate(); err != nil { - return err - } - } else { + if !*c.CreateFundSubsAndAddConsumers { if c.ConsumerAddress == nil || *c.ConsumerAddress == "" { return errors.New("consumer_address must be set when using existing environment") } @@ -107,7 +120,14 @@ func (c *ExistingEnvConfig) Validate() error { return errors.New("consumer_address must be a valid hex address") } } - + if !*c.CreateFundAddWrapperConsumers { + if c.WrapperConsumerAddress == nil || *c.WrapperConsumerAddress == "" { + return errors.New("wrapper_consumer_address must be set when using existing environment") + } + if !common.IsHexAddress(*c.WrapperConsumerAddress) { + return errors.New("wrapper_consumer_address must be a valid hex address") + } + } if c.NodeSendingKeys != nil { for _, key := range c.NodeSendingKeys { if !common.IsHexAddress(key) { @@ -115,7 +135,6 @@ func (c *ExistingEnvConfig) Validate() error { } } } - return nil } @@ -127,7 +146,6 @@ func (c *Funding) Validate() error { if c.NodeSendingKeyFundingMin != nil && *c.NodeSendingKeyFundingMin <= 0 { return errors.New("when set node_sending_key_funding_min must be a positive value") } - return nil } diff --git a/integration-tests/testconfig/default.toml b/integration-tests/testconfig/default.toml index 63de9bc7fd..6bad0b7aaf 100644 --- a/integration-tests/testconfig/default.toml +++ b/integration-tests/testconfig/default.toml @@ -1,37 +1,72 @@ [Logging] +# set to true to flush logs to selected target regardless of test result; otherwise logs are only flushed if test failed test_log_collect = false [Logging.LogStream] +# supported targets: file, loki, in-memory. if empty no logs will be persisted log_targets = ["file"] +# context timeout for starting log producer and also time-frame for requesting logs log_producer_timeout = "10s" +# number of retries before log producer gives up and stops listening to logs log_producer_retry_limit = 10 [ChainlinkImage] +# postgres version to use postgres_version = "15.6" +# chainlink image to use image = "public.ecr.aws/chainlink/chainlink" +# chainlink image tag to use version = "2.12.0" [Common] -chainlink_node_funding = 4 +# chainlink node funding in native token +chainlink_node_funding = 0.5 [Network] +# slice of networks to use; at lesat one network must be selected; each selected network must either be already defined in the CTF as a known network, or be defined in +# TOML test files as a new network selected_networks = ["simulated"] [PrivateEthereumNetwork] +# ethereum version to use; eth1 or eth2 (post-merge) ethereum_version = "eth1" +# execution layer to use; geth, besu, nethermind, erigon or reth execution_layer = "geth" [PrivateEthereumNetwork.EthereumChainConfig] +# duration of single slot, lower => faster block production, must be >= 3 seconds_per_slot = 3 +# number of slots in epoch, lower => faster epoch finalisation, must be >= 2 slots_per_epoch = 2 +# extra genesis delay, no need to modify, but it should be after all validators/beacon chain starts genesis_delay = 15 +# number of validators in the network validator_count = 4 +# chain id to use chain_id = 1337 +# slice of addresses that will be funded with native token in genesis addresses_to_fund = ["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"] +# map of hard fork epochs for each network; key is fork name, value is hard fork epoch +# keep in mind that this depends on the specific version of eth2 client you are using +# this configuration is fault-tolerant and incorrect forks will be ignored [PrivateEthereumNetwork.EthereumChainConfig.HardForkEpochs] Deneb = 500 +# General config of the Chainklink node corresponding to core/services/chainlink/config.go (Config struct) that excludes +# all chain-specific configuration, which is built based on selected_networks and either Chainlink Node's defaults for +# each network, or ChainConfigTOMLByChainID (if an entry with matching chain id is defined) or CommonChainConfigTOML (if no +# entry with matching chain id is defined). +# +# Please remember that if either ChainConfigTOMLByChainID or CommonChainConfigTOML is defined, it will override any defaults +# that Chainlink Node might have for the given network. Part of the configuration that defines blockchain node URLs is always +# dynamically generated based on the EVMNetwork configuration. +# +# Last, but not least, currently all selected networks are treated as EVM networks. There's no way to provide Solana, Starknet, +# Cosmos or Aptos configuration yet. +# +# If BaseConfigTOML is empty, then default base config provided by the Chainlink Node is used. +# Also, if tracing is enabled unique id will be generated and shared between all Chainlink nodes in the same test. [NodeConfig] BaseConfigTOML = """ [Feature] @@ -78,12 +113,14 @@ DeltaDial = '500ms' DeltaReconcile = '5s' """ -# override config toml related to EVMNode configs for chainlink nodes; applicable to all EVM node configs in chainlink toml +# Overrides default config TOML related to EVMNode configs for chainlink nodes; applicable to all EVM node configs in chainlink TOML. +# Do not use it, if you want the default values to be used. Passing blockchain nodes URLs here will have no effect. CommonChainConfigTOML = """ """ [NodeConfig.ChainConfigTOMLByChainID] -# applicable for simulated chain +# Chain-specific EVMNode config TOML for chainlink nodes; applicable to all EVM node configs in chainlink TOML. It takes precedence +# over CommonChainConfigTOML and Chainlink Node's defaults. Passing blockchain nodes URLs here will have no effect. 1337 = """ AutoCreateKey = true FinalityDepth = 1 @@ -97,13 +134,13 @@ FeeCapDefault = '200 gwei' [Seth] # controls which transactions are decoded/traced. Possbile values are: none, all, reverted (default). # if transaction level doesn't match, then calling Decode() does nothing. It's advised to keep it set -# to 'reverted' to limit noise. If you combine it with 'trace_to_json' it will save all possible data -# in JSON files for reverted transactions. +# to 'reverted' to limit noise. tracing_level = "reverted" -# saves each decoding/tracing results to JSON files; what exactly is saved depends on what we +# saves each decoding/tracing results to DOT files; what exactly is saved depends on what we # were able te decode, we try to save maximum information possible. It can either be: # just tx hash, decoded transaction or call trace. Which transactions traces are saved depends # on 'tracing_level'. +trace_outputs = ["dot", "console"] # number of addresses to be generated and runtime, if set to 0, no addresses will be generated # each generated address will receive a proportion of native tokens from root private key's balance @@ -286,11 +323,11 @@ eip_1559_dynamic_fees = true # automated gas estimation for live networks # if set to true we will dynamically estimate gas for every transaction (based on suggested values, priority and congestion rate for last X blocks) -# gas_price_estimation_enabled = true + gas_price_estimation_enabled = true # number of blocks to use for congestion rate estimation (it will determine buffer added on top of suggested values) -# gas_price_estimation_blocks = 100 + gas_price_estimation_blocks = 100 # transaction priority, which determines adjustment factor multiplier applied to suggested values (fast - 1.2x, standard - 1x, slow - 0.8x) -# gas_price_estimation_tx_priority = "standard" + gas_price_estimation_tx_priority = "standard" # URLs # if set they will overwrite URLs from EVMNetwork that Seth uses, can be either WS(S) or HTTP(S) @@ -309,7 +346,7 @@ gas_price = 200_000_000_000 # EIP-1559 transactions gas_fee_cap = 200_000_000_000 -gas_tip_cap = 2_000_000_000 +gas_tip_cap = 25_000_000_000 [[Seth.networks]] name = "Polygon zkEVM Goerli" @@ -415,3 +452,271 @@ gas_price_estimation_blocks = 100 # priority of the transaction, can be "fast", "standard" or "slow" (the higher the priority, the higher adjustment factor will be used for gas estimation) [default: "standard"] gas_price_estimation_tx_priority = "standard" + + +[[Seth.networks]] +name = "Nexon Mainnet" +transaction_timeout = "3m" +eip_1559_dynamic_fees = true +transfer_gas_fee = 21_000 + +# manual settings, used when gas_price_estimation_enabled is false or when it fails +# legacy transactions +gas_price = 30_000_000_000 + +# EIP-1559 transactions +gas_fee_cap = 30_000_000_000 +gas_tip_cap = 1_800_000_000 + + +[Network.EVMNetworks.NEXON_MAINNET] +evm_name = "NEXON_MAINNET" +#evm_urls = ["rpc ws endpoint"] +#evm_http_urls = ["rpc http endpoint"] +client_implementation = "Ethereum" +#evm_keys = ["private keys you want to use"] +evm_simulated = false +evm_chainlink_transaction_limit = 5000 +evm_minimum_confirmations = 1 +evm_gas_estimation_buffer = 10000 +evm_supports_eip1559 = true +evm_default_gas_limit = 6000000 +evm_chain_id = 60118 + +[[Seth.networks]] +name = "Nexon Stage" +transaction_timeout = "3m" +eip_1559_dynamic_fees = true +transfer_gas_fee = 21_000 + +# manual settings, used when gas_price_estimation_enabled is false or when it fails +# legacy transactions +gas_price = 30_000_000_000 + +# EIP-1559 transactions +gas_fee_cap = 30_000_000_000 +gas_tip_cap = 1_800_000_000 + + +[Network.EVMNetworks.NEXON_STAGE] +evm_name = "NEXON_STAGE" +#evm_urls = ["rpc ws endpoint"] +#evm_http_urls = ["rpc http endpoint"] +client_implementation = "Ethereum" +#evm_keys = ["private keys you want to use"] +evm_simulated = false +evm_chainlink_transaction_limit = 5000 +evm_minimum_confirmations = 1 +evm_gas_estimation_buffer = 10000 +evm_supports_eip1559 = true +evm_default_gas_limit = 6000000 +evm_chain_id = 847799 + + +#### + +[[Seth.networks]] +name = "Nexon QA" +transaction_timeout = "3m" +eip_1559_dynamic_fees = true +transfer_gas_fee = 21_000 + +# manual settings, used when gas_price_estimation_enabled is false or when it fails +# legacy transactions +gas_price = 30_000_000_000 + +# EIP-1559 transactions +gas_fee_cap = 30_000_000_000 +gas_tip_cap = 1_800_000_000 + + +[Network.EVMNetworks.NEXON_QA] +evm_name = "NEXON_QA" +#evm_urls = ["rpc ws endpoint"] +#evm_http_urls = ["rpc http endpoint"] +client_implementation = "Ethereum" +#evm_keys = ["private keys you want to use"] +evm_simulated = false +evm_chainlink_transaction_limit = 5000 +evm_minimum_confirmations = 1 +evm_gas_estimation_buffer = 10000 +evm_supports_eip1559 = true +evm_default_gas_limit = 6000000 +evm_chain_id = 807424 + +##### + +[[Seth.networks]] +name = "Nexon Test" +transaction_timeout = "3m" +eip_1559_dynamic_fees = true +transfer_gas_fee = 21_000 + +# manual settings, used when gas_price_estimation_enabled is false or when it fails +# legacy transactions +gas_price = 30_000_000_000 + +# EIP-1559 transactions +gas_fee_cap = 30_000_000_000 +gas_tip_cap = 1_800_000_000 + + +[Network.EVMNetworks.NEXON_TEST] +evm_name = "NEXON_TEST" +#evm_urls = ["rpc ws endpoint"] +#evm_http_urls = ["rpc http endpoint"] +client_implementation = "Ethereum" +#evm_keys = ["private keys you want to use"] +evm_simulated = false +evm_chainlink_transaction_limit = 5000 +evm_minimum_confirmations = 1 +evm_gas_estimation_buffer = 10000 +evm_supports_eip1559 = true +evm_default_gas_limit = 6000000 +evm_chain_id = 595581 + +##### +[[Seth.networks]] +name = "Nexon Dev" +transaction_timeout = "3m" +eip_1559_dynamic_fees = true +transfer_gas_fee = 21_000 + +# manual settings, used when gas_price_estimation_enabled is false or when it fails +# legacy transactions +gas_price = 30_000_000_000 + +# EIP-1559 transactions +gas_fee_cap = 30_000_000_000 +gas_tip_cap = 1_800_000_000 + + +[Network.EVMNetworks.NEXON_DEV] +evm_name = "NEXON_DEV" +#evm_urls = ["rpc ws endpoint"] +#evm_http_urls = ["rpc http endpoint"] +client_implementation = "Ethereum" +#evm_keys = ["private keys you want to use"] +evm_simulated = false +evm_chainlink_transaction_limit = 5000 +evm_minimum_confirmations = 1 +evm_gas_estimation_buffer = 10000 +evm_supports_eip1559 = true +evm_default_gas_limit = 6000000 +evm_chain_id = 5668 + +[[Seth.networks]] +name = "LINEA_SEPOLIA" +chain_id = "59141" +transaction_timeout = "10m" +transfer_gas_fee = 21_000 +gas_price = 200_000_000_000_000 +eip_1559_dynamic_fees = false +gas_fee_cap = 109_694_825_437 +gas_tip_cap = 30_000_000_000 +gas_price_estimation_enabled = true +gas_price_estimation_blocks = 1000 +gas_price_estimation_tx_priority = "standard" + +[[Seth.networks]] +name = "LINEA_MAINNET" +chain_id = "59144" +transaction_timeout = "10m" +transfer_gas_fee = 21_000 +gas_price = 200_000_000_000_000 +eip_1559_dynamic_fees = false +gas_fee_cap = 109_694_825_437 +gas_tip_cap = 30_000_000_000 +gas_price_estimation_enabled = true +gas_price_estimation_blocks = 1000 +gas_price_estimation_tx_priority = "standard" + +[[Seth.networks]] +name = "ZKSYNC_SEPOLIA" +chain_id = "300" +transaction_timeout = "3m" +transfer_gas_fee = 21_000 +gas_price = 200_000_000_000 +eip_1559_dynamic_fees = true +gas_fee_cap = 109_694_825_437 +gas_tip_cap = 30_000_000_000 +gas_price_estimation_enabled = true +gas_price_estimation_blocks = 1000 +gas_price_estimation_tx_priority = "standard" + +[[Seth.networks]] +name = "ZKSYNC_MAINNET" +chain_id = "324" +transaction_timeout = "3m" +transfer_gas_fee = 21_000 +gas_price = 200_000_000_000 +eip_1559_dynamic_fees = true +gas_fee_cap = 109_694_825_437 +gas_tip_cap = 30_000_000_000 +gas_price_estimation_enabled = true +gas_price_estimation_blocks = 1000 +gas_price_estimation_tx_priority = "standard" + +[[Seth.networks]] +name = "POLYGON_ZKEVM_CARDONA" +transaction_timeout = "3m" +transfer_gas_fee = 21_000 +gas_price = 743_000_000 +eip_1559_dynamic_fees = false +gas_fee_cap = 1_725_800_000 +gas_tip_cap = 822_800_000 +gas_price_estimation_enabled = true +gas_price_estimation_blocks = 1000 +gas_price_estimation_tx_priority = "standard" + +[[Seth.networks]] +name = "HEDERA_TESTNET" +transaction_timeout = "3m" +transfer_gas_fee = 800_000 +gas_limit = 2_000_000 +gas_price = 2_500_000_000_000 +eip_1559_dynamic_fees = false +gas_fee_cap = 109_694_825_437 +gas_tip_cap = 30_000_000_000 +gas_price_estimation_enabled = true +gas_price_estimation_blocks = 1000 +gas_price_estimation_tx_priority = "standard" + +[[Seth.networks]] +name = "TREASURE_RUBY" +chain_id = "978657" +transaction_timeout = "10m" +transfer_gas_fee = 21_000 +gas_price = 100_000_000 +eip_1559_dynamic_fees = true +gas_fee_cap = 200_000_000 +gas_tip_cap = 100_000_000 +gas_price_estimation_enabled = true +gas_price_estimation_blocks = 1000 +gas_price_estimation_tx_priority = "standard" + +[[Seth.networks]] +name = "XLAYER_MAINNET" +chain_id = "196" +transaction_timeout = "10m" +transfer_gas_fee = 21_000 +gas_price = 13_400_000_000 +eip_1559_dynamic_fees = false +gas_fee_cap = 109_694_825_437 +gas_tip_cap = 30_000_000_000 +gas_price_estimation_enabled = true +gas_price_estimation_blocks = 500 +gas_price_estimation_tx_priority = "standard" + +[[Seth.networks]] +name = "XLAYER_SEPOLIA" +chain_id = "195" +transaction_timeout = "10m" +transfer_gas_fee = 21_000 +gas_price = 200_000_000_000 +eip_1559_dynamic_fees = false +gas_fee_cap = 109_694_825_437 +gas_tip_cap = 30_000_000_000 +gas_price_estimation_enabled = true +gas_price_estimation_blocks = 500 +gas_price_estimation_tx_priority = "standard" diff --git a/integration-tests/testconfig/keeper/keeper.toml b/integration-tests/testconfig/keeper/keeper.toml index b4a6a3b2c0..39eae1ea53 100644 --- a/integration-tests/testconfig/keeper/keeper.toml +++ b/integration-tests/testconfig/keeper/keeper.toml @@ -57,7 +57,21 @@ contract_call_interval = "4s" [Seth] # keeper benchmark running on simulated network requires 100k per node -root_key_funds_buffer = 700_000 +root_key_funds_buffer = 1_000_000 + +[Benchmark.Keeper.Common] +registry_to_test = "2_1" +number_of_registries = 1 +number_of_nodes = 6 +number_of_upkeeps = 1000 +upkeep_gas_limit = 1500000 +check_gas_to_burn = 10000 +perform_gas_to_burn = 1000 +max_perform_gas = 5000000 +block_range = 3600 +block_interval = 60 +forces_single_tx_key = false +delete_jobs_on_end = true [Benchmark.NodeConfig] BaseConfigTOML = """ @@ -95,3 +109,54 @@ HistoryDepth = 100 Mode = 'FixedPrice' LimitDefault = 5_000_000 """ + +[Soak.Keeper.Common] +registry_to_test = "2_1" +number_of_registries = 1 +number_of_nodes = 6 +number_of_upkeeps = 50 +upkeep_gas_limit = 1500000 +check_gas_to_burn = 10000 +perform_gas_to_burn = 1000 +max_perform_gas = 5000000 +block_range = 28800 +block_interval = 300 +forces_single_tx_key = false +delete_jobs_on_end = true + +[Soak.NodeConfig] +BaseConfigTOML = """ +[Feature] +LogPoller = true + +[OCR2] +Enabled = true + +[P2P] +[P2P.V2] +Enabled = true +AnnounceAddresses = ["0.0.0.0:6690"] +ListenAddresses = ["0.0.0.0:6690"] +[Keeper] +TurnLookBack = 0 +[WebServer] +HTTPWriteTimeout = '1h' +""" + +CommonChainConfigTOML = """ +""" + +[Soak.NodeConfig.ChainConfigTOMLByChainID] +# applicable for simulated chain +1337 = """ +FinalityDepth = 50 +LogPollInterval = '1s' +MinIncomingConfirmations = 1 + +[HeadTracker] +HistoryDepth = 100 + +[GasEstimator] +Mode = 'FixedPrice' +LimitDefault = 5_000_000 +""" diff --git a/integration-tests/testconfig/vrfv2/config.go b/integration-tests/testconfig/vrfv2/config.go index 76d54a45d5..5e94040396 100644 --- a/integration-tests/testconfig/vrfv2/config.go +++ b/integration-tests/testconfig/vrfv2/config.go @@ -3,8 +3,6 @@ package testconfig import ( "errors" - "github.com/ethereum/go-ethereum/common" - vrf_common_config "github.com/smartcontractkit/chainlink/integration-tests/testconfig/common/vrf" ) @@ -52,10 +50,6 @@ func (c *ExistingEnvConfig) Validate() error { if *c.SubID == 0 { return errors.New("sub_id must be positive value") } - - if c.LinkAddress != nil && !common.IsHexAddress(*c.LinkAddress) { - return errors.New("link_address must be a valid hex address") - } } return c.Funding.Validate() diff --git a/integration-tests/testconfig/vrfv2/vrfv2.toml b/integration-tests/testconfig/vrfv2/vrfv2.toml index 011e90c15f..634e3074fe 100644 --- a/integration-tests/testconfig/vrfv2/vrfv2.toml +++ b/integration-tests/testconfig/vrfv2/vrfv2.toml @@ -19,6 +19,7 @@ MaxSize = '0b' [WebServer] AllowOrigins = '*' HTTPPort = 6688 +HTTPWriteTimeout = '1m0s' SecureCookies = false [WebServer.RateLimit] @@ -113,11 +114,17 @@ bhf_job_run_timeout = "1h" [VRFv2.ExistingEnv] coordinator_address = "" -consumer_address = "" -sub_id = 1 key_hash = "" + +use_existing_wrapper = false +wrapper_address = "" create_fund_subs_and_add_consumers = true -link_address = "" +sub_id = 1 +consumer_address = "" + +create_fund_add_wrapper_consumers = true +wrapper_consumer_address = "" + node_sending_key_funding_min = 10 node_sending_keys = [ "", diff --git a/integration-tests/testconfig/vrfv2plus/config.go b/integration-tests/testconfig/vrfv2plus/config.go index 9d863afdd1..52d518e539 100644 --- a/integration-tests/testconfig/vrfv2plus/config.go +++ b/integration-tests/testconfig/vrfv2plus/config.go @@ -59,6 +59,8 @@ type General struct { //OP Stack chains settings L1FeeCalculationMode uint8 `toml:"l1_fee_calculation_mode"` L1FeeCoefficient uint8 `toml:"l1_fee_coefficient"` + + UseTestCoordinator *bool `toml:"use_test_coordinator"` } func (c *General) Validate() error { @@ -101,6 +103,9 @@ func (c *General) Validate() error { if c.CoordinatorLinkPremiumPercentage == nil { return errors.New("coordinator_link_premium_percentage must not be nil") } + if c.UseTestCoordinator == nil { + return errors.New("use_test_coordinator must not be nil") + } return nil } diff --git a/integration-tests/testconfig/vrfv2plus/vrfv2plus.toml b/integration-tests/testconfig/vrfv2plus/vrfv2plus.toml index cd089013db..1281ae7f2d 100644 --- a/integration-tests/testconfig/vrfv2plus/vrfv2plus.toml +++ b/integration-tests/testconfig/vrfv2plus/vrfv2plus.toml @@ -30,7 +30,78 @@ Unauthenticated = 100 HTTPSPort = 0 """ +# Node TOML config depending on the chain [NodeConfig.ChainConfigTOMLByChainID] +# ETHEREUM SEPOLIA +11155111 = """ +BlockBackfillDepth = 500 +MinIncomingConfirmations = 3 + +[GasEstimator] +LimitDefault = 3500000 +""" + +# BNB TESTNET +97 = """ +BlockBackfillDepth = 500 +RPCDefaultBatchSize = 25 +LogBackfillBatchSize = 1000 + +[GasEstimator] +LimitDefault = 3500000 + +[GasEstimator.BlockHistory] +BatchSize = 100 +""" + +# Polygon Amoy +80002 = """ +BlockBackfillDepth = 500 +RPCDefaultBatchSize = 25 +LogBackfillBatchSize = 1000 + +[Transactions] +MaxInFlight = 128 +MaxQueued = 0 + +[GasEstimator] +LimitDefault = 3500000 + +[GasEstimator.BlockHistory] +BatchSize = 100 +""" + +# Avalanche Fuji +43113 = """ +BlockBackfillDepth = 500 +RPCDefaultBatchSize = 25 +LogBackfillBatchSize = 1000 + +[GasEstimator] +LimitDefault = 3500000 + +[GasEstimator.BlockHistory] +BatchSize = 100 +""" + +# Arbitrum Sepolia +# NOTE: PROD env has `LimitDefault = 100_000_000`, but it is decreased in order not to over spend testnet tokens +421614 = """ +BlockBackfillDepth = 15000 +LogBackfillBatchSize = 1000 +RPCDefaultBatchSize = 25 + +[Transactions] +MaxInFlight = 128 +MaxQueued = 0 + +[GasEstimator] +LimitDefault = 3_500_000 + +[GasEstimator.BlockHistory] +BatchSize = 100 +""" + # OPTIMISM SEPOLIA 11155420 = """ BlockBackfillDepth = 500 @@ -56,12 +127,69 @@ LimitDefault = 3500000 [GasEstimator.BlockHistory] BatchSize = 100 """ +# Nexon Staging +847799 = """ +BlockBackfillDepth = 500 +RPCDefaultBatchSize = 25 +LogBackfillBatchSize = 1000 +NoNewHeadsThreshold = '0s' + +[GasEstimator] +LimitDefault = 3500000 + +[GasEstimator.BlockHistory] +BatchSize = 100 +""" + +# Nexon QA +807424 = """ +BlockBackfillDepth = 500 +RPCDefaultBatchSize = 25 +LogBackfillBatchSize = 1000 +NoNewHeadsThreshold = '0s' + +[GasEstimator] +LimitDefault = 3500000 + +[GasEstimator.BlockHistory] +BatchSize = 100 +""" + +# Nexon TEST +595581 = """ +BlockBackfillDepth = 500 +RPCDefaultBatchSize = 25 +LogBackfillBatchSize = 1000 +NoNewHeadsThreshold = '0s' + +[GasEstimator] +LimitDefault = 3500000 + +[GasEstimator.BlockHistory] +BatchSize = 100 +""" + +# Nexon DEV +5668 = """ +BlockBackfillDepth = 500 +RPCDefaultBatchSize = 25 +LogBackfillBatchSize = 1000 +NoNewHeadsThreshold = '0s' + +[GasEstimator] +LimitDefault = 3500000 + +[GasEstimator.BlockHistory] +BatchSize = 100 +""" + [Common] -chainlink_node_funding = 0.5 +chainlink_node_funding = 0.7 [VRFv2Plus] [VRFv2Plus.General] +use_test_coordinator = false cancel_subs_after_test_run = true use_existing_env = false #todo - need to have separate minimum_confirmations config for Coordinator, CL Node and Consumer request @@ -139,11 +267,17 @@ bhf_job_run_timeout = "1h" [VRFv2Plus.ExistingEnv] coordinator_address = "" -consumer_address = "" -sub_id = "" key_hash = "" + +use_existing_wrapper = false +wrapper_address = "" create_fund_subs_and_add_consumers = true -link_address = "" +sub_id = "" +consumer_address = "" + +create_fund_add_wrapper_consumers = true +wrapper_consumer_address = "" + node_sending_key_funding_min = 1 node_sending_keys = [] @@ -216,8 +350,10 @@ bhs_test_rate_limit_unit_duration = "3s" bhs_test_rps = 1 ### POLYGON AMOY Config - +[POLYGON_AMOY.Common] +chainlink_node_funding = 5 [POLYGON_AMOY.VRFv2Plus.General] +use_test_coordinator = true #todo - need to have separate minimum_confirmations config for Coordinator, CL Node and Consumer request minimum_confirmations = 3 @@ -227,7 +363,7 @@ callback_gas_limit = 1000000 # NEW ENV CONFIG # CL Node config -cl_node_max_gas_price_gwei = 200 +cl_node_max_gas_price_gwei = 500 number_of_sending_keys_to_create = 0 # Coordinator config @@ -273,7 +409,6 @@ key_hash = "0xd360445bacd26df47086ccf255c4f932d297ed8d5c7334b51eed32f61c541601" #key_hash = "0x2328cbee29e32d0b6662d6df82ff0fea7be300bd310561c92f515c9ee19464f1" #key_hash = "0x25f4e2d0509f42ec77db5380f3433a89fe623fa75f65d5b398d5f498327be4dd" create_fund_subs_and_add_consumers = true -link_address = "0x0Fd9e8d3aF1aaee056EB9e802c3A762a667b1904" node_sending_key_funding_min = 10 node_sending_keys = [ "0xD96013C241f1741C35a135321969f92Aae02A12F", @@ -298,16 +433,16 @@ node_sending_keys = [ randomness_request_count_per_request = 1 # amount of randomness requests to make per one TX request randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting number_of_sub_to_create = 1 -subscription_funding_amount_link = 5 +subscription_funding_amount_link = 3 subscription_funding_amount_native = 1 -subscription_refunding_amount_link = 5 +subscription_refunding_amount_link = 3 subscription_refunding_amount_native = 1 number_of_words = 1 random_words_fulfilled_event_timeout = "1m30s" wait_for_256_blocks_timeout = "15m" wrapper_consumer_funding_amount_native_token = 1.0 -wrapper_consumer_funding_amount_link = 5 +wrapper_consumer_funding_amount_link = 3 [POLYGON_AMOY-Smoke.VRFv2Plus.Performance] @@ -359,15 +494,20 @@ rps = 1 ### ARBITRUM SEPOLIA Config [ARBITRUM_SEPOLIA.VRFv2Plus.General] +use_test_coordinator = true #todo - need to have separate minimum_confirmations config for Coordinator, CL Node and Consumer request minimum_confirmations = 0 -subscription_billing_type = "LINK_AND_NATIVE" +## NEW ENV CONFIG +## CL Node config cl_node_max_gas_price_gwei = 50 number_of_sending_keys_to_create = 0 -# Coordinator config +# Consumer Request config +subscription_billing_type = "LINK_AND_NATIVE" callback_gas_limit = 1000000 + +# Coordinator config max_gas_limit_coordinator_config = 2500000 fallback_wei_per_unit_link = "5352799651145251" staleness_seconds = 172_800 @@ -408,7 +548,6 @@ consumer_address = "" sub_id = "" key_hash = "0xe13aa26fe94bfcd2ae055911f4d3bf1aed54ca6cf77af34e17f918802fd69ba1" create_fund_subs_and_add_consumers = true -link_address = "0xb1D4538B4571d411F07960EF2838Ce337FE1E80E" node_sending_key_funding_min = 20 node_sending_keys = [ "0xbE21ae371FcA1aC2d8A152e707D21e68d7d99252", @@ -441,8 +580,8 @@ randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be l number_of_sub_to_create = 1 subscription_funding_amount_link = 5 subscription_funding_amount_native = 1 -subscription_refunding_amount_link = 20 -subscription_refunding_amount_native = 10 +subscription_refunding_amount_link = 5 +subscription_refunding_amount_native = 1 number_of_words = 1 random_words_fulfilled_event_timeout = "1m30s" wait_for_256_blocks_timeout = "100s" @@ -498,7 +637,10 @@ rps = 1 ### AVALANCHE FUJI Config +[AVALANCHE_FUJI.Common] +chainlink_node_funding = 3 [AVALANCHE_FUJI.VRFv2Plus.General] +use_test_coordinator = true #todo - need to have separate minimum_confirmations config for Coordinator, CL Node and Consumer request minimum_confirmations = 0 @@ -552,7 +694,6 @@ consumer_address = "" sub_id = "" key_hash = "0x5b03254a80ea3eb72139ff0423cb88be42612780c3dd25f1d95a5ba7708a4be1" create_fund_subs_and_add_consumers = true -link_address = "0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846" node_sending_key_funding_min = 50 node_sending_keys = [ "0x3D7Da5D6A23CA2240CE576C8638C1798a023920a", @@ -565,10 +706,10 @@ node_sending_keys = [ randomness_request_count_per_request = 1 # amount of randomness requests to make per one TX request randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting number_of_sub_to_create = 1 -subscription_funding_amount_link = 20 -subscription_funding_amount_native = 20 -subscription_refunding_amount_link = 20 -subscription_refunding_amount_native = 20 +subscription_funding_amount_link = 3 +subscription_funding_amount_native = 1 +subscription_refunding_amount_link = 3 +subscription_refunding_amount_native = 1 number_of_words = 1 random_words_fulfilled_event_timeout = "1m30s" wait_for_256_blocks_timeout = "10m" @@ -624,6 +765,7 @@ rps = 1 ### ETH SEPOLIA Config [SEPOLIA.VRFv2Plus.General] +use_test_coordinator = true #todo - need to have separate minimum_confirmations config for Coordinator, CL Node and Consumer request minimum_confirmations = 3 @@ -677,7 +819,6 @@ consumer_address = "" sub_id = "" key_hash = "0xf5b4a359df0598eef89872ea2170f2afa844dbf74b417e6d44d4bda9420aceb2" create_fund_subs_and_add_consumers = true -link_address = "0x779877A7B0D9E8603169DdbD7836e478b4624789" node_sending_key_funding_min = 50 node_sending_keys = [ "0x0c0DC7f33A1256f0247c5ea75861d385fa5FED31", @@ -690,16 +831,16 @@ node_sending_keys = [ randomness_request_count_per_request = 1 # amount of randomness requests to make per one TX request randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting number_of_sub_to_create = 1 -subscription_funding_amount_link = 5 +subscription_funding_amount_link = 3 subscription_funding_amount_native = 1 -subscription_refunding_amount_link = 5 +subscription_refunding_amount_link = 3 subscription_refunding_amount_native = 1 number_of_words = 1 random_words_fulfilled_event_timeout = "1m30s" wait_for_256_blocks_timeout = "70m" wrapper_consumer_funding_amount_native_token = 1.0 -wrapper_consumer_funding_amount_link = 5 +wrapper_consumer_funding_amount_link = 3 [SEPOLIA-Smoke.VRFv2Plus.Performance] test_duration = "2s" @@ -745,8 +886,9 @@ test_duration = "2m" rate_limit_unit_duration = "3s" rps = 1 -### BSC SEPOLIA Config +### BSC TESTNET Config [BSC_TESTNET.VRFv2Plus.General] +use_test_coordinator = true #todo - need to have separate minimum_confirmations config for Coordinator, CL Node and Consumer request minimum_confirmations = 3 @@ -800,7 +942,6 @@ consumer_address = "" sub_id = "" key_hash = "0x4d43763d3eff849a89cf578a42787baa32132d7a80032125710e95b3972cd214" create_fund_subs_and_add_consumers = true -link_address = "0x84b9B910527Ad5C03A9Ca831909E21e236EA7b06" node_sending_key_funding_min = 150 node_sending_keys = [ "0x4EE2Cc6D50E8acb6BaEf673B03559525a6c92fB8", @@ -813,8 +954,16 @@ node_sending_keys = [ randomness_request_count_per_request = 1 # amount of randomness requests to make per one TX request randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting number_of_sub_to_create = 1 -subscription_funding_amount_link = 10 -subscription_funding_amount_native = 10 +subscription_funding_amount_link = 3 +subscription_funding_amount_native = 1 +subscription_refunding_amount_link = 3 +subscription_refunding_amount_native = 1 +number_of_words = 1 +random_words_fulfilled_event_timeout = "1m30s" +wait_for_256_blocks_timeout = "15m" + +wrapper_consumer_funding_amount_native_token = 1.0 +wrapper_consumer_funding_amount_link = 5 [BSC_TESTNET-Smoke.VRFv2Plus.Performance] test_duration = "15s" @@ -865,6 +1014,7 @@ rps = 1 ### NEXON QA Config [NEXON_QA.VRFv2Plus.General] +use_test_coordinator = true #todo - need to have separate minimum_confirmations config for Coordinator, CL Node and Consumer request minimum_confirmations = 0 generate_txs_on_chain = true @@ -879,7 +1029,6 @@ consumer_address = "" sub_id = "" key_hash = "0x7d5692e71807c4c02f5a109627a9ad2b12a361a346790a306983af9a5e3a186f" create_fund_subs_and_add_consumers = true -link_address = "0x92Bd61014c5BDc4A43BBbaAEa63d0694BE43ECDd" node_sending_key_funding_min = 30 node_sending_keys = [ "0xB97c0C52A2B957b45DA213e652c76090DDd0FEc6", @@ -952,6 +1101,7 @@ rps = 1 ### NEXON DEV Config [NEXON_DEV.VRFv2Plus.General] +use_test_coordinator = true #todo - need to have separate minimum_confirmations config for Coordinator, CL Node and Consumer request minimum_confirmations = 0 generate_txs_on_chain = true @@ -966,7 +1116,6 @@ consumer_address = "" sub_id = "" key_hash = "0xdc023892a41e5fe74ec7c4c2e8c0a808b01aea7acaf2b2ae30f4e08df877c48b" create_fund_subs_and_add_consumers = true -link_address = "0xE4DDEDb5A220eC218791dC35b1b4D737ba813EE7" node_sending_key_funding_min = 30 node_sending_keys = [ "0xF3d9879a75BBD85890056D7c6cB37C555F9b41A3", @@ -1038,6 +1187,7 @@ rps = 1 ### NEXON TEST Config [NEXON_TEST.VRFv2Plus.General] +use_test_coordinator = true #todo - need to have separate minimum_confirmations config for Coordinator, CL Node and Consumer request minimum_confirmations = 0 generate_txs_on_chain = true @@ -1052,7 +1202,6 @@ consumer_address = "" sub_id = "" key_hash = "0x0cb2a18e8b762cb4c8f7b17a6cc02ac7b9d2a3346f048cfd2f5d37677f8747d8" create_fund_subs_and_add_consumers = true -link_address = "0xD694472F1CD02E1f3fc3534386bda6802fCFe0f7" node_sending_key_funding_min = 30 node_sending_keys = [ "0xBFD780Af421e98C35918e10B9d6da7389C3e1D10", @@ -1125,6 +1274,7 @@ rps = 1 ### NEXON STAGE Config [NEXON_STAGE.VRFv2Plus.General] +use_test_coordinator = true #todo - need to have separate minimum_confirmations config for Coordinator, CL Node and Consumer request minimum_confirmations = 0 generate_txs_on_chain = true @@ -1139,7 +1289,6 @@ consumer_address = "" sub_id = "" key_hash = "0xbc9f525e3e1d9e2336f7c77d5f33f5b60aab3765944617fed7f66a6afecac616" create_fund_subs_and_add_consumers = true -link_address = "0x8E3f5E6dFeb4498437149b0d347ef51427dB1DE2" node_sending_key_funding_min = 30 node_sending_keys = [ ] @@ -1204,3 +1353,5 @@ subscription_funding_amount_native = 0.1 test_duration = "2m" rate_limit_unit_duration = "3s" rps = 1 + + diff --git a/integration-tests/testsetups/keeper_benchmark.go b/integration-tests/testsetups/keeper_benchmark.go index af1e76b1d7..ffe71893ba 100644 --- a/integration-tests/testsetups/keeper_benchmark.go +++ b/integration-tests/testsetups/keeper_benchmark.go @@ -26,12 +26,12 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/seth" - "github.com/smartcontractkit/chainlink-testing-framework/lib/blockchain" - "github.com/smartcontractkit/chainlink-testing-framework/lib/k8s/environment" - "github.com/smartcontractkit/chainlink-testing-framework/lib/logging" - reportModel "github.com/smartcontractkit/chainlink-testing-framework/lib/testreporters" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/ptr" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + reportModel "github.com/smartcontractkit/chainlink-testing-framework/testreporters" + "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" @@ -63,9 +63,11 @@ type KeeperBenchmarkTest struct { chainClient *seth.Client testConfig tt.KeeperBenchmarkTestConfig - linkToken contracts.LinkToken - ethFeed contracts.MockETHLINKFeed - gasFeed contracts.MockGasFeed + linkToken contracts.LinkToken + linkethFeed contracts.MockLINKETHFeed + gasFeed contracts.MockGasFeed + ethusdFeed contracts.MockETHUSDFeed + wrappedNative contracts.WETHToken } // UpkeepConfig dictates details of how the test's upkeep contracts should be called and configured @@ -164,10 +166,10 @@ func (k *KeeperBenchmarkTest) Setup(env *environment.Environment, config tt.Keep } if common.IsHexAddress(c.EthFeedAddress) { - _, err = contracts.LoadMockETHLINKFeed(k.chainClient, common.HexToAddress(c.EthFeedAddress)) + _, err = contracts.LoadMockLINKETHFeed(k.chainClient, common.HexToAddress(c.EthFeedAddress)) require.NoError(k.t, err, "Loading ETH-Link feed Contract shouldn't fail") } else { - k.ethFeed, err = contracts.DeployMockETHLINKFeed(k.chainClient, big.NewInt(2e18)) + k.linkethFeed, err = contracts.DeployMockLINKETHFeed(k.chainClient, big.NewInt(2e18)) require.NoError(k.t, err, "Deploying mock ETH-Link feed shouldn't fail") } @@ -179,6 +181,11 @@ func (k *KeeperBenchmarkTest) Setup(env *environment.Environment, config tt.Keep require.NoError(k.t, err, "Deploying mock gas feed shouldn't fail") } + k.ethusdFeed, err = contracts.DeployMockETHUSDFeed(k.chainClient, big.NewInt(200000000000)) + require.NoError(k.t, err, "Deploying mock ETH-USD feed shouldn't fail") + k.wrappedNative, err = contracts.DeployWETHTokenContract(k.log, k.chainClient) + require.NoError(k.t, err, "Deploying WETH Token Contract shouldn't fail") + for index := range inputs.RegistryVersions { k.log.Info().Int("Index", index).Msg("Starting Test Setup") k.DeployBenchmarkKeeperContracts(index) @@ -192,7 +199,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 || inputs.RegistryVersions[index] == ethereum.RegistryVersion_2_2 { + if inputs.RegistryVersions[index] >= ethereum.RegistryVersion_2_0 { nodesToFund = k.chainlinkNodes[1:] } err = actions.FundChainlinkNodesAtKeyIndexFromRootAddress(k.log, k.chainClient, contracts.ChainlinkK8sClientToChainlinkNodeWithKeysAndAddress(nodesToFund), k.Inputs.ChainlinkNodeFunding, index) @@ -241,15 +248,14 @@ func (k *KeeperBenchmarkTest) Run() { txKeyId = 0 } kr := k.keeperRegistries[rIndex] - // TODO: need to add the LINK, WETH and WETH/USD feed to support v23 ocrConfig, err := actions.BuildAutoOCR2ConfigVarsWithKeyIndex( - k.t, nodesWithoutBootstrap, *inputs.KeeperRegistrySettings, kr.Address(), k.Inputs.DeltaStage, txKeyId, common.Address{}, kr.ChainModuleAddress(), kr.ReorgProtectionEnabled(), nil, nil, nil, + k.t, nodesWithoutBootstrap, *inputs.KeeperRegistrySettings, kr.Address(), k.Inputs.DeltaStage, txKeyId, common.Address{}, kr.ChainModuleAddress(), kr.ReorgProtectionEnabled(), k.linkToken, k.wrappedNative, k.ethusdFeed, ) require.NoError(k.t, err, "Building OCR config shouldn't fail") rv := inputs.RegistryVersions[rIndex] // Send keeper jobs to registry and chainlink nodes - if rv == ethereum.RegistryVersion_2_0 || rv == ethereum.RegistryVersion_2_1 || rv == ethereum.RegistryVersion_2_2 { + if rv >= ethereum.RegistryVersion_2_0 { actions.CreateOCRKeeperJobs(k.t, k.chainlinkNodes, kr.Address(), k.chainClient.ChainID, txKeyId, rv) if rv == ethereum.RegistryVersion_2_0 { err = kr.SetConfig(*inputs.KeeperRegistrySettings, ocrConfig) @@ -709,7 +715,7 @@ func (k *KeeperBenchmarkTest) DeployBenchmarkKeeperContracts(index int) { registry, err = contracts.DeployKeeperRegistry(k.chainClient, &contracts.KeeperRegistryOpts{ RegistryVersion: registryVersion, LinkAddr: k.linkToken.Address(), - ETHFeedAddr: k.ethFeed.Address(), + ETHFeedAddr: k.linkethFeed.Address(), GasFeedAddr: k.gasFeed.Address(), TranscoderAddr: actions.ZeroAddress.Hex(), RegistrarAddr: actions.ZeroAddress.Hex(), @@ -732,13 +738,13 @@ func (k *KeeperBenchmarkTest) DeployBenchmarkKeeperContracts(index int) { require.NoError(k.t, err, "Funding keeper registrar contract shouldn't fail") } else { // OCR automation - v2.X registry, registrar = actions.DeployAutoOCRRegistryAndRegistrar( - k.t, k.chainClient, registryVersion, *k.Inputs.KeeperRegistrySettings, k.linkToken, nil, nil, + k.t, k.chainClient, registryVersion, *k.Inputs.KeeperRegistrySettings, k.linkToken, k.wrappedNative, k.ethusdFeed, ) // 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, registry.ChainModuleAddress(), registry.ReorgProtectionEnabled(), nil, nil, nil) + ocrConfig, err := actions.BuildAutoOCR2ConfigVars(k.t, k.chainlinkNodes[1:], *k.Inputs.KeeperRegistrySettings, registrar.Address(), k.Inputs.DeltaStage, registry.ChainModuleAddress(), registry.ReorgProtectionEnabled(), k.linkToken, k.wrappedNative, k.ethusdFeed) require.NoError(k.t, err, "Building OCR config shouldn't fail") k.log.Debug().Interface("KeeperRegistrySettings", *k.Inputs.KeeperRegistrySettings).Interface("OCRConfig", ocrConfig).Msg("Config") require.NoError(k.t, err, "Error building OCR config vars") diff --git a/integration-tests/testsetups/ocr.go b/integration-tests/testsetups/ocr.go index 34b57558cb..30eb23991a 100644 --- a/integration-tests/testsetups/ocr.go +++ b/integration-tests/testsetups/ocr.go @@ -32,19 +32,21 @@ import ( "github.com/smartcontractkit/libocr/gethwrappers/offchainaggregator" "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" - "github.com/smartcontractkit/chainlink-testing-framework/lib/blockchain" - ctf_client "github.com/smartcontractkit/chainlink-testing-framework/lib/client" - ctf_config "github.com/smartcontractkit/chainlink-testing-framework/lib/config" - "github.com/smartcontractkit/chainlink-testing-framework/lib/k8s/environment" - "github.com/smartcontractkit/chainlink-testing-framework/lib/k8s/pkg/helm/chainlink" - "github.com/smartcontractkit/chainlink-testing-framework/lib/k8s/pkg/helm/ethereum" - "github.com/smartcontractkit/chainlink-testing-framework/lib/k8s/pkg/helm/foundry" - "github.com/smartcontractkit/chainlink-testing-framework/lib/k8s/pkg/helm/mockserver" - mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/lib/k8s/pkg/helm/mockserver-cfg" - "github.com/smartcontractkit/chainlink-testing-framework/lib/logging" - "github.com/smartcontractkit/chainlink-testing-framework/lib/networks" - reportModel "github.com/smartcontractkit/chainlink-testing-framework/lib/testreporters" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" + "github.com/smartcontractkit/chainlink-testing-framework/havoc" + + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + ctf_client "github.com/smartcontractkit/chainlink-testing-framework/client" + ctf_config "github.com/smartcontractkit/chainlink-testing-framework/config" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/foundry" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver" + mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver-cfg" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/networks" + reportModel "github.com/smartcontractkit/chainlink-testing-framework/testreporters" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" @@ -279,6 +281,7 @@ func (o *OCRSoakTest) Setup(ocrTestConfig tt.OcrTestConfig) { require.NoError(o.t, err, "Connecting to chainlink nodes shouldn't fail") o.bootstrapNode, o.workerNodes = nodes[0], nodes[1:] o.mockServer = ctf_client.ConnectMockServer(o.testEnvironment) + require.NoError(o.t, err, "Creating mockserver clients shouldn't fail") linkContract, err := contracts.DeployLinkTokenContract(o.log, sethClient) require.NoError(o.t, err, "Error deploying LINK contract") @@ -547,8 +550,7 @@ func (o *OCRSoakTest) LoadState() error { } o.mockServer = ctf_client.ConnectMockServerURL(testState.MockServerURL) - - return nil + return err } func (o *OCRSoakTest) Resume() { diff --git a/integration-tests/universal/log_poller/helpers.go b/integration-tests/universal/log_poller/helpers.go index d13f40025b..4f7451d866 100644 --- a/integration-tests/universal/log_poller/helpers.go +++ b/integration-tests/universal/log_poller/helpers.go @@ -13,6 +13,8 @@ import ( "testing" "time" + "github.com/smartcontractkit/chainlink/integration-tests/utils" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink-testing-framework/wasp" @@ -27,12 +29,11 @@ import ( "github.com/scylladb/go-reflectx" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-testing-framework/lib/blockchain" - ctf_concurrency "github.com/smartcontractkit/chainlink-testing-framework/lib/concurrency" - ctf_test_env "github.com/smartcontractkit/chainlink-testing-framework/lib/docker/test_env" - "github.com/smartcontractkit/chainlink-testing-framework/lib/logging" - "github.com/smartcontractkit/chainlink-testing-framework/lib/networks" - seth_utils "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/seth" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + ctf_concurrency "github.com/smartcontractkit/chainlink-testing-framework/concurrency" + ctf_test_env "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" @@ -1061,7 +1062,7 @@ func SetupLogPollerTestDocker( evmNetwork, err := env.GetFirstEvmNetwork() require.NoError(t, err, "Error getting first evm network") - chainClient, err := seth_utils.GetChainClient(testConfig, *evmNetwork) + chainClient, err := utils.TestAwareSethClient(t, testConfig, evmNetwork) require.NoError(t, err, "Error getting seth client") err = actions.FundChainlinkNodesFromRootAddress(l, chainClient, contracts.ChainlinkClientToChainlinkNodeWithKeysAndAddress(env.ClCluster.NodeAPIs()), big.NewFloat(chainlinkNodeFunding)) diff --git a/integration-tests/utils/seth.go b/integration-tests/utils/seth.go new file mode 100644 index 0000000000..123a7f325e --- /dev/null +++ b/integration-tests/utils/seth.go @@ -0,0 +1,24 @@ +package utils + +import ( + "fmt" + "testing" + + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + ctf_config "github.com/smartcontractkit/chainlink-testing-framework/config" + pkg_seth "github.com/smartcontractkit/chainlink-testing-framework/seth" + seth_utils "github.com/smartcontractkit/chainlink-testing-framework/utils/seth" +) + +// DynamicArtifactDirConfigFn returns a function that sets Seth's artifacts directory to a unique directory for the test +func DynamicArtifactDirConfigFn(t *testing.T) func(*pkg_seth.Config) error { + return func(cfg *pkg_seth.Config) error { + cfg.ArtifactsDir = fmt.Sprintf("seth_artifacts/%s", t.Name()) + return nil + } +} + +// TestAwareSethClient returns a Seth client with the artifacts directory set to a unique directory for the test +func TestAwareSethClient(t *testing.T, sethConfig ctf_config.SethConfig, evmNetwork *blockchain.EVMNetwork) (*pkg_seth.Client, error) { + return seth_utils.GetChainClientWithConfigFunction(sethConfig, *evmNetwork, DynamicArtifactDirConfigFn(t)) +} diff --git a/integration-tests/web/sdk/client/client.go b/integration-tests/web/sdk/client/client.go index e13a02aa5a..bcfd3b1f0b 100644 --- a/integration-tests/web/sdk/client/client.go +++ b/integration-tests/web/sdk/client/client.go @@ -9,8 +9,8 @@ import ( "github.com/Khan/genqlient/graphql" - "github.com/smartcontractkit/ccip/integration-tests/web/sdk/client/internal/doer" - "github.com/smartcontractkit/ccip/integration-tests/web/sdk/internal/generated" + "github.com/smartcontractkit/chainlink/integration-tests/web/sdk/client/internal/doer" + "github.com/smartcontractkit/chainlink/integration-tests/web/sdk/internal/generated" ) type Client interface { @@ -48,12 +48,12 @@ type Credentials struct { } func New(baseURI string, creds Credentials) (Client, error) { - ep := endpoints{ + e := endpoints{ Sessions: baseURI + "/sessions", Query: baseURI + "/query", } c := &client{ - endpoints: ep, + endpoints: e, credentials: creds, } diff --git a/integration-tests/wrappers/contract_caller.go b/integration-tests/wrappers/contract_caller.go index 8669b11e49..d70be63abe 100644 --- a/integration-tests/wrappers/contract_caller.go +++ b/integration-tests/wrappers/contract_caller.go @@ -20,7 +20,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/seth" "github.com/smartcontractkit/chainlink-testing-framework/lib/blockchain" - + "github.com/smartcontractkit/chainlink-testing-framework/seth" evmClient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" ) diff --git a/operator_ui/install.go b/operator_ui/install.go index 1e09783db6..14d920ddd4 100644 --- a/operator_ui/install.go +++ b/operator_ui/install.go @@ -22,7 +22,7 @@ func main() { fullRepo = owner + "/" + repo tagPath = "operator_ui/TAG" unpackDir = "core/web/assets" - downloadTimeoutSeconds = 10 + downloadTimeoutSeconds = 30 ) // Grab first argument as root directory if len(os.Args) < 2 { diff --git a/package.json b/package.json index f32ffe9e5a..03b663ef3e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "ccip", - "version": "2.15.0-ccip1.5.0", + "name": "chainlink", + "version": "2.16.0", "description": "node of the decentralized oracle network, bridging on and off-chain computation", "main": "index.js", "scripts": { diff --git a/plugins/medianpoc/plugin.go b/plugins/medianpoc/plugin.go index 41580afe49..e6ada1bec8 100644 --- a/plugins/medianpoc/plugin.go +++ b/plugins/medianpoc/plugin.go @@ -40,7 +40,7 @@ func (e *PipelineNotFoundError) Error() string { } func (p *Plugin) NewValidationService(ctx context.Context) (core.ValidationService, error) { - s := &reportingPluginValidationService{lggr: p.Logger} + s := &reportingPluginValidationService{Service: services.Config{Name: "ValidationService"}.NewService(p.Logger)} p.SubService(s) return s, nil } @@ -81,7 +81,10 @@ func (p *Plugin) NewReportingPluginFactory( if err != nil { return nil, err } - s := &reportingPluginFactoryService{lggr: p.Logger, ReportingPluginFactory: f} + s := &reportingPluginFactoryService{ + Service: services.Config{Name: "ReportingPluginFactory"}.NewService(p.Logger), + ReportingPluginFactory: f, + } p.SubService(s) return s, nil } @@ -157,28 +160,12 @@ func (p *Plugin) newFactory(ctx context.Context, config core.ReportingPluginServ } type reportingPluginFactoryService struct { - services.StateMachine - lggr logger.Logger + services.Service ocrtypes.ReportingPluginFactory } -func (r *reportingPluginFactoryService) Name() string { return r.lggr.Name() } - -func (r *reportingPluginFactoryService) Start(ctx context.Context) error { - return r.StartOnce("ReportingPluginFactory", func() error { return nil }) -} - -func (r *reportingPluginFactoryService) Close() error { - return r.StopOnce("ReportingPluginFactory", func() error { return nil }) -} - -func (r *reportingPluginFactoryService) HealthReport() map[string]error { - return map[string]error{r.Name(): r.Healthy()} -} - type reportingPluginValidationService struct { - services.StateMachine - lggr logger.Logger + services.Service } func (r *reportingPluginValidationService) ValidateConfig(ctx context.Context, config map[string]interface{}) error { @@ -196,16 +183,3 @@ func (r *reportingPluginValidationService) ValidateConfig(ctx context.Context, c return nil } -func (r *reportingPluginValidationService) Name() string { return r.lggr.Name() } - -func (r *reportingPluginValidationService) Start(ctx context.Context) error { - return r.StartOnce("ValidationService", func() error { return nil }) -} - -func (r *reportingPluginValidationService) Close() error { - return r.StopOnce("ValidationService", func() error { return nil }) -} - -func (r *reportingPluginValidationService) HealthReport() map[string]error { - return map[string]error{r.Name(): r.Healthy()} -} diff --git a/testdata/scripts/health/default.txtar b/testdata/scripts/health/default.txtar index 1dbf6b8eb9..777d3e5e12 100644 --- a/testdata/scripts/health/default.txtar +++ b/testdata/scripts/health/default.txtar @@ -31,6 +31,7 @@ fj293fbBnlQ!f9vNs HTTPPort = $PORT -- out.txt -- +ok HeadReporter ok JobSpawner ok Mailbox.Monitor ok Mercury.WSRPCPool @@ -38,12 +39,20 @@ ok Mercury.WSRPCPool.CacheSet ok PipelineORM ok PipelineRunner ok PipelineRunner.BridgeCache -ok PromReporter ok TelemetryManager -- out.json -- { "data": [ + { + "type": "checks", + "id": "HeadReporter", + "attributes": { + "name": "HeadReporter", + "status": "passing", + "output": "" + } + }, { "type": "checks", "id": "JobSpawner", @@ -107,15 +116,6 @@ ok TelemetryManager "output": "" } }, - { - "type": "checks", - "id": "PromReporter", - "attributes": { - "name": "PromReporter", - "status": "passing", - "output": "" - } - }, { "type": "checks", "id": "TelemetryManager", diff --git a/testdata/scripts/health/help.txtar b/testdata/scripts/health/help.txtar index 07eb0509e7..68176f05a4 100644 --- a/testdata/scripts/health/help.txtar +++ b/testdata/scripts/health/help.txtar @@ -9,5 +9,6 @@ USAGE: chainlink health [command options] [arguments...] OPTIONS: - --json, -j json output + --failing, -f filter for failing services + --json, -j json output diff --git a/testdata/scripts/health/multi-chain.txtar b/testdata/scripts/health/multi-chain.txtar index 8178f8e821..bba3b3e111 100644 --- a/testdata/scripts/health/multi-chain.txtar +++ b/testdata/scripts/health/multi-chain.txtar @@ -15,6 +15,14 @@ cp stdout compact.json exec jq . compact.json cmp stdout out.json +exec chainlink --remote-node-url $NODEURL health -failing +cmp stdout out-unhealthy.txt + +exec chainlink --remote-node-url $NODEURL health -f -json +cp stdout compact.json +exec jq . compact.json +cmp stdout out-unhealthy.json + -- testdb.txt -- CL_DATABASE_URL -- testport.txt -- @@ -74,7 +82,9 @@ ok EVM.1.Txm ok EVM.1.Txm.BlockHistoryEstimator ok EVM.1.Txm.Broadcaster ok EVM.1.Txm.Confirmer +ok EVM.1.Txm.Finalizer ok EVM.1.Txm.WrappedEvmEstimator +ok HeadReporter ok JobSpawner ok Mailbox.Monitor ok Mercury.WSRPCPool @@ -82,11 +92,14 @@ ok Mercury.WSRPCPool.CacheSet ok PipelineORM ok PipelineRunner ok PipelineRunner.BridgeCache -ok PromReporter ok Solana.Bar ok StarkNet.Baz ok TelemetryManager +-- out-unhealthy.txt -- +! EVM.1.HeadTracker.HeadListener + Listener is not connected + -- out.json -- { "data": [ @@ -207,6 +220,15 @@ ok TelemetryManager "output": "" } }, + { + "type": "checks", + "id": "EVM.1.Txm.Finalizer", + "attributes": { + "name": "EVM.1.Txm.Finalizer", + "status": "passing", + "output": "" + } + }, { "type": "checks", "id": "EVM.1.Txm.WrappedEvmEstimator", @@ -216,6 +238,15 @@ ok TelemetryManager "output": "" } }, + { + "type": "checks", + "id": "HeadReporter", + "attributes": { + "name": "HeadReporter", + "status": "passing", + "output": "" + } + }, { "type": "checks", "id": "JobSpawner", @@ -279,15 +310,6 @@ ok TelemetryManager "output": "" } }, - { - "type": "checks", - "id": "PromReporter", - "attributes": { - "name": "PromReporter", - "status": "passing", - "output": "" - } - }, { "type": "checks", "id": "Solana.Bar", @@ -317,3 +339,17 @@ ok TelemetryManager } ] } +-- out-unhealthy.json -- +{ + "data": [ + { + "type": "checks", + "id": "EVM.1.HeadTracker.HeadListener", + "attributes": { + "name": "EVM.1.HeadTracker.HeadListener", + "status": "failing", + "output": "Listener is not connected" + } + } + ] +} diff --git a/testdata/scripts/node/validate/default.txtar b/testdata/scripts/node/validate/default.txtar index a2dea94e02..ff8b4889c4 100644 --- a/testdata/scripts/node/validate/default.txtar +++ b/testdata/scripts/node/validate/default.txtar @@ -18,7 +18,7 @@ ShutdownGracePeriod = '5s' FeedsManager = true LogPoller = false UICSAKeys = false -CCIP = false +CCIP = true [Database] DefaultIdleInTxSessionTimeout = '1h0m0s' diff --git a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar index a6612cc8f1..f1c1b6fcf6 100644 --- a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar @@ -62,7 +62,7 @@ ShutdownGracePeriod = '5s' FeedsManager = true LogPoller = false UICSAKeys = false -CCIP = false +CCIP = true [Database] DefaultIdleInTxSessionTimeout = '1h0m0s' @@ -319,12 +319,12 @@ AutoCreateKey = true BlockBackfillDepth = 10 BlockBackfillSkip = false FinalityDepth = 50 -FinalityTagEnabled = true +FinalityTagEnabled = false LinkContractAddress = '0x514910771AF9Ca656af840dff83E8264EcF986CA' LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 -LogPrunePageSize = 10000 +LogPrunePageSize = 0 BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' @@ -355,8 +355,8 @@ Mode = 'BlockHistory' PriceDefault = '20 gwei' PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' PriceMin = '1 gwei' -LimitDefault = 8000000 -LimitMax = 8000000 +LimitDefault = 500000 +LimitMax = 500000 LimitMultiplier = '1' LimitTransfer = 21000 EstimateLimit = false @@ -375,9 +375,6 @@ CheckInclusionBlocks = 12 CheckInclusionPercentile = 90 TransactionPercentile = 50 -[EVM.GasEstimator.FeeHistory] -CacheTimeout = '10s' - [EVM.HeadTracker] HistoryDepth = 100 MaxBufferSize = 3 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 79dcfa776d..e4fe23e768 100644 --- a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar @@ -62,7 +62,7 @@ ShutdownGracePeriod = '5s' FeedsManager = true LogPoller = false UICSAKeys = false -CCIP = false +CCIP = true [Database] DefaultIdleInTxSessionTimeout = '1h0m0s' @@ -319,12 +319,12 @@ AutoCreateKey = true BlockBackfillDepth = 10 BlockBackfillSkip = false FinalityDepth = 50 -FinalityTagEnabled = true +FinalityTagEnabled = false LinkContractAddress = '0x514910771AF9Ca656af840dff83E8264EcF986CA' LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 -LogPrunePageSize = 10000 +LogPrunePageSize = 0 BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' @@ -355,8 +355,8 @@ Mode = 'BlockHistory' PriceDefault = '20 gwei' PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' PriceMin = '1 gwei' -LimitDefault = 8000000 -LimitMax = 8000000 +LimitDefault = 500000 +LimitMax = 500000 LimitMultiplier = '1' LimitTransfer = 21000 EstimateLimit = false @@ -375,9 +375,6 @@ CheckInclusionBlocks = 12 CheckInclusionPercentile = 90 TransactionPercentile = 50 -[EVM.GasEstimator.FeeHistory] -CacheTimeout = '10s' - [EVM.HeadTracker] HistoryDepth = 100 MaxBufferSize = 3 diff --git a/testdata/scripts/node/validate/disk-based-logging.txtar b/testdata/scripts/node/validate/disk-based-logging.txtar index efa27eec11..2364fcd6fe 100644 --- a/testdata/scripts/node/validate/disk-based-logging.txtar +++ b/testdata/scripts/node/validate/disk-based-logging.txtar @@ -62,7 +62,7 @@ ShutdownGracePeriod = '5s' FeedsManager = true LogPoller = false UICSAKeys = false -CCIP = false +CCIP = true [Database] DefaultIdleInTxSessionTimeout = '1h0m0s' @@ -319,12 +319,12 @@ AutoCreateKey = true BlockBackfillDepth = 10 BlockBackfillSkip = false FinalityDepth = 50 -FinalityTagEnabled = true +FinalityTagEnabled = false LinkContractAddress = '0x514910771AF9Ca656af840dff83E8264EcF986CA' LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 -LogPrunePageSize = 10000 +LogPrunePageSize = 0 BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' @@ -355,8 +355,8 @@ Mode = 'BlockHistory' PriceDefault = '20 gwei' PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' PriceMin = '1 gwei' -LimitDefault = 8000000 -LimitMax = 8000000 +LimitDefault = 500000 +LimitMax = 500000 LimitMultiplier = '1' LimitTransfer = 21000 EstimateLimit = false @@ -375,9 +375,6 @@ CheckInclusionBlocks = 12 CheckInclusionPercentile = 90 TransactionPercentile = 50 -[EVM.GasEstimator.FeeHistory] -CacheTimeout = '10s' - [EVM.HeadTracker] HistoryDepth = 100 MaxBufferSize = 3 diff --git a/testdata/scripts/node/validate/invalid-ocr-p2p.txtar b/testdata/scripts/node/validate/invalid-ocr-p2p.txtar index 36e3324126..0cdf001ecc 100644 --- a/testdata/scripts/node/validate/invalid-ocr-p2p.txtar +++ b/testdata/scripts/node/validate/invalid-ocr-p2p.txtar @@ -47,7 +47,7 @@ ShutdownGracePeriod = '5s' FeedsManager = true LogPoller = false UICSAKeys = false -CCIP = false +CCIP = true [Database] DefaultIdleInTxSessionTimeout = '1h0m0s' diff --git a/testdata/scripts/node/validate/invalid.txtar b/testdata/scripts/node/validate/invalid.txtar index 6932eb5038..cd2834ffc2 100644 --- a/testdata/scripts/node/validate/invalid.txtar +++ b/testdata/scripts/node/validate/invalid.txtar @@ -52,7 +52,7 @@ ShutdownGracePeriod = '5s' FeedsManager = true LogPoller = false UICSAKeys = false -CCIP = false +CCIP = true [Database] DefaultIdleInTxSessionTimeout = '1h0m0s' @@ -309,12 +309,12 @@ AutoCreateKey = true BlockBackfillDepth = 10 BlockBackfillSkip = false FinalityDepth = 50 -FinalityTagEnabled = true +FinalityTagEnabled = false LinkContractAddress = '0x514910771AF9Ca656af840dff83E8264EcF986CA' LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 -LogPrunePageSize = 10000 +LogPrunePageSize = 0 BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' @@ -345,8 +345,8 @@ Mode = 'BlockHistory' PriceDefault = '20 gwei' PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' PriceMin = '1 gwei' -LimitDefault = 8000000 -LimitMax = 8000000 +LimitDefault = 500000 +LimitMax = 500000 LimitMultiplier = '1' LimitTransfer = 21000 EstimateLimit = false @@ -365,9 +365,6 @@ CheckInclusionBlocks = 12 CheckInclusionPercentile = 90 TransactionPercentile = 50 -[EVM.GasEstimator.FeeHistory] -CacheTimeout = '10s' - [EVM.HeadTracker] HistoryDepth = 100 MaxBufferSize = 3 diff --git a/testdata/scripts/node/validate/valid.txtar b/testdata/scripts/node/validate/valid.txtar index 7074c82bf5..f872cdf364 100644 --- a/testdata/scripts/node/validate/valid.txtar +++ b/testdata/scripts/node/validate/valid.txtar @@ -59,7 +59,7 @@ ShutdownGracePeriod = '5s' FeedsManager = true LogPoller = false UICSAKeys = false -CCIP = false +CCIP = true [Database] DefaultIdleInTxSessionTimeout = '1h0m0s' @@ -316,12 +316,12 @@ AutoCreateKey = true BlockBackfillDepth = 10 BlockBackfillSkip = false FinalityDepth = 50 -FinalityTagEnabled = true +FinalityTagEnabled = false LinkContractAddress = '0x514910771AF9Ca656af840dff83E8264EcF986CA' LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 -LogPrunePageSize = 10000 +LogPrunePageSize = 0 BackupLogPollerBlockDelay = 100 MinIncomingConfirmations = 3 MinContractPayment = '0.1 link' @@ -352,8 +352,8 @@ Mode = 'BlockHistory' PriceDefault = '20 gwei' PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' PriceMin = '1 gwei' -LimitDefault = 8000000 -LimitMax = 8000000 +LimitDefault = 500000 +LimitMax = 500000 LimitMultiplier = '1' LimitTransfer = 21000 EstimateLimit = false @@ -372,9 +372,6 @@ CheckInclusionBlocks = 12 CheckInclusionPercentile = 90 TransactionPercentile = 50 -[EVM.GasEstimator.FeeHistory] -CacheTimeout = '10s' - [EVM.HeadTracker] HistoryDepth = 100 MaxBufferSize = 3 diff --git a/testdata/scripts/node/validate/warnings.txtar b/testdata/scripts/node/validate/warnings.txtar index 683ef2519b..dea40ec8da 100644 --- a/testdata/scripts/node/validate/warnings.txtar +++ b/testdata/scripts/node/validate/warnings.txtar @@ -41,7 +41,7 @@ ShutdownGracePeriod = '5s' FeedsManager = true LogPoller = false UICSAKeys = false -CCIP = false +CCIP = true [Database] DefaultIdleInTxSessionTimeout = '1h0m0s' diff --git a/tools/bin/goreleaser_utils b/tools/bin/goreleaser_utils index 4e1b3ffc4d..979204d1e3 100755 --- a/tools/bin/goreleaser_utils +++ b/tools/bin/goreleaser_utils @@ -25,7 +25,7 @@ _get_arch() { _get_wasmvm_lib_path() { local -r platform="$1" local -r arch="$2" - wasmvm_dir=$(go list -json -m all | jq -r '. | select(.Path == "github.com/CosmWasm/wasmvm") | .Dir') + wasmvm_dir=$(go list -json -m github.com/CosmWasm/wasmvm | jq -r '.Dir') shared_lib_dir="$wasmvm_dir/internal/api" lib_name="libwasmvm" if [ "$platform" == "darwin" ]; then @@ -68,11 +68,13 @@ before_hook() { install_remote_plugins "linux" "amd64" "$gobin"/linux_amd64/ mkdir -p "$lib_path/linux_amd64/plugins" cp "$gobin"/linux_amd64/chainlink* "$lib_path/linux_amd64/plugins" + cp "$gobin"/chainlink* "$lib_path/linux_amd64/plugins" install_local_plugins "linux" "arm64" "$gobin"/linux_arm64/ install_remote_plugins "linux" "arm64" "$gobin"/linux_arm64/ mkdir -p "$lib_path/linux_arm64/plugins" cp "$gobin"/linux_arm64/chainlink* "$lib_path/linux_arm64/plugins" + cp "$gobin"/chainlink* "$lib_path/linux_arm64/plugins" } install_local_plugins() { @@ -94,10 +96,10 @@ get_remote_plugin_paths() { ) for plugin in "${plugins[@]}"; do - plugin_dep_name=$(echo "$plugin" | cut -d"|" -f1) - plugin_main=$(echo "$plugin" | cut -d"|" -f2) + plugin_dep_name=$(echo "$plugin" | cut -d"|" -f1) + plugin_main=$(echo "$plugin" | cut -d"|" -f2) - full_plugin_path=$(go list -m -f "{{.Dir}}" "$plugin_dep_name")"$plugin_main" + full_plugin_path=$(go list -m -f "{{.Dir}}" "$plugin_dep_name")"$plugin_main" echo "$full_plugin_path" done } diff --git a/tools/bin/modgraph b/tools/bin/modgraph index 4d8ad108c6..4080e53abe 100755 --- a/tools/bin/modgraph +++ b/tools/bin/modgraph @@ -11,11 +11,7 @@ flowchart LR chainlink-cosmos chainlink-solana chainlink-starknet/relayer - subgraph chainlink-integrations - direction LR - chainlink-integrations/evm/relayer - chainlink-integrations/common - end + chainlink-evm end subgraph products @@ -27,8 +23,13 @@ flowchart LR chainlink-vrf end + subgraph tdh2 + tdh2/go/tdh2 + tdh2/go/ocr2/decryptionplugin + end + classDef outline stroke-dasharray:6,fill:none; - class chains,products outline + class chains,products,tdh2 outline " go mod graph | \ # org only diff --git a/tools/ci/gorace_test b/tools/ci/gorace_test deleted file mode 100755 index ebf0b89281..0000000000 --- a/tools/ci/gorace_test +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash - -set -e -GORACE="halt_on_error=1" go test -v -race -parallel 2 -p 1 chainlink/core/internal chainlink/core/services diff --git a/tools/ci/init_gcloud b/tools/ci/init_gcloud deleted file mode 100755 index f1ebb12b59..0000000000 --- a/tools/ci/init_gcloud +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -if [ -z "$GCLOUD_SERVICE_KEY" ] -then - echo "Skipping gcloud initiation because no service key is set" - exit 0 -else - echo $GCLOUD_SERVICE_KEY > ${HOME}/gcloud-service-key.json - gcloud auth activate-service-account --key-file=${HOME}/gcloud-service-key.json - gcloud --quiet config set project ${GOOGLE_PROJECT_ID} - gcloud --quiet config set compute/zone ${GOOGLE_COMPUTE_ZONE} -fi diff --git a/tools/docker/README.md b/tools/docker/README.md index e0ccadd68c..527951e64c 100644 --- a/tools/docker/README.md +++ b/tools/docker/README.md @@ -23,6 +23,7 @@ cd tools/docker ### Compose script env vars The following env vars are used for the compose script : +- `WITH_OBSERVABILITY=true` to enable grafana, prometheus and alertmanager - `GETH_MODE=true` to use geth instead of parity - `CHAIN_ID=` to specify the chainID (default is 34055 for parity and 1337 for geth) - `HTTPURL=` to specify the RPC node HTTP url (default is set if you use geth or parity) @@ -35,6 +36,10 @@ for example : CHAIN_ID=11155111 WSURL=wss://eth.sepolia HTTPURL=https://eth.sepolia ./compose dev ``` +```sh +WITH_OBSERVABILITY=true ./compose up +``` + ## Dev Will run one node with a postgres database and by default a devnet RPC node that can be either geth or parity. diff --git a/tools/docker/alertmanager/alertmanager.yml b/tools/docker/alertmanager/alertmanager.yml new file mode 100644 index 0000000000..7521fd768b --- /dev/null +++ b/tools/docker/alertmanager/alertmanager.yml @@ -0,0 +1,15 @@ + +route: + receiver: 'mail' + repeat_interval: 4h + group_by: [ alertname ] + + +receivers: + - name: 'mail' + email_configs: + - smarthost: 'smtp.gmail.com:465' + auth_username: 'your_mail@gmail.com' + auth_password: "" + from: 'your_mail@gmail.com' + to: 'some_mail@gmail.com' \ No newline at end of file diff --git a/tools/docker/compose b/tools/docker/compose index 0e754a5ffc..abaf377333 100755 --- a/tools/docker/compose +++ b/tools/docker/compose @@ -16,7 +16,14 @@ if [ -z "$WSURL" ] && [ -z "$HTTPURL" ]; then fi fi -base="docker-compose $base_files" +args="node" + +if [ "$WITH_OBSERVABILITY" ]; then + base_files="$base_files -f docker-compose.observability.yaml" + args="$args grafana" +fi + +base="docker compose $base_files" dev="$base -f docker-compose.dev.yaml" configure() { @@ -41,7 +48,7 @@ configure() { clean_docker() { $base down -v --remove-orphans $dev down -v --remove-orphans - rm -f config.toml + rm -rf config.toml } usage() { @@ -71,7 +78,7 @@ logs) dev) configure $dev build - $dev up -d node + $dev up -d $args $dev watch --no-up node ;; connect) diff --git a/tools/docker/docker-compose.observability.yaml b/tools/docker/docker-compose.observability.yaml new file mode 100644 index 0000000000..c82c050d58 --- /dev/null +++ b/tools/docker/docker-compose.observability.yaml @@ -0,0 +1,44 @@ +services: + prometheus: + image: prom/prometheus:main + container_name: chainlink-prometheus + volumes: + - ./prometheus/:/etc/prometheus/ + - prometheus_data:/prometheus + command: + - '--config.file=/etc/prometheus/prometheus.yaml' + - '--storage.tsdb.path=/prometheus' + - '--web.console.libraries=/usr/share/prometheus/console_libraries' + - '--web.console.templates=/usr/share/prometheus/consoles' + restart: always + ports: + - 9090:9090 + grafana: + image: grafana/grafana:10.4.3 + user: "472" + depends_on: + - prometheus + - alertmanager + ports: + - 3000:3000 + volumes: + - grafana_data:/var/lib/grafana + - ./grafana/provisioning/:/etc/grafana/provisioning/ + env_file: + - ./grafana/config.monitoring + restart: always + alertmanager: + image: prom/alertmanager:main + container_name: chainlink-alertmanager + volumes: + - "./alertmanager:/config" + - alertmanager-data:/data + command: --config.file=/config/alertmanager.yml --log.level=debug + restart: always + ports: + - 9093:9093 + +volumes: + alertmanager-data: {} + prometheus_data: {} + grafana_data: {} diff --git a/tools/docker/grafana/config.monitoring b/tools/docker/grafana/config.monitoring new file mode 100644 index 0000000000..a2b009166b --- /dev/null +++ b/tools/docker/grafana/config.monitoring @@ -0,0 +1,2 @@ +GF_SECURITY_ADMIN_PASSWORD=foobar +GF_USERS_ALLOW_SIGN_UP=false \ No newline at end of file diff --git a/tools/docker/grafana/provisioning/datasources/datasource.yml b/tools/docker/grafana/provisioning/datasources/datasource.yml new file mode 100644 index 0000000000..f57418b29f --- /dev/null +++ b/tools/docker/grafana/provisioning/datasources/datasource.yml @@ -0,0 +1,59 @@ +# config file version +apiVersion: 1 + +# list of datasources that should be deleted from the database +deleteDatasources: + - name: Prometheus + orgId: 1 + +# list of datasources to insert/update depending +# whats available in the database +datasources: + # name of the datasource. Required + - name: Prometheus + # datasource type. Required + type: prometheus + # access mode. direct or proxy. Required + access: proxy + # org id. will default to orgId 1 if not specified + orgId: 1 + # url + url: http://prometheus:9090 + # database password, if used + password: + # database user, if used + user: + # database name, if used + database: + # enable/disable basic auth + basicAuth: false + # basic auth username, if used + basicAuthUser: + # basic auth password, if used + basicAuthPassword: + # enable/disable with credentials headers + withCredentials: + # mark as default datasource. Max one per org + isDefault: true + # fields that will be converted to json and stored in json_data + jsonData: + graphiteVersion: "1.1" + tlsAuth: false + tlsAuthWithCACert: false + # json object of data that will be encrypted. + secureJsonData: + tlsCACert: "..." + tlsClientCert: "..." + tlsClientKey: "..." + version: 1 + # allow users to edit datasources from the UI. + editable: true + - name: Alertmanager + type: alertmanager + url: http://alertmanager:9093 + access: proxy + jsonData: + # Valid options for implementation include mimir, cortex and prometheus + implementation: prometheus + # Whether or not Grafana should send alert instances to this Alertmanager + handleGrafanaManagedAlerts: false \ No newline at end of file diff --git a/tools/docker/prometheus/prometheus.yaml b/tools/docker/prometheus/prometheus.yaml new file mode 100644 index 0000000000..96ca106493 --- /dev/null +++ b/tools/docker/prometheus/prometheus.yaml @@ -0,0 +1,13 @@ +global: + scrape_interval: 5s +scrape_configs: + - job_name: 'local_scrape' + scrape_interval: 1s + static_configs: + - targets: ['chainlink-node:6688', 'chainlink-node-2:6688'] + metrics_path: '/metrics' +alerting: + alertmanagers: + - scheme: http + static_configs: + - targets: ['alertmanager:9093'] \ No newline at end of file